Skip to content

[codex] harden client id validation#25

Merged
AnkanMisra merged 2 commits intomainfrom
feature/review-fix-hardening
May 3, 2026
Merged

[codex] harden client id validation#25
AnkanMisra merged 2 commits intomainfrom
feature/review-fix-hardening

Conversation

@AnkanMisra
Copy link
Copy Markdown
Owner

@AnkanMisra AnkanMisra commented May 3, 2026

What changed

  • Hardened X-Client-Id handling so present-but-invalid headers return 400 InvalidClientId instead of silently falling back to unscoped admin/global access.
  • Kept the intentional no-header path for CLI/admin callers.
  • Moved PUT /policies/:id client-id validation outside the policy update catch so validation errors do not get misreported as 404 NotFound.
  • Added regression coverage for oversized, blank, malformed, read-path, write-path, update, evaluate, and timeline client-id cases.
  • Updated stale docs/test-count references and removed the obsolete claim that Gensyn AXL was de-scoped.

Why

The previous boundary behavior treated invalid X-Client-Id values as absent. Since absence intentionally means global/admin view, malformed browser traffic could bypass per-browser isolation semantics.

Validation

  • bun test -> 112 pass, 0 fail, 337 assertions
  • bun run test:coverage -> 112 pass, 0 fail
  • bun run typecheck -> server + Astro clean
  • bun run build -> server bundle + Astro static build clean
  • bundled runtime smoke: /health OK and malformed X-Client-Id returns 400 InvalidClientId
  • git diff --check clean
  • conflict/stale-doc scan clean
  • emoji-byte scan clean

Review note

Do not merge until explicitly approved. This PR is intended for review first.


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Summary by CodeRabbit

Release Notes

  • New Features

    • X-Client-Id header validation now enforces format requirements; invalid, blank, or oversized headers return 400 InvalidClientId errors
  • Documentation

    • Test coverage metrics updated to 112 specs across 13 files
    • Gensyn AXL integration status and documentation expanded

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 3, 2026

Warning

Rate limit exceeded

@AnkanMisra has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 20 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8076bdf8-197f-41a7-acf3-7cf62eb20fcb

📥 Commits

Reviewing files that changed from the base of the PR and between c5b2feb and b995dd1.

📒 Files selected for processing (10)
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • docs/architecture.md
  • docs/submission.md
  • src/README.md
  • src/core/policyService.ts
  • src/risk-gate/app.ts
  • tests/README.md
  • tests/api.test.ts
📝 Walkthrough

Walkthrough

This PR introduces X-Client-Id header validation in the risk-gate application and expands test coverage from 90 specs across 10 files to 112 specs across 13 files. Documentation is updated across multiple files to reflect the new test count and mark Gensyn AXL as integrated.

Changes

X-Client-Id Validation & Documentation Updates

Layer / File(s) Summary
Validation Configuration & Error Type
src/risk-gate/app.ts
Added CLIENT_ID_MAX_LENGTH (128 chars), CLIENT_ID_PATTERN validation regex, and InvalidClientIdError class for invalid header states.
Core Validation Logic
src/risk-gate/app.ts
clientIdOf() function returns undefined when header is absent; throws InvalidClientIdError for non-string, blank, oversized, or pattern-mismatched values; returns trimmed valid ID otherwise.
Error Handling
src/risk-gate/app.ts
Fastify setErrorHandler extended to catch InvalidClientIdError and respond with 400 InvalidClientId instead of generic internal error.
Request Integration
src/risk-gate/app.ts
PUT /policies/:id handler computes validated cid once and passes it to policyService.update().
Test Coverage
tests/clientIsolation.test.ts
Added putPolicy() helper and test cases for blank, oversized, and malformed x-client-id rejection across policy CRUD, evaluate, and timeline endpoints; verified admin view excludes rejected policies. Updated spec count from 109 to 112.
Documentation Metrics
AGENTS.md, CLAUDE.md, README.md, docs/architecture.md, src/README.md, tests/README.md
Updated test suite metrics from 90 specs/10 files (or 109 specs/13 files) to 112 specs/13 files throughout.
Integration & Submission Status
docs/submission.md
Marked Gensyn AXL as integrated (gossip transport via AXL_BASE_URL, non-blocking soft failures); updated de-scoped items to exclude AXL; expanded test coverage list to include axlGossip.test.ts, clientIsolation.test.ts, and cors.test.ts.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Fastify Handler
    participant clientIdOf
    participant policyService
    participant Error Handler

    Client->>Fastify Handler: PUT /policies/:id with X-Client-Id
    Fastify Handler->>clientIdOf: Validate X-Client-Id header
    
    alt Valid header present
        clientIdOf-->>Fastify Handler: Return trimmed valid ID
        Fastify Handler->>policyService: Update with validated cid
        policyService-->>Fastify Handler: Success
        Fastify Handler-->>Client: 200 OK
    else Header missing
        clientIdOf-->>Fastify Handler: Return undefined
        Fastify Handler->>policyService: Update with undefined
        policyService-->>Fastify Handler: Success
        Fastify Handler-->>Client: 200 OK
    else Invalid header (blank, oversized, malformed, etc.)
        clientIdOf->>clientIdOf: Throw InvalidClientIdError
        clientIdOf-->>Error Handler: InvalidClientIdError
        Error Handler-->>Client: 400 InvalidClientId
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through validation gates,
Where client IDs must match their fates,
One hundred twelve specs now shine so bright,
The tests all pass, the headers tight! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[codex] harden client id validation' directly and precisely summarizes the main change: hardening the X-Client-Id header validation to reject invalid values with 400 errors instead of silently falling back to unscoped access.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/review-fix-hardening

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
Review rate limit: 0/1 reviews remaining, refill in 44 minutes and 20 seconds.

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

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 3, 2026

