A capability token with a line break inside it is printed in the job log #51

Open
opened 2026-10-06 05:35:52 +00:00 by jercik · 0 comments
Owner

When REVIEW_CAPABILITY_TOKEN holds a carriage return or line feed inside its value, the wrapper prints the whole token to stderr after 40 seconds of retries, and no request ever reaches the service.

What happens

reviewCredentials() returns the token without checking its shape (src/credentials.ts:69). The runtime then refuses to build the Authorization header, and its error message quotes the header value. The retry ladder copies that message into its failure text (src/review/session.ts:121-124), and the orchestrator writes it to stderr (src/wrapper/orchestrator.ts:153-155).

Reproduced at 9f272a52 with the committed bundle on Node 24.21.0, the runtime action.yml declares. A token of FAKECAP-AAAA\r\nFAKECAP-BBBB was printed in full, 40 seconds after the start, with 0 requests sent to the service (scenarios s1-crlf, s1-lf-only, s1-cr-only). A line feed at the start of the value is printed too (s1-leading-lf), because it sits after Bearer in the header. A line break at the end of the value is not printed (s1-trailing-lf), and neither is trailing whitespace (s1-ws).

What it should do instead

reviewCredentials() should reject a token that contains a character an HTTP header value cannot hold. The error should name the variable and the problem and never repeat the value. Failing there ends the run immediately, before the retry ladder.

Why it matters

The capability token has no expiry (src/review/session.ts:52), so a printed token stays valid until someone rotates it. If the real token is one of the lines of the stored value, that line is printed along with the rest.

This needs an operator to store a malformed secret. A pull request cannot cause it. Whether the job log shows the value or masks it depends on the runner's secret masking, which has not been checked on the deployed runner.

🤖 Generated with Claude Code

When `REVIEW_CAPABILITY_TOKEN` holds a carriage return or line feed inside its value, the wrapper prints the whole token to stderr after 40 seconds of retries, and no request ever reaches the service. ## What happens `reviewCredentials()` returns the token without checking its shape ([`src/credentials.ts:69`](https://code.j4k.dev/j4k-oss/review-wrapper/src/commit/9f272a522e8ed6f748533761de45ee3e94fd7481/src/credentials.ts#L69)). The runtime then refuses to build the `Authorization` header, and its error message quotes the header value. The retry ladder copies that message into its failure text ([`src/review/session.ts:121-124`](https://code.j4k.dev/j4k-oss/review-wrapper/src/commit/9f272a522e8ed6f748533761de45ee3e94fd7481/src/review/session.ts#L121-L124)), and the orchestrator writes it to stderr ([`src/wrapper/orchestrator.ts:153-155`](https://code.j4k.dev/j4k-oss/review-wrapper/src/commit/9f272a522e8ed6f748533761de45ee3e94fd7481/src/wrapper/orchestrator.ts#L153-L155)). Reproduced at `9f272a52` with the committed bundle on Node 24.21.0, the runtime `action.yml` declares. A token of `FAKECAP-AAAA\r\nFAKECAP-BBBB` was printed in full, 40 seconds after the start, with 0 requests sent to the service (scenarios `s1-crlf`, `s1-lf-only`, `s1-cr-only`). A line feed at the start of the value is printed too (`s1-leading-lf`), because it sits after `Bearer ` in the header. A line break at the end of the value is not printed (`s1-trailing-lf`), and neither is trailing whitespace (`s1-ws`). ## What it should do instead `reviewCredentials()` should reject a token that contains a character an HTTP header value cannot hold. The error should name the variable and the problem and never repeat the value. Failing there ends the run immediately, before the retry ladder. ## Why it matters The capability token has no expiry ([`src/review/session.ts:52`](https://code.j4k.dev/j4k-oss/review-wrapper/src/commit/9f272a522e8ed6f748533761de45ee3e94fd7481/src/review/session.ts#L52)), so a printed token stays valid until someone rotates it. If the real token is one of the lines of the stored value, that line is printed along with the rest. This needs an operator to store a malformed secret. A pull request cannot cause it. Whether the job log shows the value or masks it depends on the runner's secret masking, which has not been checked on the deployed runner. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
j4k-oss/review-wrapper#51
No description provided.