Skip to content

fix(webhooks): stop following delivery redirects#1813

Merged
riderx merged 3 commits into
mainfrom
fix/webhook-redirect-ssrf
Mar 18, 2026
Merged

fix(webhooks): stop following delivery redirects#1813
riderx merged 3 commits into
mainfrom
fix/webhook-redirect-ssrf

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Mar 17, 2026

Summary (AI generated)

  • stop automatic redirect following during webhook delivery
  • add a unit regression test that asserts deliverWebhook() uses manual redirect handling
  • keep redirect responses as failed deliveries instead of following unvalidated targets

Motivation (AI generated)

The current webhook delivery path validates only the initial URL, then lets fetch() follow redirects automatically. That leaves a redirect-based SSRF path open even when the original URL passes validation.

Business Impact (AI generated)

This closes a high-severity SSRF vector in webhook delivery, reducing risk to internal services and metadata endpoints without changing the public webhook configuration flow for customers.

Test Plan (AI generated)

  • bun run supabase:with-env -- bunx vitest run tests/webhook-delivery-security.unit.test.ts
  • bun run supabase:with-env -- bunx vitest run tests/webhooks.test.ts tests/webhook-signature.test.ts
  • bunx eslint supabase/functions/_backend/utils/webhook.ts tests/webhook-delivery-security.unit.test.ts

Generated with AI

Summary by CodeRabbit

  • Bug Fixes

    • Webhook delivery now treats HTTP redirects as non-followed, preventing automatic redirect following during delivery attempts.
  • Tests

    • Added unit tests verifying webhook delivery for successful responses and for redirect responses, ensuring redirect handling and result reporting are correct.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

The fetch used by deliverWebhook now sets redirect: 'manual', preventing automatic HTTP redirect following. A new unit test file verifies that 200 responses are treated as successful and 302 redirect responses are reported as failed deliveries (success: false, status: 302).

Changes

