Skip to content

[comp] Production Deploy#2330

Merged
tofikwest merged 4 commits intoreleasefrom
main
Mar 18, 2026
Merged

[comp] Production Deploy#2330
tofikwest merged 4 commits intoreleasefrom
main

Conversation

@github-actions
Copy link
Contributor

This is an automated pull request to release the candidate branch into production, which will trigger a deployment.
It was created by the [Production PR] action.

github-actions bot and others added 3 commits March 17, 2026 20:44
…PI integration

- Added logic to trigger Vercel domain verification for SSL provisioning, ensuring domains are active without manual intervention.
- Updated verification checks to account for Vercel domains, improving the accuracy of the verification process.
- Introduced error handling for Vercel API calls to log warnings without failing the domain verification flow.
[dev] [tofikwest] tofik/custom-domain-issue
@cursor
Copy link

cursor bot commented Mar 18, 2026

PR Summary

Medium Risk
Medium risk because it changes trust-portal domain verification logic and adds a new Vercel verification API call, which could affect custom-domain activation behavior. Trigger.dev version bumps may also impact task deployment/runtime behavior in prod.

Overview
Updates Trigger.dev tooling from 4.0.6 to 4.4.3 across CI deploy workflows and workspace scripts/dependencies (API + app), with corresponding lockfile churn.

Fixes PDF rendering regressions by stripping emoji characters during PDF text sanitization (kept in sync between apps/api and apps/app), and adds targeted tests to prevent garbled emoji output.

Adjusts trust portal custom-domain verification to only require the Vercel-specific TXT record when the domain is marked as a Vercel domain, and after successful verification attempts to trigger Vercel’s /verify endpoint to provision SSL/activate the domain (non-fatal on failure).

Written by Cursor Bugbot for commit 65ccbe9. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Mar 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app (staging) Ready Ready Preview, Comment Mar 18, 2026 4:04pm
comp-api-test Ready Ready Preview, Comment Mar 18, 2026 4:04pm
portal (staging) Ready Ready Preview, Comment Mar 18, 2026 4:04pm

Request Review

Comment on lines +1028 to +1032
await this.vercelApi.post(
`/v9/projects/${process.env.TRUST_PORTAL_PROJECT_ID}/domains/${domain}/verify`,
{},
{ params: { teamId: process.env.VERCEL_TEAM_ID } },
);

Check failure

Code scanning / CodeQL

Server-side request forgery Critical

The
URL
of this request depends on a
user-provided value
.

Copilot Autofix

AI 1 day ago

General approach: ensure that any user-controlled value used in constructing an HTTP request URL is strictly validated and normalized before use, especially when used in a path segment. We should reject invalid or malformed domains before reaching the vercelApi.post call, and rely only on a sanitized representation.

