Conversation
…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
PR SummaryMedium Risk Overview Fixes PDF rendering regressions by stripping emoji characters during PDF text sanitization (kept in sync between 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 Written by Cursor Bugbot for commit 65ccbe9. This will update automatically on new commits. Configure here. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| 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
Show autofix suggestion
Hide autofix suggestion
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:
- Keep the centralized
validateDomainfunction as the single gatekeeper for domain inputs, and ensure that it is always called before any use ofdomainin outbound requests. - Slightly harden
validateDomainto:- Trim whitespace.
- Explicitly reject domains that are empty or excessively long.
- Retain the strict hostname regex (which already prevents
/,?,#, etc.).
- Use the trimmed/validated value consistently in
checkDnsRecords, including in the Vercel API call. This makes it clear to both humans and tools that thedomainin 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
sanitizedDomaininstead ofdomainwhen:- Computing
rootDomain. - Calling
axios.getfornetworkcalc.com. - Constructing the Vercel API path for
this.vercelApi.post.
- Computing
- Call
No new imports or external libraries are needed; we just adjust the existing methods.
| @@ -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}`, | ||
| ); | ||
| } | ||
| } |
…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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
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 |
There was a problem hiding this comment.
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)
|
🎉 This PR is included in version 3.9.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |


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.