Deploying chainshield with  Cloudflare Pages  Cloudflare Pages

Latest commit: b995dd1
Status: ✅  Deploy successful!
Preview URL: https://0c9596f8.chainshield.pages.dev
Branch Preview URL: https://feature-review-fix-hardening.chainshield.pages.dev

View logs

@AnkanMisra AnkanMisra marked this pull request as ready for review May 3, 2026 15:05
@AnkanMisra
Copy link
Copy Markdown
Owner Author

@codex please deeply review this PR for correctness, security/isolation regressions, API semantics, test coverage, docs accuracy, and merge risk; prioritize blocker/P1 findings with file/line references.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@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)
src/risk-gate/app.ts (1)

146-156: ⚡ Quick win

catch in PUT /policies/:id still swallows all non-NotFound errors as 404.

The PR intentionally moved clientIdOf outside the try so InvalidClientIdError reaches setErrorHandler. However, the catch still absorbs any other error from policyService.update (e.g. a ZeroGStore upload failure) and misreports it as 404 NotFound. Narrowing the catch to re-throw unexpected errors avoids this mask.

Proposed fix
  } catch (err) {
+   if (!(err instanceof Error && err.message.toLowerCase().includes("not found"))) {
+     throw err;
+   }
    reply.status(404);
    return { error: "NotFound", message: (err as Error).message };
  }

Alternatively, introduce a typed PolicyNotFoundError in policyService.ts and narrow on that, which avoids the string-matching fragility.

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

In `@src/risk-gate/app.ts` around lines 146 - 156, The PUT /policies/:id handler
currently catches all errors and converts them to 404; change the catch to only
handle the policy-not-found case and re-throw any other errors so they propagate
to setErrorHandler. Concretely, in the app.put handler (the function using
clientIdOf and calling policyService.update) replace the broad catch with logic
that detects the specific not-found error (preferably by checking a typed
PolicyNotFoundError from policyService.update or, if not yet available, by
matching a unique error.name or code) and return the 404 payload only for that
case; for any other err re-throw it. If needed, add a PolicyNotFoundError type
in policyService and throw it from update to make the check robust.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/risk-gate/app.ts`:
- Around line 146-156: The PUT /policies/:id handler currently catches all
errors and converts them to 404; change the catch to only handle the
policy-not-found case and re-throw any other errors so they propagate to
setErrorHandler. Concretely, in the app.put handler (the function using
clientIdOf and calling policyService.update) replace the broad catch with logic
that detects the specific not-found error (preferably by checking a typed
PolicyNotFoundError from policyService.update or, if not yet available, by
matching a unique error.name or code) and return the 404 payload only for that
case; for any other err re-throw it. If needed, add a PolicyNotFoundError type
in policyService and throw it from update to make the check robust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bbc38d58-b422-41b3-b282-17fde0db617e

📥 Commits

Reviewing files that changed from the base of the PR and between 3c3e198 and c5b2feb.

📒 Files selected for processing (9)
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • docs/architecture.md
  • docs/submission.md
  • src/README.md
  • src/risk-gate/app.ts
  • tests/README.md
  • tests/clientIsolation.test.ts

@AnkanMisra
Copy link
Copy Markdown
Owner Author

Addressed the CodeRabbit catch-path feedback: PolicyService.update now throws a typed PolicyNotFoundError, PUT /policies/:id only maps that typed error to 404, and unexpected update failures now propagate to the global error handler. Added regression coverage for both unknown-policy 404 and unexpected-update 500; verified with bun test, bun run test:coverage, bun run typecheck, bun run build, stale-count/conflict scans, and the emoji-byte scan.

@AnkanMisra AnkanMisra marked this pull request as draft May 3, 2026 15:19
@AnkanMisra
Copy link
Copy Markdown
Owner Author

@codex review the changes

@AnkanMisra AnkanMisra marked this pull request as ready for review May 3, 2026 15:20
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@AnkanMisra AnkanMisra merged commit 8d08fb2 into main May 3, 2026
3 checks passed
@AnkanMisra AnkanMisra deleted the feature/review-fix-hardening branch May 3, 2026 15:31
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