Skip to content

test(web): pin both edges of the SSRF guard's IPv6 bit-mask ranges - #1512

Merged
groupthinking merged 2 commits into
mainfrom
claude/clever-heisenberg-8nrfdc
Aug 13, 2026
Merged

test(web): pin both edges of the SSRF guard's IPv6 bit-mask ranges#1512
groupthinking merged 2 commits into
mainfrom
claude/clever-heisenberg-8nrfdc

Conversation

@groupthinking

@groupthinking groupthinking commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Canonical issue

Closes #1520

Rescoped mid-run, and the production change was dropped. This PR originally carried a fix for the IPv6 transition-prefix gap (NAT64 / 6to4 / IPv4-translated / site-local / discard). #1486 merged that fix first, as f876787, having absorbed the same CodeRabbit finding. Rather than argue for a duplicate, the branch was rebuilt on top of it and reduced to the one thing main still lacks. Details in "What happened to the original change" below — including the case where my version was worse.

Outcome

ipIsPrivate blocks two IPv6 ranges with bit masks rather than exact matches:

if ((h[0] & 0xffc0) === 0xfec0) return true;                                  // fec0::/10 site-local
if (h[0] === 0x0100 && h[1] === 0 && h[2] === 0 && h[3] === 0) return true;   // 100::/64 discard

ssrf-guard.test.ts covers the too-narrow direction thoroughly — every blocked range has an address squarely inside it. Nothing covers too-wide, and neither edge of either mask is pinned.

That asymmetry matters because a too-wide mask fails silently. It refuses legitimate public destinations, and the only symptom is a rejection carrying the same uniform message every other rejection uses — the indistinguishability property #1381 introduced on purpose. Nothing separates "correctly blocked" from "wrongly blocked" at the call site.

Both plausible slips are within a character of the current code: 0xffc0/0xfec00xff00/0xfe00 widens site-local to fe00::/8; dropping the h[1]h[3] conjunction widens the discard prefix from a /64 to a /16.

Scope

  • Included: apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts — new file, 10 tests. Both edges of each masked range, a just-outside neighbour of each, and the dotted-quad spelling of a NAT64 address that ssrf-guard.test.ts already covers in hex.
  • Explicitly excluded: all detection logic. main is correct as of fix(web): range-check bracketed IPv6 literals in the SSRF guard #1486. Not one line of ssrf-guard.ts changes; this PR is one new test file and nothing else.

Risk

  • Risk level: minimal — test-only, no production file touched.
  • Failure mode: none at runtime. The tests could in principle over-constrain a future deliberate widening of either range, which is the intent: such a change should have to edit a failing test that explains the boundary.
  • Rollback: delete the file.

Verification

Head 75a901d, based on main @ f876787.

  • Focused tests — 10 cases, all passing.

  • Non-vacuity shown by mutation, not by failing against main. These pass on main by construction — there is no bug here, which is exactly why the usual "revert the fix and watch it fail" check does not apply. Saying so plainly rather than letting them look like fix-pinning tests. Each mutant is caught by precisely the test that claims to cover it:

    mutation result
    (h[0] & 0xffc0) === 0xfec0(h[0] & 0xff00) === 0xfe00 1 failed | 9 passedstill allows [fe00::1], which is below the range
    h[0] === 0x0100 && h[1..3] === 0h[0] === 0x0100 1 failed | 9 passedstill allows [100:0:0:1::1] (inside 100::/16, outside the /64)
    h[0] === 0x0100 && h[1..3] === 0 → exact match on 100:: alone 1 failed | 9 passedrejects [100:0:0:0:ffff:ffff:ffff:ffff] (last address in the range)

    The third mutant is the regression CodeRabbit identified in review. It survived the first version of this file — that gap was real, and closing it is what the second commit does.

  • Both entry paths covered — URL literals and DNS answers reach ipIsPrivate through different branches of assertPublicHttpUrl and report different messages (Blocked private IP literal vs Host does not resolve to a public address), so a regression can land on one and not the other.

  • Full apps/web suite — 59 files, 355 passed, 0 failed.

  • npx tsc --noEmit — clean. npm run lint — clean.

  • Required CI green on 75a901d — 26 checks, zero failures: test-frontend, test, build, guards, lint-frontend, lint-python, validate, PR Governance, Canonical issue and evidence, both Security Scans, bandit, trivy, python-safety, npm-audit, gitleaks, dependency-review, CodeQL, Coverage, Vercel.

  • The new tests are genuinely gated, not vacuously greenci.yml's test-frontend runs cd apps/web && npx vitest run, and the job log shows ✓ src/lib/__tests__/ssrf-guard-range-boundaries.test.ts by name. A green total alone would not distinguish "ran and passed" from "never collected".

  • Review threads resolved — both CodeRabbit findings addressed in 75a901d and confirmed by CodeRabbit.

