Hostile-input test banks: JSONTestSuite corpus + HTTP framing - #34
Merged
Merged
Conversation
Two data-driven test banks for the "does the parser crash on hostile input" class of bug (following the JSON depth-bomb and SIGPIPE fixes). (a) JSON conformance: vendor nst/JSONTestSuite's 318-file parsing corpus (MIT, see PROVENANCE.md) and run every case through smithy::json::Decode in json_conformance_test. Invariant for all files: Decode returns, never crashes or hangs; y_ must be accepted, n_ rejected (one documented nlohmann-tolerated case allowlisted), i_ implementation-defined. A genrule bakes the corpus filenames into the parametrized case list (runfiles directory enumeration isn't portable); the test is gated to POSIX since the parser is platform-independent. (b) HTTP framing: socket_transport_hostile_test drives raw bytes at SocketHttpServer's hand-rolled parser — request-smuggling framing, malformed content-lengths, header floods, truncation — asserting the server never crashes, hangs, or accepts ambiguous framing. This surfaced two real bugs, both fixed here in socket_transport.cc: - Request smuggling: ReadMessage ignored Transfer-Encoding entirely and used the first of conflicting Content-Length headers. Now rejects both as 400 (neither transport direction implements chunked). - Response double-framing: the server emitted its own content-length and then re-emitted a handler-set one (generated payload responses set it), putting two on the wire. The transport now strips framing headers a handler set before writing its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ytv3VMrURFYP2mdWhk3un
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.
What
Two data-driven test banks for the "does the parser crash on hostile input" class of bug — the same class as the JSON depth-bomb (#33) and SIGPIPE (#32) fixes. Building them surfaced two more real bugs, both fixed here.
(a) JSON conformance corpus. Vendors nst/JSONTestSuite's 318-file parsing corpus (MIT — the canonical "Parsing JSON is a Minefield" bank) under
runtime/tests/json/jsontestsuite/withLICENSE+PROVENANCE.md, and runs every file throughsmithy::json::Decodeinjson_conformance_test. The load-bearing invariant for all 318:Decodereturns, never crashes or hangs. On top of that,y_must be accepted,n_rejected,i_implementation-defined. Results on our stack (nlohmann +FromBackend+ the depth guard): 95/95 valid accepted, 187/188 invalid rejected (one nlohmann-tolerated trailing-NUL case allowlisted with a comment), zero crashes. A genrule bakes the corpus filenames into the parametrized case list (runfiles directory enumeration isn't portable); the test is gated to POSIX since the JSON parser is platform-independent.(b) HTTP framing.
socket_transport_hostile_testdrives raw bytes atSocketHttpServer's hand-rolledReadMessage(the client only ever emits well-formed requests, so this is the only way to reach the attacker-controlled paths): conflictingContent-Length,Transfer-Encoding, negative/non-numeric/overflowing lengths, header floods, truncated headers. Invariant: never crash, never hang, never accept ambiguous framing as valid. POSIX-only (raw-socket client; the parser is platform-independent).Bugs found and fixed (
runtime/src/http/socket_transport.cc)ReadMessageignoredTransfer-Encodingentirely and used the first of conflictingContent-Lengthheaders — a proxy and this server could disagree on message boundaries. Now both are rejected as 400 (neither transport direction implements chunked). TheRejectsTransferEncodingandRejectsConflictingContentLengthcases fail without this fix and pass with it.content-length, then re-emitted a handler-set one — and generated payload responses (e.g.UploadAttachment) do setcontent-length— putting two on the wire. Harmless before only because clients silently took the first; the new strict client (fix Phased implementation plan for smithy-cpp client & server generators #1) correctly rejects it. The transport now strips framing headers a handler set before writing its own. Caught by the existingUploadAttachmentroundtrip integration test once the client turned strict;ServerEmitsExactlyOneContentLengthpins it directly.Testing
//runtime:json_conformance_test(318 parametrized cases) and//runtime:socket_transport_hostile_test(10 cases) pass locally.UploadAttachmentroundtrip fail without it, pass with it.clang-format,clang-tidy(clean on the touched source), andbuildifierall pass.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_019ytv3VMrURFYP2mdWhk3un
Generated by Claude Code