Best concrete fix here:

  1. Keep the centralized validateDomain function as the single gatekeeper for domain inputs, and ensure that it is always called before any use of domain in outbound requests.
  2. Slightly harden validateDomain to:
    • Trim whitespace.
    • Explicitly reject domains that are empty or excessively long.
    • Retain the strict hostname regex (which already prevents /, ?, #, etc.).
  3. Use the trimmed/validated value consistently in checkDnsRecords, including in the Vercel API call. This makes it clear to both humans and tools that the domain in the URL is sanitized.

We do not need to change functionality: the accepted domains remain conventional FQDNs; we only normalize whitespace and fail fast on obviously invalid input. The code changes are all in apps/api/src/trust-portal/trust-portal.service.ts, within the already shown snippets:

  • Update validateDomain (around lines 888–892) to normalize and more explicitly guard the input, and to return the sanitized domain so that callers can use that value.
  • Update checkDnsRecords(organizationId: string, domain: string) (line 902 ff.) to:
    • Call const sanitizedDomain = this.validateDomain(domain);
    • Use sanitizedDomain instead of domain when:
      • Computing rootDomain.
      • Calling axios.get for networkcalc.com.
      • Constructing the Vercel API path for this.vercelApi.post.

No new imports or external libraries are needed; we just adjust the existing methods.


Suggested changeset 1
apps/api/src/trust-portal/trust-portal.service.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/api/src/trust-portal/trust-portal.service.ts b/apps/api/src/trust-portal/trust-portal.service.ts
--- a/apps/api/src/trust-portal/trust-portal.service.ts
+++ b/apps/api/src/trust-portal/trust-portal.service.ts
@@ -885,10 +885,20 @@
   /** Validate domain to prevent path injection in API URLs */
   private static readonly VALID_DOMAIN_PATTERN = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
 
-  private validateDomain(domain: string): void {
-    if (!TrustPortalService.VALID_DOMAIN_PATTERN.test(domain)) {
+  /**
+   * Validates and normalizes a domain string coming from user input.
+   * Returns a trimmed, validated domain that is safe to use in URL path segments.
+   */
+  private validateDomain(domain: string): string {
+    const trimmed = domain.trim();
+    // Basic sanity checks to prevent abuse and malformed input
+    if (!trimmed || trimmed.length > 253) {
       throw new BadRequestException('Invalid domain format');
     }
+    if (!TrustPortalService.VALID_DOMAIN_PATTERN.test(trimmed)) {
+      throw new BadRequestException('Invalid domain format');
+    }
+    return trimmed;
   }
 
   /**
@@ -900,13 +908,13 @@
     /vercel-dns[^.]*\.com\.?$/i;
 
   async checkDnsRecords(organizationId: string, domain: string) {
-    this.validateDomain(domain);
+    const sanitizedDomain = this.validateDomain(domain);
 
-    const rootDomain = domain.split('.').slice(-2).join('.');
+    const rootDomain = sanitizedDomain.split('.').slice(-2).join('.');
 
     const [cnameResp, txtResp, vercelTxtResp] = await Promise.all([
       axios
-        .get(`https://networkcalc.com/api/dns/lookup/${domain}`)
+        .get(`https://networkcalc.com/api/dns/lookup/${sanitizedDomain}`)
         .catch(() => null),
       axios
         .get(
@@ -1026,14 +1030,14 @@
     if (process.env.TRUST_PORTAL_PROJECT_ID && process.env.VERCEL_TEAM_ID) {
       try {
         await this.vercelApi.post(
-          `/v9/projects/${process.env.TRUST_PORTAL_PROJECT_ID}/domains/${domain}/verify`,
+          `/v9/projects/${process.env.TRUST_PORTAL_PROJECT_ID}/domains/${sanitizedDomain}/verify`,
           {},
           { params: { teamId: process.env.VERCEL_TEAM_ID } },
         );
       } catch (error) {
         // Non-fatal — domain is verified on our side, Vercel will eventually pick it up
         this.logger.warn(
-          `Failed to trigger Vercel domain verification for ${domain}: ${error}`,
+          `Failed to trigger Vercel domain verification for ${sanitizedDomain}: ${error}`,
         );
       }
     }
EOF
@@ -885,10 +885,20 @@
/** Validate domain to prevent path injection in API URLs */
private static readonly VALID_DOMAIN_PATTERN = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;

private validateDomain(domain: string): void {
if (!TrustPortalService.VALID_DOMAIN_PATTERN.test(domain)) {
/**
* Validates and normalizes a domain string coming from user input.
* Returns a trimmed, validated domain that is safe to use in URL path segments.
*/
private validateDomain(domain: string): string {
const trimmed = domain.trim();
// Basic sanity checks to prevent abuse and malformed input
if (!trimmed || trimmed.length > 253) {
throw new BadRequestException('Invalid domain format');
}
if (!TrustPortalService.VALID_DOMAIN_PATTERN.test(trimmed)) {
throw new BadRequestException('Invalid domain format');
}
return trimmed;
}

/**
@@ -900,13 +908,13 @@
/vercel-dns[^.]*\.com\.?$/i;

async checkDnsRecords(organizationId: string, domain: string) {
this.validateDomain(domain);
const sanitizedDomain = this.validateDomain(domain);

const rootDomain = domain.split('.').slice(-2).join('.');
const rootDomain = sanitizedDomain.split('.').slice(-2).join('.');

const [cnameResp, txtResp, vercelTxtResp] = await Promise.all([
axios
.get(`https://networkcalc.com/api/dns/lookup/${domain}`)
.get(`https://networkcalc.com/api/dns/lookup/${sanitizedDomain}`)
.catch(() => null),
axios
.get(
@@ -1026,14 +1030,14 @@
if (process.env.TRUST_PORTAL_PROJECT_ID && process.env.VERCEL_TEAM_ID) {
try {
await this.vercelApi.post(
`/v9/projects/${process.env.TRUST_PORTAL_PROJECT_ID}/domains/${domain}/verify`,
`/v9/projects/${process.env.TRUST_PORTAL_PROJECT_ID}/domains/${sanitizedDomain}/verify`,
{},
{ params: { teamId: process.env.VERCEL_TEAM_ID } },
);
} catch (error) {
// Non-fatal — domain is verified on our side, Vercel will eventually pick it up
this.logger.warn(
`Failed to trigger Vercel domain verification for ${domain}: ${error}`,
`Failed to trigger Vercel domain verification for ${sanitizedDomain}: ${error}`,
);
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
…ev to 4.4.3

* fix(pdf): strip emoji characters in policy PDF export and update trigger.dev to 4.4.3

Emoji characters (flags, symbols) were rendered as garbled text in exported
PDFs because Helvetica cannot render them. Added emoji stripping regex to
cleanTextForPDF in both server-side and client-side PDF generators.

Also updates trigger.dev from 4.0.6 to 4.4.3 across all packages, deploy
scripts, and GitHub Actions workflows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add superjson as direct dependency

superjson was previously a transitive dependency via @trigger.dev/sdk 4.0.6.
After updating to 4.4.3, it's no longer pulled in transitively but is still
imported in providers.tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(pdf): simplify emoji regex and remove redundant sub-ranges

Addresses review feedback:
- Removed redundant sub-ranges (e.g. \u{2614}-\u{2615}) already covered
  by broader ranges (\u{2600}-\u{26FF})
- Added cross-reference comments between the two cleanTextForPDF copies
- Compacted replacement maps for readability

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

* Clean text for safe rendering with standard PDF fonts (Helvetica).
* Strips invisible chars, emojis, and maps typographic chars to ASCII.
*
* NOTE: Keep in sync with apps/app/src/lib/pdf-generator.ts cleanTextForPDF
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emoji fix not applied to all PDF generators

Medium Severity

The emoji stripping fix (for CS-191 garbled flag emojis) was applied to cleanTextForPDF in two files with "keep in sync" notes referencing each other. However, apps/api/src/tasks/evidence-export/evidence-pdf-generator.ts contains a third cleanTextForPDF implementation that lacks the new emoji stripping logic entirely. Evidence export PDFs will still produce garbled output when content contains emojis.

Additional Locations (1)
Fix in Cursor Fix in Web

@tofikwest tofikwest merged commit d7e34a9 into release Mar 18, 2026
13 of 14 checks passed
@claudfuen
Copy link
Contributor

🎉 This PR is included in version 3.9.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants