Repository navigation
Conversation
VerificationAdversarial verification at head Superseded check first: upstream issue stoplightio#2744 is still open; File test (re-run, verbatim)
Head: Base (production file swapped to Production source restored to head afterward; Mutant (always pass
|
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the gating lane (gating review).
Verdict: APPROVE. Ready for the operator to submit upstream. Reviewed at head 46fec025db30b98996b6c942b3e8287fe20427ef against base 2024fd9e1b47c5a6b1bde701241d924271ed4394.
What I checked
Bug is real on the base. I fetched convertRequestToSample.ts at the base sha. It calls snippet.convert(language, library) with no options. In httpsnippet-lite 3.0.5 (dist/cjs/targets/shell/curl/client.js:50) binary defaults to false, and for a raw postData.text body line 136 does const flag = binary ? '--data-binary' : arg('data'), so the HAR that buildHarRequest emits for a picked file (text: '@<name>', build-request.ts:299-303 at base) becomes --data @<name>. curl's --data @file strips CR, LF and NUL, so the copied sample uploads a different body from the file. The body's 7-byte echo-server run (3 bytes received on base, 7 on head) matches what the code says will happen.
The fix. convertRequestToSample.ts:15-17:
const options = request.postData?.mimeType === 'application/octet-stream' ? { binary: true } : undefined;
let converted = await snippet.convert(language, library, options);binary is a declared member of the library's CurlOptions; convert's third argument is options?: any so the call type-checks. I grepped the shipped targets for binary: only shell/curl reads it, so passing it for the other 33 targets is a no-op, which the verification's 238-case base/head probe measured (one line changed, shell/curl). The existing --data-urlencode rewrite is gated on a urlencoded Content-Type header and cannot coincide with an octet-stream postData.mimeType from the producer, so the two rewrites do not interact.
The test proves it. The new case builds the exact HAR shape the producer emits and asserts toContain('--data-binary @some-file.bin'). On base the cURL client emits --data @some-file.bin, which does not contain that substring, so it fails; on head it passes. The body's verbatim before/after output shows exactly that (1 failed / 36 passed, then 37 passed). The "always binary" mutant is killed by the existing given shell / curl snapshot, so the other side of the predicate is pinned by existing tests.
Boundaries. Walked the one predicate the diff adds (postData?.mimeType === 'application/octet-stream'): postData undefined (existing curly-bracket fixtures, postData: undefined), non-octet-stream mimeType (34 snapshots), exact match (new test). Mixed-case and parameterised mimeTypes cannot reach this path with an @file body because buildHarRequest:299 compares the same exact string, and the ledger says so with the producer line. No unpinned reachable input.
Prior art, re-run. gh search prs --repo stoplightio/elements for octet-stream, data-binary, 2744, binary request sample: no PR about the cURL sample flag (the data-binary hits are dependency bumps and stoplightio#807/stoplightio#1715 file-upload work from years ago). gh search issues for octet-stream: only stoplightio#2744 (still open, 0 comments) and closed stoplightio#2615. The last three commits touching convertRequestToSample.ts on upstream main (stoplightio#2835, stoplightio#2799, stoplightio#2608) do not address this. Not a duplicate.
Policy. CONTRIBUTING at main requires tests with bug fixes in a __tests__ directory next to the component; the diff does that. No AI policy, CLA, DCO, changeset or mandated trailer. Commit 46fec025 has a conventional fix: subject in the repo's style and an empty body; no AI attribution in the commit, branch or title.
Reads-as-generated pass. No em dashes, filler words, control cases, patch narration or history in the diff, the commit, or the title. The test name and fixture shape match the file's existing describe('curly bracket encoding') block. The one source comment states why --data-binary is needed (a fact the code cannot show) and does not restate the line. 23 test lines for a 3-line fix.
CI. gh pr checks reports none (fork Actions not enabled; the body says so and the operator card exists). Verification was executed locally with verbatim jest, eslint, prettier and tsc output.
Notes for the operator
- Recent
fix:commits on upstreammainbump the three packages'package.jsonversions; this PR does not. Add a patch bump only if a maintainer asks (the body already records this). - Upstream's PR template has a "Read CONTRIBUTING.md" checkbox and asks for elements-owner as reviewer; tick and assign on the upstream PR.
What's good
Minimal, uses the library's own option instead of post-processing the string, the ledger names the producer line for every "unreachable" row, and the alternatives section rejects the regex rewrite for a concrete reason (--data-urlencode collision).
|
Submitted upstream for review. |
Summary
application/octet-streamrequest body, the cURL request sample (the default sample language) printed--data @<file>. curl's--datastrips carriage returns, newlines and NUL bytes from a file it reads, so copying the sample uploads a corrupted file.convertRequestToSamplenow passes httpsnippet-lite's own cURL option{ binary: true }whenpostData.mimeTypeisapplication/octet-stream, so the sample prints--data-binary @<file>, which sends the file byte-for-byte.convertRequestToSample.ts, plus one test next to the existing tests inconvertRequestToSample.spec.ts.Upstream
main2024fd9e1b47c5a6b1bde701241d924271ed4394(main has since moved to414608ff, a dependabot prismjs bump that touches none of the files below)packages/elements-core/src/components/RequestSamples/convertRequestToSample.ts,convertRequestToSamplepackages/elements-core/src/components/TryIt/build-request.ts,buildHarRequest, lines 299-303 (text: \@${bodyInput.name}``, added in f01e454 "feat: File Uploads as Request Body (feat: File Uploads as Request Body stoplightio/elements#2737)")Bug
When an operation's request body is
application/octet-stream(for exampletype: string, format: binary) and the user picks a file in Try It,buildHarRequestemitspostData = { mimeType: 'application/octet-stream', text: '@<file name>' }.convertRequestToSamplehands that toHTTPSnippet.convert(language, library)without options, and httpsnippet-lite's cURL target writes--data @<file name>. For--data, curl reads the file in text mode and removes CR, LF and NUL bytes, so the sample a user copies uploads a different body from the file they picked. Anyone documenting a binary upload endpoint with Elements hits this; cURL is the default sample (selectedLanguageAtomdefaults toshell,selectedLibraryAtomtocurlinRequestSamples.tsx:47-48).Repro
Generated sample (base) and the same command run against a local echo server that reports the bytes it received (
/agent-output/oss/elements/echo2744.js), using a 7-byte file containing CR, LF and NUL:The samples above were produced by calling the real
convertRequestToSampleon base and head (probe spec, not committed; outputs/agent-output/oss/elements/probe-{base,head}.txt). curl 8.22.0.Fix
binaryis an existing option of httpsnippet-lite's cURL client (targets/shell/curl/client.js:const flag = binary ? '--data-binary' : arg('data')). It is read by the cURL client only (grep ofdist/cjs/targetsforbinaryfinds onlyshell/curl), so passing it for other targets is a no-op; measured below.Alternatives rejected:
converted.replace(/--data\b/, '--data-binary'), like the existing urlencoded rewrite): gives the same output here but reimplements a flag the library already has, and the regex would also need to avoid--data-urlencode.language === 'shell' && library === 'curl': behaviourally identical (only cURL reads the option) and longer.buildHarRequestline 299) only emits the@<file>body for the exact stringapplication/octet-stream, so a looser check here changes nothing reachable (Boundaries rows 6-7).Test evidence
application/octet-stream body › should send the file unmodified with --data-binary in curlpackages/elements-core/src/components/RequestSamples/__tests__/convertRequestToSample.spec.tsExisting tests that pin the other side of the new condition (unchanged, pass on both arms): the 34
given <language>, convertRequestToSample converts a request to a samplesnapshots (JSON body). Mutant "always pass{ binary: true }" failsgiven shell / curl(snapshot--databecomes--data-binary):Tests: 1 failed, 36 passed/Snapshots: 1 failed, 33 passed(/agent-output/oss/elements/mutant-always.txt). Mutant "never pass it" is base, killed by the new test.Runs:
npx jest --maxWorkers=2 src/components/RequestSamples/__tests__/convertRequestToSample.spec.tsbase: 1 failed, 36 passed; head: 37 passed; 34 snapshots passed on both (transcripts/agent-output/oss/elements/{base,head}-arm.txt).npx jest --maxWorkers=2 src/components/RequestSampleshead: 2 suites, 43 passed, 34 snapshots passed (includesRequestSamples.test.tsx).npx eslinton both touched files: rc=0.npx prettier --checkon both: "All matched files use Prettier code style!".npx tsc --noEmit -p packages/elements-core/tsconfig.json: rc=0, no output.Verification method
executedin the Linux container: Node v24.19.0, yarn 1.22.22 (yarn install --frozen-lockfile --ignore-scripts), jest 26 viapackages/elements-coreconfig, curl 8.22.0 for the wire check. No platform-specific code involved. Fork CI: Actions are not yet enabled on sprayberry-code/elements (operator card filed), so no fork run exists at this head;gh pr checksshows none.Prior art
gh pr list --repo stoplightio/elements --search "2744 in:body" --state all(scout):[]gh search prs --repo stoplightio/elements "octet-stream":[]gh search prs --repo stoplightio/elements "binary request sample":[]gh search prs --repo stoplightio/elements "data-binary": chore(deps-dev): Bump webpack from 5.76.1 to 5.94.0 stoplightio/elements#2682, chore(deps): Bump sharp, gatsby, gatsby-plugin-image, gatsby-plugin-manifest, gatsby-plugin-sharp and gatsby-transformer-sharp in /examples/react-gatsby stoplightio/elements#2620, feat(try-it): add base64 support to file uploads stoplightio/elements#807, Upgrade css-loader: 6.2.0 → 6.7.1 (minor) stoplightio/elements#2130, fix: file upload stoplightio/elements#1715, none about request samples.gh search prs --repo stoplightio/elements "2744": feat: prettify try it json response stoplightio/elements#1099 (unrelated, 2021).gh search issues ... "octet-stream": Incorrect Request Sample for application/octet-stream Requests stoplightio/elements#2744 (this), Unable to upload file stoplightio/elements#2615 (closed, "Unable to upload file")."data-binary": Incorrect Request Sample for application/octet-stream Requests stoplightio/elements#2744, Uploading a file is no longer possible with multipart/form-data stoplightio/elements#2263, Unable to upload file stoplightio/elements#2615, File upload Content-Type header is missing boundary parameter stoplightio/elements#1751, Send Request: File picker doesn't show up for a multipart/form-data request with a binary format field stoplightio/elements#1649, Try It: Support Form Data request bodies stoplightio/elements#695, none about the cURL sample flag.build-request*,RequestSamples/*,BinaryBody*,request-body-utils*: fix: Stoplight vulnerability: Stoplight portal leads to Browser Based… stoplightio/elements#2884, Netlify issue stoplightio/elements#2822, fix: added comment stoplightio/elements#2821, STOP-1246 security fix changes stoplightio/elements#2806, fix: allow delete to have body stoplightio/elements#2785, Allow TryIt to send request body for GET and DELETE requests, fixes #2230 stoplightio/elements#2777, STOP-1246 security fix changes stoplightio/elements#2756, Stoplight vulnerability -STOP1246 stoplightio/elements#2754, fix: Content-Type header added despite no request body defined in POST/PUT/PATCH endpoints stoplightio/elements#2739. Only fix: Content-Type header added despite no request body defined in POST/PUT/PATCH endpoints stoplightio/elements#2739 mentions octet-stream, in TryIt tests for the Content-Type header (Content-Typeheader added despite no request body defined in POST/PUT/PATCH endpoints stoplightio/elements#2734); none touchesconvertRequestToSample.git log origin/main --grep 2744and-S"binary: true"overpackages/elements-core/srcfind nothing relevant; main moved by one commit (prismjs bump) touching none of the files.Policy
CONTRIBUTING.md@ main (2024fd9): no mention of AI, LLMs, generated code or agents. Applicable lines, verbatim:__tests__close to the component being tested".github/pull_request_template.md@ main: "- [ ] ReadCONTRIBUTING.md"CODE_OF_CONDUCT.md: links the Stoplight community code of conduct; nothing on AI.AGENTS.md,CLAUDE.md,AI_POLICY.md,.github/AI_POLICY.md,AI.md,AGENT_POLICY.md,.github/CONTRIBUTING.md,.changeset/config.json. No CLA, no DCO.fix:commits on main also bump the three packages'package.jsonversions (e.g. 09edf7f, fe9e657). This candidate does not; add a patch bump only if a maintainer asks.Disclosure facts for the operator
convertRequestToSample.ts, and confirmed it by running the real function on base and head.Boundaries
postData.mimeType === 'application/octet-stream', text@some-file.bin(file picked in Try It)--data-binary @some-file.binshould send the file unmodified with --data-binary in curl(fails on base)application/json(any non-octet-stream body)undefined, output unchangedgiven shell / curlpostDataundefined (no file picked, or GET)?.gives undefined, optionsundefined, unchangedpostData: undefined) pass both arms; probenoBodyidentical on base and head for all 34 targets@)--data-binary 'raw text'; for a literal argument curl sends the same bytes with either flagoctetText); not produced by Try It, whose binary body is a File or undefined (TryIt.tsx:150,174)''octetEmptyTextidentical base/head; not produced bybuildHarRequest(File or undefined)Application/Octet-Streamundefined, unchanged@filebody:buildHarRequest:299also compares exactly and emitsparams: []instead of@filefor this case (pre-existing, separate). ProbeoctetUpperidentical base/headapplication/octet-stream; foo=barundefined, unchangedisBinaryContent(request-body-utils.ts:29) does not treat it as binary, so no file input and no@filebody. ProbeoctetParamsidenticaldiff probe-base.txt probe-head.txtchanges exactly 2 lines, both shell/curl. These targets still print the literal@some-file.binstring as the body (pre-existing, not changed by this PR; the rest of stoplightio#2744)application/x-www-form-urlencoded/multipart/form-dataundefined; urlencoded--data-urlencoderewrite and multipart--formunchangedurlencoded,multipartidentical base/headSuggested upstream PR title
fix: use --data-binary in cURL request sample for application/octet-stream bodies