Skip to content

fix: bound stream signature length - #228

Merged
rubenhensen merged 2 commits into
mainfrom
fix/bound-stream-signature-length
Jul 10, 2026
Merged

fix: bound stream signature length#228
rubenhensen merged 2 commits into
mainfrom
fix/bound-stream-signature-length

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

The streaming Unsealer read a length prefix from the input stream and used it directly to size a buffer allocation before the corresponding data was validated. This adds an explicit upper bound so a malformed/oversized length prefix is rejected cleanly instead of driving an allocation.

Changes

  • Add MAX_SIG_SIZE (8 KiB) constant in pg-core/src/consts.rs. A serialized header signature holds a single signature plus its policy and fits comfortably in a few KiB.
  • Reject length prefixes exceeding MAX_SIG_SIZE with Error::ConstraintViolation in both streaming unsealers:
    • pg-core/src/client/rust/stream.rs (native)
    • pg-core/src/client/web/stream.rs (wasm)
    • This mirrors the existing MAX_HEADER_SIZE check in preamble_checked.
  • Add a regression test (test_stream_unseal_rejects_oversized_sig_len) confirming an oversized length prefix is rejected with ConstraintViolation rather than allocating.

Testing

  • cargo test -p pg-core --features "test,rust,stream" — all 54 tests pass (incl. the new regression) + doc-tests.
  • cargo fmt -p pg-core -- --check — clean.
  • cargo check -p pg-core --no-default-features --features "web,stream" --target wasm32-unknown-unknown — clean.

Closes #227.

Kept generic while the associated security advisory is embargoed.

The streaming Unsealer read a length prefix from the input and used it to
size a buffer allocation before any validation. Add a MAX_SIG_SIZE bound
(8 KiB) in consts.rs and reject oversized values with
Error::ConstraintViolation in both the native and web streaming unsealers,
mirroring the existing MAX_HEADER_SIZE check in preamble_checked.

Adds a regression test that an oversized length prefix is rejected cleanly
instead of driving an allocation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Rules + Review gatekeeper — cycle 1

The security bound itself is correct and well-tested: the MAX_SIG_SIZE (8 KiB) check lands before both allocations, mirrors the existing MAX_HEADER_SIZE check, returns the right error, and the regression test exercises the rejection path cleanly. Full native suite (54 tests) + wasm check + cargo fmt are green, and the happy path stays covered by the existing stream round-trip tests. PR title/body, commit, branch name, and issue #227 (masked advisory link, redacted comments) are all appropriately generic.

One item to address before sign-off, plus a nit:

1. Embargo hygiene (blocking). This is a public repo and the driving advisory is still in draft. A few comments — the two fix comments, the doc comment on the new constant (which gets published in the crate docs), and the regression-test comment — describe the issue's mechanism/impact more specifically than the (correctly neutral) PR body does. While the embargo holds, please genericize those to a neutral rationale, e.g. "bound the length prefix to a sane maximum before allocation, mirroring MAX_HEADER_SIZE", and restore the full detail once the advisory is published. Keep the code fix and test logic byte-identical. Inline notes mark each spot.

2. Nit (non-blocking). web/stream.rs uses Err(Error::ConstraintViolation.into()) where the native side uses a bare Err(Error::ConstraintViolation); since new() returns Result<Self, Error> the .into() is an identity conversion — drop it for consistency.

Keeping the PR in draft while the advisory is embargoed (not flipping to ready). Routing back for a quick fix pass.

Comment thread pg-core/src/consts.rs Outdated
Comment thread pg-core/src/client/rust/stream.rs Outdated
Comment thread pg-core/src/client/rust/stream.rs
Comment thread pg-core/src/client/web/stream.rs Outdated
Comment thread pg-core/src/client/web/stream.rs Outdated
Reword the MAX_SIG_SIZE doc comment, the two bound-check comments, and
the regression-test comment to neutral rationale (mirrors MAX_HEADER_SIZE)
while the advisory is still in draft; code is unchanged. Also drop a
redundant identity .into() on the web unsealer error path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Rules + Review gatekeeper — cycle 2

Clean. Both cycle-1 items are resolved:

1. Embargo hygiene (was blocking) — fixed. The two bound-check comments, the MAX_SIG_SIZE doc comment, and the regression-test comment were all genericized to neutral rationale ("bound the length prefix to a sane maximum before allocation, mirroring MAX_HEADER_SIZE") in commit 8310ba96, with the code/test logic left byte-identical. A tree-wide sweep confirms no draft-advisory id or vulnerability detail leaks into any public artifact: PR title/body, both commit messages, branch name, and issue #227 (masked advisory link, redacted comments) are all appropriately generic.

2. Nit — fixed. The redundant identity .into() on the web unsealer error path was dropped; it now matches the native side (return Err(Error::ConstraintViolation)).

The security bound itself remains correct and well-tested: the check lands before both allocations in rust/stream.rs and web/stream.rs, mirrors the existing MAX_HEADER_SIZE guard, returns the right error, and the regression test exercises the rejection path cleanly while the happy path stays covered by the existing round-trip tests. Native suite (54 tests) + wasm check + cargo fmt are green per the PR notes.

Staying in draft until the associated advisory is published — this is a public repo and the driving advisory is still embargoed, so I'm not flipping to ready-for-review even though the gate is clean. Signing off; no further changes needed.

@rubenhensen
rubenhensen marked this pull request as ready for review July 10, 2026 08:41
@rubenhensen
rubenhensen merged commit 8503bb2 into main Jul 10, 2026
21 checks passed
@rubenhensen
rubenhensen deleted the fix/bound-stream-signature-length branch July 10, 2026 08:41
@github-actions github-actions Bot mentioned this pull request Jul 10, 2026
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.

Harden pg-core stream parser against malformed length prefixes

1 participant