Cohort / File(s) Summary
Webhook Delivery Configuration
supabase/functions/_backend/utils/webhook.ts
Added redirect: 'manual' to the fetch call in deliverWebhook to disable automatic redirect following.
Redirect Handling Tests
tests/webhook-delivery-security.unit.test.ts
New unit tests validating delivery behavior for a 200 OK (success) and a 302 Redirect (treated as failure with status: 302). Mocks global fetch, env utils, and logging.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐇 I hop where webhooks go and stay,
No secret jumps to lead astray,
Redirects now sit in plain display,
I sniff the codes and mark the way,
Hooray for safe delivery today! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly and concisely describes the primary security fix: disabling automatic redirect following in webhook delivery.
Description check ✅ Passed The PR description covers the summary, motivation, business impact, and test plan, aligning well with the template's key sections.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/webhook-redirect-ssrf
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/webhook-delivery-security.unit.test.ts`:
- Line 35: Replace the non-concurrent test declarations with concurrent ones:
change the two occurrences of it('sends webhook requests with manual redirect
handling', ...) and the other it(...) at the second location to
it.concurrent(...) in tests/webhook-delivery-security.unit.test.ts so both tests
run in parallel per the repo policy; locate the test blocks by their test
descriptions and update the it() calls to it.concurrent().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07ac9fb3-383f-4705-a7c2-71d5add5a69f

📥 Commits

Reviewing files that changed from the base of the PR and between 877c35b and ffc2533.

📒 Files selected for processing (2)
  • supabase/functions/_backend/utils/webhook.ts
  • tests/webhook-delivery-security.unit.test.ts

Comment thread tests/webhook-delivery-security.unit.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/webhook-delivery-security.unit.test.ts (1)

37-40: Potential test flakiness with concurrent global stubbing.

Both it.concurrent() tests call vi.stubGlobal('fetch', ...) which modifies a shared global. Since concurrent tests in the same file share execution context, one test's stub could overwrite the other's mid-execution. Similarly, afterEach running vi.unstubAllGlobals() for a completed test could affect a concurrent test still in progress.

In practice, these tests are fast enough that races are unlikely, but for robustness you could consider either:

  1. Using a per-test module isolation with vi.resetModules() before each dynamic import
  2. Or wrapping these specific tests in a nested describe without concurrent execution

Also applies to: 71-80

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/webhook-delivery-security.unit.test.ts` around lines 37 - 40, The tests
use vi.stubGlobal('fetch') inside it.concurrent blocks which can race across
concurrent tests and vi.unstubAllGlobals() in afterEach; to fix, avoid shared
global stubbing by calling vi.resetModules() before each dynamic import and then
stubbing fetch within each test (ensure the dynamic import of
../supabase/functions/_backend/utils/webhook.ts happens after resetModules so
deliverWebhook binds to the per-test stub), or alternatively remove
it.concurrent and wrap the tests in a non-concurrent describe so
vi.stubGlobal/vi.unstubAllGlobals around each test won't interfere with others;
reference the existing it.concurrent usage, vi.stubGlobal/vi.unstubAllGlobals,
vi.resetModules(), and the dynamic import of deliverWebhook.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/webhook-delivery-security.unit.test.ts`:
- Around line 37-40: The tests use vi.stubGlobal('fetch') inside it.concurrent
blocks which can race across concurrent tests and vi.unstubAllGlobals() in
afterEach; to fix, avoid shared global stubbing by calling vi.resetModules()
before each dynamic import and then stubbing fetch within each test (ensure the
dynamic import of ../supabase/functions/_backend/utils/webhook.ts happens after
resetModules so deliverWebhook binds to the per-test stub), or alternatively
remove it.concurrent and wrap the tests in a non-concurrent describe so
vi.stubGlobal/vi.unstubAllGlobals around each test won't interfere with others;
reference the existing it.concurrent usage, vi.stubGlobal/vi.unstubAllGlobals,
vi.resetModules(), and the dynamic import of deliverWebhook.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff5f66fa-b845-4252-ad3d-c7caa8c5a2cc

📥 Commits

Reviewing files that changed from the base of the PR and between ffc2533 and 9ec7b2b.

📒 Files selected for processing (1)
  • tests/webhook-delivery-security.unit.test.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
tests/webhook-delivery-security.unit.test.ts (1)

35-35: ⚠️ Potential issue | 🟡 Minor

Use concurrent test declarations in this .test.ts file

Line 35 and Line 70 still use it(...); this file should use it.concurrent(...) per repo test policy.

Suggested patch
-  it('sends webhook requests with manual redirect handling', async () => {
+  it.concurrent('sends webhook requests with manual redirect handling', async () => {
@@
-  it('does not treat redirect responses as successful deliveries', async () => {
+  it.concurrent('does not treat redirect responses as successful deliveries', async () => {

As per coding guidelines, tests/**/*.test.{ts,tsx}: ALL TEST FILES RUN IN PARALLEL; use it.concurrent() instead of it() to maximize parallelism; create dedicated seed data when tests modify shared resources.

Also applies to: 70-70

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/webhook-delivery-security.unit.test.ts` at line 35, Replace
non-concurrent test declarations in this test file by changing plain it(...)
calls to it.concurrent(...) for all tests (including the one titled "sends
webhook requests with manual redirect handling" and the other test flagged
around line 70) so the file follows the repo policy of running test files in
parallel; update any imports or helpers if needed to preserve behavior but do
not change test logic or titles—only replace the it function calls with
it.concurrent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/webhook-delivery-security.unit.test.ts`:
- Line 35: Replace non-concurrent test declarations in this test file by
changing plain it(...) calls to it.concurrent(...) for all tests (including the
one titled "sends webhook requests with manual redirect handling" and the other
test flagged around line 70) so the file follows the repo policy of running test
files in parallel; update any imports or helpers if needed to preserve behavior
but do not change test logic or titles—only replace the it function calls with
it.concurrent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8679afa9-3aa1-417e-8a19-dd24ce7e80c3

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec7b2b and 896518d.

📒 Files selected for processing (1)
  • tests/webhook-delivery-security.unit.test.ts

@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit 14802c6 into main Mar 18, 2026
15 checks passed
@riderx riderx deleted the fix/webhook-redirect-ssrf branch March 18, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant