Skip to content

fix(issuer): validate the credential id before it reaches the query - #153

Open
crazywriter1 wants to merge 1 commit into
agentcommercekit:mainfrom
crazywriter1:fix/issuer-id-param-validation
Open

fix(issuer): validate the credential id before it reaches the query#153
crazywriter1 wants to merge 1 commit into
agentcommercekit:mainfrom
crazywriter1:fix/issuer-id-param-validation

Conversation

@crazywriter1

@crazywriter1 crazywriter1 commented Aug 13, 2026

Copy link
Copy Markdown

What's wrong

GET /credentials/controller/:id and GET /credentials/receipts/:id read the route parameter with parseInt, and that goes wrong in two directions.

An id with no leading digits becomes NaN and reaches the query, which fails on it. The shared error handler has no branch for a driver error, so it falls through to returning the message verbatim, and GET /credentials/controller/abc answers 500 with the statement in the body:

Failed query: select "id", "credential_type", "base_credential", "issued_at", "revoked_at" from "credentials" where "credentials"."id" = ?
params: NaN

An id with trailing characters keeps its leading digits, so /1abc, /0x1, /1e3 and /1.9 all resolve to credential 1. parseInt reads 0x1 as hexadecimal and stops at the e in 1e3, so neither is the number the URL appears to name.

Nothing caller-supplied reaches the signed credential — buildSignedCredential builds the id from credential.id — so this is a wrong status code and a wrong row, not a forgery.

What changed

status.ts already parses its route parameter for exactly this reason and says so in a comment. Rather than copy that pipe into two more files, I moved it into parseIdParam and used it in all three routes. Three copies of a parse that guards a query is how these two fell out of step to begin with.

The status route keeps its behaviour: same schema, same notFound on a bad parameter, and status.test.ts passes untouched.

A leading zero is still accepted and normalised, so /01 returns credential 1. That is deliberate: the parsed number is what the handler goes on to use, so the id it builds is canonical either way.

Tests

parse-id-param.test.ts covers what the parse accepts and what it rejects, and both route test files gained a GET /:id block. Reverting only the route change makes six of them fail — abc with 500 instead of 404, the rest with 200 and the wrong credential.

No changeset: .changeset/config.json ignores @examples/*.

AI usage

Per AI_POLICY.md, Cursor (Claude Opus 5) did the work: it measured the NaN failure against a real libsql database, then wrote the fix and the tests. I read the diff before opening this.

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation of credential, receipt, and status route identifiers.
    • Invalid, malformed, fractional, signed, oversized, or unsafe IDs now return a consistent 404 response.
    • Valid numeric IDs, including those with leading zeros, are handled consistently.
    • Invalid requests are rejected before database queries are performed.
  • Tests

    • Added coverage for valid identifiers and a broad range of invalid input formats across credential, receipt, and status endpoints.

`GET /credentials/controller/:id` and `GET /credentials/receipts/:id` read the id with `parseInt`. A parameter with no leading digits becomes `NaN`, which reaches the query and fails it, so `/credentials/controller/abc` answers 500 with the failed statement in the response body rather than 404. A parameter with trailing characters keeps its leading digits, so `/1abc`, `/0x1`, `/1e3` and `/1.9` all return credential 1.

`status.ts` already parses its route parameter for this reason. Move that parse into `parseIdParam` and use it in all three routes, so the three do not drift apart.
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ebf79e26-2fba-44da-9e72-2daa9cd15e4a

📥 Commits

Reviewing files that changed from the base of the PR and between 0b8fdaa and fd3fdd5.

📒 Files selected for processing (7)
  • examples/issuer/src/lib/utils/parse-id-param.test.ts
  • examples/issuer/src/lib/utils/parse-id-param.ts
  • examples/issuer/src/routes/credentials.test.ts
  • examples/issuer/src/routes/credentials.ts
  • examples/issuer/src/routes/receipts.test.ts
  • examples/issuer/src/routes/receipts.ts
  • examples/issuer/src/routes/status.ts

Walkthrough

Changes

Issuer ID validation

Layer / File(s) Summary
Shared ID parser and validation tests
examples/issuer/src/lib/utils/parse-id-param.ts, examples/issuer/src/lib/utils/parse-id-param.test.ts
Adds strict numeric ID parsing. Tests cover valid, malformed, noncanonical, and unsafe values.
Credential and receipt route integration
examples/issuer/src/routes/credentials.ts, examples/issuer/src/routes/credentials.test.ts, examples/issuer/src/routes/receipts.ts, examples/issuer/src/routes/receipts.test.ts
Credential and receipt routes validate IDs before database access and return 404 for invalid values.
Status route ID integration
examples/issuer/src/routes/status.ts
The status route uses the normalized ID for lookup and credential URL construction, and returns 404 for invalid values.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to fd3fd

The change validates credential route IDs before querying and adds coverage for accepted and rejected inputs; no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

Suggested reviewers: venables

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: validating credential IDs before database queries.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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