fiat-html: make the argv string encoding a faithful round trip (scrutineer #2519) - #2406
Draft
JasonGross wants to merge 2 commits into
Draft
fiat-html: make the argv string encoding a faithful round trip (scrutineer #2519)#2406JasonGross wants to merge 2 commits into
JasonGross wants to merge 2 commits into
Conversation
Fixes scrutineer finding #2519. In fiat-html/main.js, splitUnescapedSpaces and joinWithEscaping convert between the "Input String" text box and the argv array passed to the worker. The decoder used U+0000 and U+0001 as internal placeholders and dropped empty strings, so `splitUnescapedSpaces(joinWithEscaping(a))` was not `a` for arguments containing those characters or for empty arguments. When the page was opened via a crafted `?argv=...&interactive` link, the text box was filled with the lossy re-encoding while synthesis ran on the original argv, so the visitor saw one command line and ran another. Rewrite the decoder as a character-by-character parser with no placeholder characters. `\ ` and `\\` keep their meaning, `\"` is a literal quote, and a bare `""` token is the empty argument; any other backslash is kept literally, as before. The encoder emits `""` for an empty argument and escapes the quotes of an argument that is literally `""`. With the pair an exact inverse, the displayed string always decodes to the argv that ran. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Rbn2gww3MGhvrh52fNjpD
JasonGross
force-pushed
the
fable/fix-2519-argv-roundtrip
branch
from
September 2, 2026 03:37
2a3e519 to
1dde156
Compare
…a JSON-view link (scrutineer #2519 follow-up) (#2416) Follow-up to scrutineer finding #2519. When the page is opened via `?argv=...&inputType=json&interactive`, the text box is first filled with the decoded argv (JSON text) and then `updateInputType('json')` runs `splitUnescapedSpaces` over that JSON text and re-stringifies the result, so the box shows a single mangled argument such as `["[\"a\",\"\",\"b\"]"]` while the worker ran `["a","","b"]`. Since the box already holds argv as JSON at that point, only the string view needs converting; for the JSON view just validate the input. Claude-Session: https://claude.ai/code/session_016Rbn2gww3MGhvrh52fNjpD Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.
Fixes scrutineer finding #2519 (Low, CWE-116): the argv string<->array encode/decode pair in
fiat-html/main.jswas not a faithful inverse, so a crafted?argv=...&interactivelink could display one command line in the text box while synthesising another.The bug
splitUnescapedSpacesdecoded the "Input String" text box by substituting U+0000/U+0001 as placeholders for\\and\and then dropping empty tokens;joinWithEscapingescaped neither placeholder. So["a<U+0000>b"]re-decoded as["a\\b"],["a<U+0001>b"]as["a b"], and["a","","b"]as["a","b"]. On load from a?argv=link the text box is filled withjoinWithEscaping(argv)while synthesis runs on the originalargv, so the box and the executed command line diverged.The fix
Only the two functions change, in place (
fiat-html/main.js, +27/-10):splitUnescapedSpacesis now a character-by-character parser with no placeholder characters. Unescaped spaces separate arguments;\,\\and\"are a literal space, backslash and double quote; a bare""token (delimited by spaces or the ends of the string) is the empty argument, which is the only new syntax. Any other backslash is kept literally, as before, so hand-typed input such asC:\pathis unchanged.joinWithEscapingemits""for an empty argument and escapes the quotes of an argument that is literally""; quotes are otherwise left alone.Since the pair is now an exact inverse, whatever the box shows decodes to exactly the argv that ran, whether the page was opened from a link or the arguments were typed by hand, and toggling between the string and JSON views never changes the arguments. No plumbing changes.
Verification (scratchpad only, nothing committed)
main.jsby slicing the source and exercises them with hand-picked adversarial cases (empty args, U+0000/U+0001, backslashes, quotes, leading/trailing/multiple spaces, surrogate pairs), 20000 random arrays over[' ', '\\', '"', 'a', 'b', U+0000, U+0001], 20000 random legacy-only strings compared against the old decoder, and string->JSON->string stability: 8/8 pass on this branch, 6/8 fail on master.fiat-crypto.html+file-input.js+main.jswith a stubbedWorker: for?argv=...&interactivelinks with an empty argument, a U+0000 in an argument, the README p256 link and a literal""argument, the worker receives the URL argv and pressing Synthesize on the pre-filled box sends the same argv. On master the empty-argument case showsa band would run["a","b"]while the worker ran["a","","b"], reproducing the finding. Typingx "" y\ z, toggling to the JSON view and back, synthesising, and reloading the permalink all give["x","","y z"].node --check fiat-html/main.jsis clean.Possible follow-ups (deliberately not included here)
&inputType=jsonin the URL,updateInputType('json')runs the string decoder over the JSON text already in the box, so the box shows["[\"a\",\"\",\"b\"]"]while the worker ran["a","","b"]. This is the same display-vs-run divergence for the JSON view; the fix is to fill the box from the decoded array rather than the raw text.updatePermalinkemits&inputType=&inputType=json, so a permalink from the JSON view always reopens in the string view.?inputType=is interpolated into a CSS selector; a value likex"]throws and leaves the page blank.🤖 Generated with Claude Code
https://claude.ai/code/session_016Rbn2gww3MGhvrh52fNjpD