Review findings addressed

CodeRabbit requested changes on 2d65d6d; both were valid and are fixed in 75a901d.

  1. err as Error in the rejectionOf helper — an unsafe assertion on a value catch binds as unknown, which .cursorrules forbids outright. Narrowed with instanceof and rethrow, so a non-Error throw surfaces as itself rather than being mistyped and failing later on a missing .message.
  2. 100::/64 was pinned only at its first address. This contradicted the file's own premise — fec0::/10 gets both edges, and the entire argument here is that one edge does not hold a range. 100:0:0:0:ffff:ffff:ffff:ffff added, and the mutation above proves it catches the regression.

What happened to the original change

Recording this because the conclusion is that I was wrong, and that is worth stating rather than quietly force-pushing over.

This branch originally implemented the transition-prefix fix independently. #1486 landed the same fix first. On comparing them, main's implementation is a strict superset — and on one case mine was less safe:

input mine main (#1486)
64:ff9b:1::a00:1 — RFC 8215 local-use NAT64 64:ff9b:1::/48 fell through to return falseallowed matches 64:ff9b, is not the exact /96, → blocked

main blocks the whole of 64:ff9b::/32 outside the well-known /96, on the reasoning that anything else in that prefix is local-use by definition and guessing where its embedded IPv4 sits would be worse than refusing it. That is the better call. My version had a test asserting the permissive behaviour, which would have encoded the weaker posture into the suite.

So the production commits were dropped rather than rebased — merging them would have regressed main. Only unmerged work of my own was discarded; no merged history was touched. What survives is the part main genuinely lacks, rewritten against main's actual behaviour.

I also filed the finding on #1486 (comment) correcting my own earlier framing of it as "a regression in #1486" — it was reachable on main via DNS answers before #1486 existed.

Production evidence

Not applicable — test-only, no runtime behaviour changes and no apps/web UI surface. Vercel preview builds green.

Agent handoff

One outstanding item is not something I can satisfy myself: CodeRabbit's Enforce Copilot Verification pre-merge check wants an explicit GitHub Copilot approval plus the copilot-rabbit label. I requested a Copilot review rather than adding the label, since adding it myself would assert that a review happened when it has not. If the repo wants that gate satisfied, it needs the Copilot review to actually land.

Agent provenance

Produced by a scheduled, unattended PR-remediation routine running under the repo owner's account. It halts at the human gate — no auto-merge to protected main is requested or performed. The branch was force-pushed once, replacing two unmerged commits of my own with the rescoped 2d65d6d; 75a901d then applied the review fixes on top.

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, v0 Aug 7, 2026 10:04pm

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Tests
    • Added coverage for SSRF protection at IPv6 site-local and discard-range boundaries.
    • Verified that boundary addresses are blocked while nearby valid addresses remain allowed.
    • Added checks for NAT64 dotted-quad addresses and DNS-resolved IPv6 equivalents.
    • Improved test isolation by resetting DNS mocks and console output between tests.

Walkthrough

The PR adds SSRF guard tests for IPv6 site-local, discard, and NAT64 boundaries. It covers literal addresses, nearby allowed addresses, and DNS-resolved IPv6 addresses. Test setup mocks DNS resolution and restores mocks and console output after each test.

Changes

SSRF boundary coverage

Layer / File(s) Summary
IPv6 SSRF boundary regression coverage
apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
Adds shared test setup and rejection assertions. Tests site-local and discard range boundaries, nearby allowed addresses, NAT64 dotted-quad rejection, and DNS-resolved IPv6 behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • EventRelay#1520 — Adds the requested IPv6 SSRF boundary and DNS-path tests.
  • EventRelay#1495 — Adds IPv6 and DNS-resolution regression coverage, including alternate address representations.
  • EventRelay#1484 — Adds boundary tests for IPv6 literal and DNS-resolved SSRF guard behavior.

Poem

IPv6 lines are tested tight,
Boundaries checked in morning light.
DNS paths join the careful queue,
NAT64 gets its test case too.
Safe addresses pass on through.

🚥 Pre-merge checks | ✅ 5 | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Enforce Copilot Verification ❓ Inconclusive Initial assessment pending evidence of an explicit GitHub Copilot approval. Need inspect pull request review records and repository metadata for a GitHub Copilot approval.
Require Ai Unit Tests ❓ Inconclusive Assessment pending repository and pull-request metadata checks. Inspect pull request labels and changed files to verify copilot-rabbit and committed AI-generated unit tests.
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the added IPv6 SSRF guard boundary tests.
Description check ✅ Passed The description covers all required template sections with issue, scope, risk, verification, evidence, and handoff details.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/clever-heisenberg-8nrfdc
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/clever-heisenberg-8nrfdc

Warning

Review ran into problems

🔥 Problems

These MCP integrations need to be re-authenticated in the Integrations settings: Sentry


Linked repositories: Public OSS repositories can only analyze public repositories installed in this organization. No linked repositories were analyzed; skipped groupthinking/uvai-skills.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@groupthinking groupthinking added javascript Pull requests that update javascript code high-priority Urgent - blocks revenue or core functionality tests labels Aug 7, 2026 — with Claude

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Labels added manually (javascript, tests, high-priority) — the auto-review was skipped for having none, and the repo's Auto Label workflow can't supply them because it's sitting in the stalled Actions queue described in the PR body.

Worth reviewing with fresh eyes rather than treating your confirmation on #1486 as pre-approval: this is a different diff. That thread confirmed the gap; what's here is a specific implementation of the fix, including both corrections you specified (exact 64:ff9b::/96 match requiring h[2]h[5] zero, and 100::/64 blocked as a discard prefix rather than as an embedded-IPv4 bypass). The decoding arithmetic and the prefix boundaries are the parts most worth attacking.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@groupthinking I will perform an independent review of #1512. I will focus on transition-prefix matching, embedded IPv4 extraction, and boundary conditions.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Owner Author

Superseded by #1486, which merged at 21:23 — and one test here now contradicts main

#1486 merged as of 21:23:02 carrying the same detection logic: NAT64 64:ff9b::/96 (exact /96), 6to4 2002::/16, IPv4-translated ::ffff:0:0:0/96, fec0::/10, 100::/64. Your ssrf-guard.ts diff and the merged one are semantically the same change.

I ran this PR's test file, unmodified, against current main:

Test Files  1 failed (1)
     Tests  1 failed | 15 passed (16)

15 of 16 pass with no production change at all — that is the measure of how much of this PR main already has. The one failure is not a gap in main; it is a disagreement:

FAIL  does not decode a non-/96 64:ff9b embedding as if it were /96
Error: Host does not resolve to a public address
  ❯ Module.assertPublicHttpUrl src/lib/ssrf-guard.ts:200:13

stderr: [ssrf-guard] host resolved to a non-public address:
        nat64alt.example -> 64:ff9b:1::a00:1

The disagreement, and why main has it right

Both implementations require the exact /96 before decoding — that part is identical, and your reasoning for it (a /40../64 embedding puts the IPv4 at a different offset, so a loose prefix test reads the wrong bytes) is correct and is why the merged version carries the same constraint.

They differ on what happens to the rest of 64:ff9b::/32:

// main (#1486)
if (h[0] === 0x0064 && h[1] === 0xff9b) {
  if (h[2] === 0 && h[3] === 0 && h[4] === 0 && h[5] === 0) return ipIsPrivate(lowV4());
  return true;                       // block the remainder outright
}

// this PR — falls through to `return false` (allow)

Your test pins the fall-through as intended behaviour. But 64:ff9b:1::/48 is RFC 8215 local-use NAT64 space — local-use by definition, so allowing it re-opens the class of hole this PR exists to close, in the one sub-range where the address cannot be safely decoded. "Cannot determine where the embedded IPv4 sits" is a reason to refuse, not a reason to permit. main's comment says exactly that.

So this is not a merge conflict to resolve mechanically. As written, merging this PR would turn test-frontend red on main — the assertion and the merged guard cannot both hold.

What is actually left

Dropping the contradicting case, several of your tests are genuinely not on main and are better than what merged:

  • rejects the dotted-quad spelling of a NAT64 address (64:ff9b::169.254.169.254) — pins that ipv6ToHextets folds the trailing IPv4 before the prefix test. Nothing on main covers that ordering.
  • The fec0::/10 mask boundaries — feff:ffff::1 and fec0:: rejected, fe00::1 allowed. main tests one interior value and neither edge.
  • 101::1 and 100:0:0:1::1 — the 100::/64 edges, same gap.
  • still allows a genuine public IPv6 answer as a whole-file control.

That is the salvage. My suggestion is to reduce this PR to those cases against main's guard with no production diff, rather than close it outright — the boundary coverage is the part main is actually missing, and it is the part that would catch a future off-by-one in either mask.

Process note

This is the third time today two PRs have independently implemented the same ssrf-guard.ts change (#1428#1483/#1486, now #1486#1512), and the same pattern is running on logging_config.py across #1497, #1504 and #1515. Under MERGE_POLICY.md §6 these want needs-reconciliation and an owner within 72h. Flagging it here because this instance is the first where the duplicates disagree on behaviour rather than merely overlapping — which is the failure mode that turns duplicate work into a red main.


Generated by Claude Code

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 75a901d.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@groupthinking
groupthinking marked this pull request as ready for review August 7, 2026 21:45
Closes #1520

`ssrf-guard.test.ts` covers the too-narrow direction for every blocked
range — each has an address squarely inside it. Nothing covers too-wide.

`fec0::/10` and `100::/64` are the two checks written as bit masks rather
than exact matches, and a widened mask fails silently in the opposite
direction: it refuses public space, reporting the same uniform message
every other rejection uses. Neither edge of either mask was pinned, so
widening one broke no test.

Adds both edges of each range plus a just-outside neighbour, through both
paths into `ipIsPrivate` — URL literals and DNS answers enter via
different branches of `assertPublicHttpUrl` and report different messages,
so a regression can land on one and not the other.

Also covers `64:ff9b::169.254.169.254`, the dotted-quad spelling of a case
`ssrf-guard.test.ts` already has in hex. `ipv6ToHextets` folds the trailing
quad before any range check runs, so both spellings must agree.

These pass on `main` by construction — there is no bug here. Non-vacuity is
therefore shown by mutation instead, each mutant caught by exactly the test
that claims to cover it:

  (h[0] & 0xffc0) === 0xfec0  ->  (h[0] & 0xff00) === 0xfe00
    fails: still allows [fe00::1], which is below the range

  h[0] === 0x0100 && h[1..3] === 0  ->  h[0] === 0x0100
    fails: still allows [100:0:0:1::1] (inside 100::/16, outside the /64)

Tests only. Full apps/web suite 59 files / 354 passed; tsc and eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNXdtee4Wwwjew74cNZ1C5
@groupthinking
groupthinking force-pushed the claude/clever-heisenberg-8nrfdc branch from 8ac3958 to 2d65d6d Compare August 7, 2026 21:48
@groupthinking groupthinking changed the title fix(web): block IPv6 transition-prefix encodings of private IPv4 in the SSRF guard test(web): pin both edges of the SSRF guard's IPv6 bit-mask ranges Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Green on 2d65d6d, halting at the merge gate.

All required checks pass: test-frontend, test, build, guards, lint-frontend, lint-python, validate, PR Governance, Canonical issue and evidence, both Security Scans, bandit, trivy, python-safety, npm-audit, gitleaks, dependency-review, CodeQL, Coverage, Vercel. No failures.

Two notes on reading that check list, since both could mislead:

  • The Canonical issue and evidence failure at 21:48:56 is stale. It ran against the body before Closes #1520 was added; the 21:49:30 rerun passed. GitHub scores the latest run per check name, but the older red entry is still visible in the list.
  • test-frontend genuinely executed the new tests✓ src/lib/__tests__/ssrf-guard-range-boundaries.test.ts (9 tests) appears by name in the job log, 59 files / 354 tests total. Checking the green total alone would not have distinguished "ran and passed" from "never collected", which is the failure mode this repo has hit before.

Also worth flagging: pull_request_read --method get_status returns state: success for this PR off three legacy commit statuses (two Vercel, plus CodeRabbit reporting green for "Review skipped"). That rollup excludes every Actions check. It is not a merge signal.

No auto-merge performed. main is protected and this PR carries no automerge label, so the publish gate is yours. Staged command:

gh pr merge 1512 --repo groupthinking/EventRelay --squash --delete-branch

CodeRabbit has not reviewed this head — it was rate-limited through the window and its auto-review is label-gated. @coderabbitai review if you want a pass before merging.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@groupthinking I will review #1512 independently. I will verify the IPv6 range boundaries, URL and DNS entry paths, and whether the tests can catch the stated mask-widening regressions.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts`:
- Around line 68-72: Extend the SSRF boundary tests around rejectionOf to also
reject the final address in the 100::/64 range, using the literal
100:0:0:0:ffff:ffff:ffff:ffff and asserting the existing “Blocked private IP
literal” message.
- Around line 37-39: Update the catch block in the affected test helper to
narrow the unknown caught value with an instanceof Error check before returning
it; rethrow non-Error values so the Promise<Error> contract remains reliable.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e0beeef8-5b48-4968-a0a9-d5bb0226363a

📥 Commits

Reviewing files that changed from the base of the PR and between f876787 and 2d65d6d.

📒 Files selected for processing (1)
  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
📜 Review details
⚠️ CI failures not shown inline (1)

GitHub Check: PR Governance: Canonical delivery contract blocked

Conclusion: failure

View job details

exactly one closing reference is required: Closes #<issue>
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{py,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{py,js,jsx,ts,tsx}: Use Python 3.9+ and Node 18+ for development
Never hardcode API keys, database URLs, or secrets in code
Make minimal, surgical changes and avoid deleting working code unless fixing security issues

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*.{py,js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain >80% code coverage for new features

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*.{py,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{py,ts,tsx}: Keep frontend and backend data models synchronized using matching Pydantic (backend) and TypeScript (frontend) interfaces
Use type-safe interfaces for backend-frontend data exchange

**/*.{py,ts,tsx}: Production code must use real behavior only: no mock delays, fake data, or simulated responses.
Maintain strict type safety: mypy strict mode for Python and TypeScript strict mode for the frontend.
Name events using the <domain>.<entity>.<action> format.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: TypeScript runs in strict mode: do not use implicit any or unsafe assertions.
All fetch() calls must use AbortSignal.timeout().
Do not use a bare catch; errors must be logged when caught.

Use strict TypeScript mode.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*

📄 CodeRabbit inference engine (Custom checks)

**/*: Strictly verify that GitHub Copilot has explicitly reviewed and approved the pull request; human approvals alone must not satisfy this check.
Before allowing a merge, require the copilot-rabbit label and AI-generated unit tests committed alongside the code changes; fail the check if either is missing.

For Vercel-specific work, include https://vercel.com/docs/llms-full.txt in the AI assistant context set.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use strict TypeScript mode.
Use ESLint with the shared Next.js configuration.
Use Tailwind CSS for styling.
Use the @/* path alias for imports mapped to src/*.
Do not use dangerouslySetInnerHTML in React.

apps/web/**/*.{ts,tsx}: Use the Next.js ESLint rules and the shared configuration from packages/eslint-config/.
Use the @/* path alias for imports mapped to apps/web/src/*.
Never use dangerouslySetInnerHTML.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*.{py,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not commit secrets; store keys and credentials in gitignored .env files.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*.{py,pyi,ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{py,pyi,ts,tsx}: Preserve the single workflow: YouTube link → transcript → events → agents → outputs; do not introduce alternative flows or manual triggers that bypass it.
Use event names following <domain>.<entity>.<action>, such as youtube.video.captured.
Make surgical, precise changes and do not delete working code without justification.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
**/*.ts

⚙️ CodeRabbit configuration file

This is a TypeScript/Next.js project. Focus on type safety, null checks, async/await error handling, and SSE stream lifecycle management. Flag any fetch() calls without AbortSignal.timeout. Check for proper error boundaries. Flag any TODO, placeholder, or stub implementations that are not production-ready. Enforce TypeScript strict mode compliance — flag implicit any, missing return types, and unsafe type assertions.

Files:

  • apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
🔍 Remote MCP GitHub Copilot

Additional review context

  • PR #1512 adds only ssrf-guard-range-boundaries.test.ts (+124 lines); production SSRF logic is unchanged. It covers 9 cases across URL literals, DNS answers, IPv6 boundaries, and dotted-quad NAT64 notation.
  • main explicitly checks fec0::/10 with (h[0] & 0xffc0) === 0xfec0 and 100::/64 by requiring h[0] === 0x0100 plus h[1]–h[3] === 0; it also folds embedded IPv4 text before transition-prefix checks. The new assertions match these implementations.
  • Existing tests already cover interior blocked ranges, public IPv6 controls, DNS resolution, and NAT64 in hexadecimal form. The new file adds the missing mask edges/outside neighbors and dotted-quad NAT64 coverage.
  • The current test-frontend, build, lint, security, CodeQL, coverage, and validation checks are successful. An older “Canonical issue and evidence” failure remains visible, while the later run passed; the overall commit status is still pending because CodeRabbit’s review status is pending.
  • There are no review threads or substantive completed reviews on the current head.
🔇 Additional comments (1)
apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts (1)

1-1: 📐 Maintainability & Code Quality

Block merge until required Copilot evidence is present.

PR #1512 still lacks the copilot-rabbit label and an explicit GitHub Copilot approval on the current head. Add both before merge; the AI-attributed test is present, so only the Copilot review evidence remains missing.

Comment thread apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts
Comment thread apps/web/src/lib/__tests__/ssrf-guard-range-boundaries.test.ts Outdated
Both findings were valid.

1. `err as Error` in the `rejectionOf` helper was an unsafe assertion on a
   value `catch` binds as `unknown`, which `.cursorrules` forbids outright.
   Narrow with `instanceof` and rethrow anything else, so a non-Error throw
   surfaces as itself rather than being mistyped and failing later on a
   missing `.message`.

2. The `100::/64` range was pinned only at its first address, `100::`. That
   is an inconsistency in this file's own premise: `fec0::/10` gets both
   edges, and the whole point of the file is that one edge does not hold a
   range. A regression blocking only the first address would have passed.
   Add `100:0:0:0:ffff:ffff:ffff:ffff`.

The second finding is confirmed by mutation rather than taken on faith —
replacing the range check with an exact match on `100::` alone:

  h[0] === 0x0100 && h[1..3] === 0  ->  h.join(':') === '256:0:0:0:0:0:0:0'
    fails: rejects [100:0:0:0:ffff:ffff:ffff:ffff] (last address in the range)

That mutant survived the previous version of this file, so the gap was real.
The two mutants already covered still fail exactly one test each.

Full apps/web suite 59 files / 355 passed; tsc and eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNXdtee4Wwwjew74cNZ1C5
@groupthinking groupthinking self-assigned this Aug 13, 2026
@groupthinking
groupthinking merged commit 4050fa3 into main Aug 13, 2026
31 checks passed
@groupthinking
groupthinking deleted the claude/clever-heisenberg-8nrfdc branch August 13, 2026 07:47
@linear-code

linear-code Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

GRV-410

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

Labels

high-priority Urgent - blocks revenue or core functionality javascript Pull requests that update javascript code tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSRF guard: neither IPv6 bit-mask range has its boundaries pinned

2 participants