Validate synthetic ID format on inbound header and cookie values#508
Open
Validate synthetic ID format on inbound header and cookie values#508
Conversation
Inbound synthetic IDs from the x-synthetic-id header and synthetic_id cookie were accepted without validation. An attacker could inject arbitrary strings — including very long values, special characters, or newlines — which were then set as response headers, cookies, and forwarded to third-party APIs. Adds a private is_valid_synthetic_id() validator enforcing the canonical format (64 lowercase hex chars + '.' + 6 alphanumeric chars). The length check is O(1) and runs first to bound all downstream work. Invalid values are silently discarded and a fresh ID is generated in their place; the raw value is never written to logs. Also adds a debug_assert! in generate_synthetic_id() to catch any future regression in the generator, moves VALID_SYNTHETIC_ID to test_support so it is shared across all test modules, and demotes synthetic ID values from INFO to DEBUG in log output to avoid recording pseudonymous identifiers in production log pipelines. Closes #412
ChristianPavilonis
approved these changes
Mar 16, 2026
Collaborator
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Clean, well-executed security hardening. Validation logic is correct and thoroughly tested. Approving with one suggestion inline.
Strengths:
- O(1) length check gates all downstream work — good defense against oversized input on an edge server
- Lowercase hex enforcement prevents intermediary case-normalization from creating fake-valid IDs
debug_assert!in the generator ensures validator and generator never drift apart- Logging hygiene — invalid values logged with
len={}only, never the raw attacker-controlled string - Excellent test coverage with 8+ new/updated test cases
- Shared
VALID_SYNTHETIC_IDconstant eliminates test fragility across 3 modules - CHANGELOG and docs properly updated
Note: The lol_html bump (e295f3a) in Cargo.toml/Cargo.lock appears to be a merge artifact from main — unrelated to the synthetic ID validation. Not a problem, just flagging it.
| "should fall through to valid cookie when header ID is invalid" | ||
| ); | ||
| } | ||
|
|
Collaborator
There was a problem hiding this comment.
suggestion (P2): Tests cover valid header, valid cookie, invalid header → cookie fallthrough, and more — but there's no test confirming that when both header and cookie contain valid-but-different IDs, the header wins. The behavior is implicitly correct from code structure, but an explicit test would document the priority semantics.
Suggested change
| } | |
| #[test] | |
| fn test_get_synthetic_id_header_takes_precedence_over_cookie() { | |
| let cookie_id = "b2a1c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0b1a2.Zx98y7"; | |
| let req = create_test_request(vec![ | |
| (HEADER_X_SYNTHETIC_ID, VALID_SYNTHETIC_ID), | |
| ( | |
| header::COOKIE, | |
| &format!("{}={}", COOKIE_SYNTHETIC_ID, cookie_id), | |
| ), | |
| ]); | |
| let result = get_synthetic_id(&req).expect("should succeed"); | |
| assert_eq!( | |
| result, | |
| Some(VALID_SYNTHETIC_ID.to_string()), | |
| "should prefer header over cookie" | |
| ); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
x-synthetic-idheader andsynthetic_idcookie were accepted without validation, allowing injection of arbitrary strings into response headers, cookies, and third-party API callsis_valid_synthetic_id()validator (64 lowercase hex +.+ 6 alphanumeric) with an O(1) length check first to bound all downstream work; invalid values are silently discarded and a fresh ID is generated in their placeINFOtoDEBUGto avoid recording pseudonymous identifiers in production log pipelinesChanges
crates/common/src/synthetic.rsis_valid_synthetic_id()production validator; validate inget_synthetic_id(); adddebug_assert!in generator; demote ID values todebug!; new rejection + fallthrough testscrates/common/src/test_support.rsVALID_SYNTHETIC_IDconstantcrates/common/src/proxy.rsVALID_SYNTHETIC_IDcrates/common/src/integrations/registry.rsdocs/guide/synthetic-ids.mdCHANGELOG.md### Securityentry under[Unreleased]Closes
Closes #412
Test plan
cargo test --workspacecargo clippy --all-targets --all-features -- -D warningscargo fmt --all -- --checkcd crates/js/lib && npm run formatcd docs && npm run formatChecklist
unwrap()in production code — useexpect("should ...")tracingmacros (notprintln!)