Skip to content

Reject oversized bid dimensions instead of truncating them - #1182

Open
dhruv8sh wants to merge 2 commits into
mainfrom
fix/adserver-mock-dimension-truncation
Open

dhruv8sh wants to merge 2 commits into
mainfrom
fix/adserver-mock-dimension-truncation

Conversation

@dhruv8sh

@dhruv8sh dhruv8sh commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Bid width and height in the mock ad-server mediation path were cast from u64 to u32 with as, which silently wraps values above u32::MAX into small, plausible-looking dimensions that pass the zero-check.
  • Use u32::try_from and skip the bid (log at debug) when a dimension doesn't fit, consistent with the existing zero-dimension handling.
  • Chose skip over clamp: it matches the existing zero-dimension behavior and doesn't invent a dimension the bidder never sent.

Changes

File Change
crates/trusted-server-core/src/integrations/adserver_mock.rs Replace truncating as u32 casts on bid w/h with u32::try_from, skipping the bid on overflow; add tests for oversized and zero/missing dimensions

Closes

Closes #420

Test plan

  • cargo test-fastly && cargo test-axum
  • cargo clippy-fastly && cargo clippy-axum
  • cargo fmt --all -- --check
  • JS tests: cd crates/trusted-server-js/lib && npx vitest run
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve
  • Other:

Checklist

  • Changes follow AGENTS.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses tracing macros (not println!)
  • New code has tests
  • No secrets or credentials committed

