fix: add URL validation to webhook endpoints#26593
Conversation
There was a problem hiding this comment.
4 issues found across 16 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/lib/ssrfProtection.ts">
<violation number="1" location="packages/lib/ssrfProtection.ts:138">
P0: IPv6 addresses bypass private IP validation because `URL.hostname` includes brackets for IPv6 (e.g., `[::1]`), but `net.isIP('[::1]')` returns 0 due to brackets. This allows requests to private IPv6 addresses like `https://[::1]/` (localhost) to pass validation. Strip brackets before checking.</violation>
<violation number="2" location="packages/lib/ssrfProtection.ts:203">
P2: Rule violated: **Avoid Logging Sensitive Information**
URL logged may contain sensitive data (API keys, tokens, or basic auth credentials in query parameters). Consider sanitizing the URL to log only the hostname and path, or redact query parameters before logging.</violation>
</file>
<file name="apps/api/v2/src/modules/teams/teams/inputs/update-team.input.ts">
<violation number="1" location="apps/api/v2/src/modules/teams/teams/inputs/update-team.input.ts:21">
P2: Replacing `@IsUrl()` with `@IsString()` + `@Validate(SSRFSafeUrlValidator)` allows empty strings to pass validation. The `SSRFSafeUrlValidator` uses `if (!url) return true;` which treats empty string as valid (since `!''` is truthy), whereas `@IsUrl()` would reject empty strings. Consider adding `@IsNotEmpty()` decorator or fixing the validator to check `url === undefined || url === null` instead of `!url`.</violation>
</file>
<file name="apps/api/v2/src/modules/webhooks/validators/webhookUrlValidator.ts">
<violation number="1" location="apps/api/v2/src/modules/webhooks/validators/webhookUrlValidator.ts:20">
P1: Security mismatch: Code uses `allowHttp: true` but PR description specifies "HTTPS only" and testing instructions say HTTP should be rejected. If HTTP should be blocked per security requirements, change to `allowHttp: false`. If HTTP is intentionally allowed, update the PR description and testing instructions to reflect the actual behavior.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
apps/api/v2/src/modules/webhooks/validators/webhookUrlValidator.ts
Outdated
Show resolved
Hide resolved
aa55415 to
148e550
Compare
Add URL validation to webhook endpoints to prevent malicious URLs pointing to internal networks, cloud metadata endpoints, and localhost - Add URL validation schema for tRPC webhook endpoints - Add WebhookUrlValidator for API v2 NestJS endpoints - Apply validation to create, edit, and testTrigger endpoints Security: Only HTTPS allowed. Blocks private IPs (RFC1918, RFC6598), localhost, and cloud metadata endpoints (AWS, GCP, Azure)
- Fix IPv6 bypass: strip brackets before IP check (e.g., [::1] -> ::1) - Sanitize URLs in logs: remove query params that may contain secrets - Add tests for IPv6 private addresses with brackets
148e550 to
8e220c7
Compare
hariombalhara
left a comment
There was a problem hiding this comment.
Small non-blocking suggestions
- Replace manual regex patterns with ipaddr.js library for robust IP validation - Fix IPv6 bypass vulnerability where bracketed addresses ([::1]) were not blocked - Add reserved and benchmarking IP ranges to blocklist - Remove unused allowHttp option (HTTPS-only policy) - DRY refactor Zod URL schemas
0abf659
volnei
left a comment
There was a problem hiding this comment.
Small comment. Rest looks good 🚀
|
This PR has been marked as stale due to inactivity. If you're still working on it or need any help, please let us know or update the PR to keep it active. |
- tRPC: validate URL change in edit.handler.ts instead of schema - API v2: validate in services using shared utility functions - Remove unused WebhookUrlValidator class-validator decorator - Add DRY helpers: validateWebhookUrl, validateWebhookUrlIfChanged
There was a problem hiding this comment.
2 issues found across 15 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/api/v2/src/modules/webhooks/services/webhooks.service.ts">
<violation number="1" location="apps/api/v2/src/modules/webhooks/services/webhooks.service.ts:11">
P2: Fetch only `subscriberUrl` instead of loading the entire webhook row. The new call introduces a full-row Prisma read for a single field, which is against the select-only guidance and increases data exposure.</violation>
</file>
<file name="packages/lib/zod/ssrfSafeUrl.ts">
<violation number="1" location="packages/lib/zod/ssrfSafeUrl.ts:36">
P2: The new non-nullable optional schema still accepts an empty string because it reuses validateSsrfUrl. That allows clearing the URL with "" without SSRF validation, which defeats the intent of a non-nullable update field.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Devin AI is addressing Cubic AI's review feedbackA Devin session has been created to address the issues identified by Cubic AI. |
- Use select-only query for subscriberUrl comparison (reduces data exposure) - Reject empty string in non-nullable URL schema
E2E results are ready! |
Paragon: tests updated1 new test and 1 updated test generated for this PR. New Tests
Updated Tests
DetailsNew Tests
Updated Tests
|
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/lib/ssrfProtection.ts">
<violation number="1" location="packages/lib/ssrfProtection.ts:134">
P1: Self-hosted mode allows arbitrary protocols (file://, ftp://, etc.) instead of restricting to HTTP/HTTPS. According to the PR's validation rules, self-hosted should allow HTTP, but this allows all protocols. Consider adding a protocol check before returning valid.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
🧪 Tests Added by ParagonThe following test files have been added to this PR:
These tests were generated from an approved test proposal. Generated with Paragon |
Devin AI is addressing Cubic AI's review feedbackA Devin session has been created to address the issues identified by Cubic AI. |
Address Cubic AI review feedback (confidence 9/10): Self-hosted mode was allowing arbitrary protocols (file://, ftp://, etc.) instead of restricting to HTTP/HTTPS. Added protocol check before returning valid for self-hosted deployments. Co-Authored-By: unknown <>
emrysal
left a comment
There was a problem hiding this comment.
Awesome, small NIT; but non-blocking; otherwise approved.
| } | ||
| } | ||
|
|
||
| export function validateWebhookUrlIfChanged( |
There was a problem hiding this comment.
NIT: I prefer assert naming for throwing functions like this.


What does this PR do?
Adds URL validation to webhook endpoints to prevent SSRF attacks (malicious URLs pointing to internal networks, cloud
metadata endpoints, and localhost).
Existing webhooks are preserved: validation only applies when the URL is created or changed, allowing existing
HTTP webhooks to continue working.
Changes
ssrfProtection.tsedit.schema.tsedit.handler.tsvalidate-webhook-url.ts*-webhooks.service.tsValidation rules
Exceptions
Grandfathering behavior
Updates since last revision
Breaking Changes Assessment
Existing users:
New restrictions (Cal.com only):
How should this be tested?
https://example.com/hook→ ✓ workshttp://example.com/hook→ X rejectedhttps://localhost/hook→ X rejectedhttps://10.0.0.1/hook→ X rejectedactiveonly) → ✓ works (grandfathered)file://orftp://URLs rejectedHuman Review Checklist
ssrfProtection.ts:137-139correctly restricts self-hosted to HTTP/HTTPS onlyNEXT_PUBLIC_IS_E2E) cannot be exploited in productionMandatory Tasks