Skip to content

csrf: parse the encoding option in one place - #39140

Merged
alii merged 4 commits into
mainfrom
farm/eb94bd83/csrf-token-format-helper
Aug 15, 2026
Merged

alii merged 4 commits into
mainfrom
farm/eb94bd83/csrf-token-format-helper

Conversation

@robobun

@robobun robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • Bun.CSRF.generate and Bun.CSRF.verify each read the encoding option with their own copy of the same code: get("encoding"), Encoding::from_js_with_default_on_empty, the Encoding to csrf::TokenFormat match, and the Invalid format error (which appeared four times). src/runtime/api/csrf_jsc.rs:99-120 and :254-275 before this change.
  • mordant's same_match_twice reports the second copy; it is the same_match_twice:src/runtime/api/csrf_jsc.rs entry in mordant-baseline.toml.

Fix

  • One get_optional_token_format(options, global) helper in csrf_jsc.rs, next to the existing get_optional_int_u64, called from both functions at the same point in the option-reading order. The None from an unknown encoding name and the non-token encodings share the one error arm, so the message exists once.
  • The mapping stays in csrf_jsc.rs rather than on an enum: TokenFormat lives in bun_csrf, which cannot see crate::node::Encoding, and this file is TokenFormat's only user. The comment on the reverse mapping (used to encode the token) still named TokenFormat::to_node_encoding, removed in Remove ~39k lines of dead Rust across the workspace #35002; reworded.
  • Removed the baseline entry. bun run rust:mordant over the workspace is clean with it removed; with the entry removed but the old source restored it reports the one finding at csrf_jsc.rs:265, so the entry was this site.
  • No behavior change. Compared the release build against this build on the same script covering every rejected encoding (utf8, latin1, buffer, bogus, null, a number, an object), case-insensitive names, "", undefined, and the order in which the option properties are read by both functions: identical output.
  • Verified with bun bd test test/js/bun/util/csrf.test.ts (25 pass). Added one test pinning the error message for both functions and the "" default, since that path was not covered; it passes before and after, as expected for a refactor.

Background

  • crate::node::Encoding is the Buffer encoding enum (utf8, hex, base64, ...). from_js_with_default_on_empty parses a JS value as an encoding name, returning the given default for "" and None for names it does not know.
  • csrf::TokenFormat is the three-variant enum (Base64, Base64Url, Hex) the bun_csrf crate encodes and decodes tokens with. The CSRF API accepts the encoding option as a Buffer encoding name and narrows it to a TokenFormat, which is the mapping this PR deduplicates.
  • mordant is the lint pack run by bun run rust:mordant; mordant-baseline.toml records the accepted count of findings per (lint, file), and CI fails only on findings above the recorded count, so fixing a finding means deleting its entry.
  • Deduplicate small helpers across crypto, api objects, sockets, valkey, napi, bake, and coverage #37366 (an 82-file dedup PR stacked on Deduplicate package manager and CLI command helpers #32022) contains an equivalent helper for this file among many others; when it is rebased, its csrf_jsc.rs hunk becomes redundant with this one.

Bun.CSRF.generate and Bun.CSRF.verify each read the `encoding` option
with the same copy of the Encoding to TokenFormat mapping and its error.
Move it into get_optional_token_format, next to get_optional_int_u64,
and call it from both. No behavior change.

Drop the now clean same_match_twice entry for this file from the
mordant baseline, and fix the comment on the reverse mapping, which
still named TokenFormat::to_node_encoding (removed in #35002).
@coderabbitai

coderabbitai Bot commented Aug 15, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9cf23fe9-50d8-4787-a2e7-aac0db6bc967

📥 Commits

Reviewing files that changed from the base of the PR and between 47fb36e and 72bc5bf.

📒 Files selected for processing (3)
  • mordant-baseline.toml
  • src/runtime/api/csrf_jsc.rs
  • test/js/bun/util/csrf.test.ts

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

@robobun

robobun commented Aug 15, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 12:05 PM PT - Aug 15th, 2026

@alii, your commit 72bc5bf is building: #98399

@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Status: ready for review.

  • bun bd test test/js/bun/util/csrf.test.ts: 25 pass.
  • bun run rust:mordant (workspace): clean with the baseline entry removed. Same command with the entry removed but the previous csrf_jsc.rs restored reports exactly one finding, same_match_twice at csrf_jsc.rs:265, so the deleted entry was this site.
  • bun run rust:mordant:baseline on this branch reproduces the committed file except for two entries unrelated to this change that also no longer fire (always_unwrapped_option:src/install/PackageInstall.rs, narrowed_two_ways:src/runtime/node/node_crypto_binding.rs); left alone here to keep this PR to the one site.

@claude claude 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.

LGTM — mechanical dedup of two identical encoding option-parsing blocks into one helper; no behavior change.

What was reviewed:

  • Traced old vs new control flow: the None (unknown name) and Some(non-token encoding) cases both still hit the same error, and property-read order at both call sites is unchanged.
  • Checked that the helper mirrors the existing get_optional_int_u64 pattern and that the reverse-mapping comment update is accurate.
  • New test asserts the exact error message for both generate and verify and the "" → base64url default; loop is non-empty so assertions can fail.
  • mordant-baseline.toml entry removal matches the deduplicated site.
Extended reasoning...

Overview

This PR extracts a ~20-line block that appeared twice — once in csrf__generate and once in csrf__verify — into a single get_optional_token_format helper in src/runtime/api/csrf_jsc.rs. The block reads the encoding option, parses it via NodeEncoding::from_js_with_default_on_empty, maps it to csrf::TokenFormat, and throws on unsupported values. It also removes the corresponding same_match_twice entry from mordant-baseline.toml, updates a stale comment referring to a removed TokenFormat::to_node_encoding helper, and adds one test in test/js/bun/util/csrf.test.ts pinning the error message and the empty-string default.

Security risks

None. The CSRF token generation and verification logic (csrf::generate / csrf::verify) is untouched. Only the option-parsing plumbing that maps a user-supplied encoding name to a TokenFormat enum is deduplicated, and its input/output behavior is byte-for-byte identical: unknown names and non-token Buffer encodings still throw the same error, "" still defaults to base64url, and the three accepted formats still map to the same variants.

Level of scrutiny

Low. This is a textbook "second time it appears in your diff, extract a named helper" refactor from the review guide. The helper is placed next to the existing get_optional_int_u64 helper it mirrors, is called at the same point in the option-read sequence in both functions (so observable getter-invocation order is preserved), and the old two-error-site structure (let Some(...) else { err } + match { _ => err }) is collapsed into one _ arm on a match over Option<NodeEncoding> — semantically equivalent.

Other factors

The added test is well-formed: it iterates a fixed non-empty list (so the toThrow assertions inside the loop always run), asserts the exact error string rather than a bare toThrow(), exercises both generate and verify, and covers both the "known encoding but not a token format" (utf8, latin1, buffer) and "not an encoding at all" (bogus) branches that now share one error arm. The PR description documents that the test passes both before and after (correct for a refactor) and that behavior parity was verified against a release build. No CODEOWNERS apply to the touched files.

@alii
alii merged commit bcab5ed into main Aug 15, 2026
9 of 10 checks passed
@alii
alii deleted the farm/eb94bd83/csrf-token-format-helper branch August 15, 2026 17:19
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.

2 participants