Bid width and height from the mock ad-server mediation response were
cast from u64 to u32 with \`as\`, which silently wraps a value above
u32::MAX into an unrelated small number that can pass the zero-check
and flow onward with corrupted dimensions. Use u32::try_from and skip
the bid, consistent with the existing zero-dimension handling.

Closes #420

Signed-off-by: dhruv8sh <dhruv8sh@proton.me>

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

The checked conversion correctly prevents oversized mock ad-server dimensions from entering auction consumers. One medium-severity test gap remains: the oversized fixture wraps to zero under the old implementation, so it does not reproduce the original nonzero-truncation defect.

Comment thread crates/trusted-server-core/src/integrations/adserver_mock.rs Outdated

@prk-Jr prk-Jr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

Reviewed commit 6f95f1d5096240b80fe7c44bb25ddd4c07edb3ea.

The checked conversion correctly rejects overflowing dimensions and preserves ordinary, zero, and missing-dimension behavior. One non-blocking test improvement remains.

Non-blocking

  • 🤔 [P2] Exercise a nonzero wrapped value in the overflow regression — see inline at crates/trusted-server-core/src/integrations/adserver_mock.rs:1343.

Validation

All 18 mock-adserver tests pass at the reviewed head. Mutation checks confirm that the original cast passes the current fixture, fails with a u32::MAX + 101 fixture, and the checked conversion passes with that stronger fixture. Scratch source was restored byte-for-byte. These checks establish the test gap; they are not a full release-gate validation of a proposed patch.

CI Status

Comment thread crates/trusted-server-core/src/integrations/adserver_mock.rs Outdated

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

The production fix is correct and complete. I swept the repository for the JSON-accessor-to-narrowing-as idiom this PR removes and found zero remaining instances — adserver_mock.rs really was the last one, and Prebid, APS, and the generic OpenRTB path already use checked conversions. Skipping the bid rather than clamping is the right call, and the PR body justifies it well.

The blocking issue is in the test, not the fix: test_parse_mediation_response_skips_oversized_dimensions passes against the unfixed code, so it does not pin the regression it was written for.

1 of the inline comments below carries a one-click GitHub suggestion — use Commit suggestion to apply it as a commit on the PR branch. The remaining comments are prose because they describe follow-ups or trade-offs rather than a mechanical edit.

Blocking

wrench

  • Oversized-dimension test passes against the unfixed code — see inline at crates/trusted-server-core/src/integrations/adserver_mock.rs:1343

Non-blocking

thinking / out of scope / nitpick

  • Skip-vs-unwrap_or(0) diverges from the cited prebid precedent — see inline at crates/trusted-server-core/src/integrations/adserver_mock.rs:295
  • Rejects float-encoded dimensions the shared helper accepts — see inline at crates/trusted-server-core/src/integrations/adserver_mock.rs:301
  • Skip logs drop the offending value — see inline at crates/trusted-server-core/src/integrations/adserver_mock.rs:297
  • adserver_mock is the only provider not validating dimensions against requested slot formats — see cross-cutting below

Cross-cutting / body-level findings

  • Out of scope — adserver_mock is the only bid parser that does not validate dimensions against the requested slot formats. Prebid and the generic OpenRTB path both call resolve_bid_dimensions (crates/trusted-server-core/src/auction/openrtb.rs:195), which rejects with DimensionMismatch; APS checks dimensions.contains(...) (crates/trusted-server-core/src/integrations/aps.rs:671) and compatible_dimensions (aps.rs:1375). adserver_mock.rs:307 only checks width == 0 || height == 0, so it still accepts an in-range but never-requested size such as 1x1 or 9999x9999.

    This is pre-existing and arguably acceptable for a mock provider, so it is not a blocker for this PR. Worth noting that it is the reason this path needed a bespoke dimension check at all: routing it through parse_optional_bid_dimension + resolve_bid_dimensions like the other three providers would delete the hand-rolled logic and make the truncation fix fall out for free. A reasonable follow-up issue.

  • Completeness check (informational). Two sweeps over all crates — as_(u64|i64|f64)\(\).*\bas (u|i)(8|16|32)\b and unwrap_or\([0-9]+\) as — returned no hits after this change, so this PR closes the last instance of the pattern rather than one of several.

Verification performed

Scratch-verified in an isolated worktree at 6f95f1d50:

  • The PR's two new tests were grafted onto the merge-base commit 6cae7f5da (the unfixed code). Both passed, confirming the oversized test does not detect the bug.
  • Changing only the test input to u64::from(u32::MAX) + 101 makes that test fail on the base commit (left: 3, right: 1) and pass on this PR's head — a real regression test.
  • With the suggested fix applied: cargo fmt --all -- --check, all six clippy aliases (fastly, axum, cloudflare, cloudflare-wasm, spin-native, spin-wasm), cargo test-axum -p trusted-server-core (2669 passed), cargo test-cloudflare, cargo test-spin, and the cross-adapter parity suite (13 passed) all pass.

CI Status

All 20 reported checks PASS.

  • cargo test: PASS
  • cargo test (axum native): PASS
  • cargo test (cross-adapter parity): PASS
  • cargo test (ts CLI, native): PASS
  • cargo check (cloudflare native + wasm32-unknown-unknown): PASS
  • cargo check/build/test (spin native + wasm32-wasip1): PASS
  • cargo fmt: PASS
  • vitest: PASS
  • format-typescript: PASS
  • format-docs: PASS
  • CodeQL: PASS
  • Analyze (rust): PASS
  • Analyze (actions): PASS
  • Analyze (javascript-typescript): PASS
  • integration tests: PASS
  • integration tests (Fastly EC lifecycle): PASS
  • browser integration tests: PASS
  • prepare integration artifacts: PASS
  • CLAUDE.md symlink guard: PASS

Comment thread crates/trusted-server-core/src/integrations/adserver_mock.rs Outdated
Comment thread crates/trusted-server-core/src/integrations/adserver_mock.rs
Comment thread crates/trusted-server-core/src/integrations/adserver_mock.rs
Comment thread crates/trusted-server-core/src/integrations/adserver_mock.rs Outdated
The fixture used u32::MAX + 1, which truncates to zero under the original as u32 cast and is already caught by the separate zero-dimension check, so the test passed even without the fix. Use u32::MAX + 101 so the test actually fails against the old truncating cast, and log the offending raw value when a width or height is rejected.

Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
@dhruv8sh
dhruv8sh requested review from aram356 and prk-Jr September 21, 2026 16:05
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.

Integer truncation on bid dimensions in adserver_mock

4 participants