release: v0.4.13 — routes that fabricated their answers - #193
Merged
Conversation
Seven HTTP routes returned results they never obtained from the browser.
Each answered 200 with a well-formed body, so no caller could tell
"Chrome reported nothing" apart from "kuri never asked Chrome".
/react/tree said {"react":false} on every React page. React double-buffers
fibers and __reactContainer$ is stamped at mount, so after the first commit
it addresses the off-screen side of the pair, whose .child is null —
detection concluded there was no React at all. It now follows whichever side
actually rendered. /trace/stop never asked Tracing.start for
transferMode: ReturnAsStream, so there was no stream to drain and the route
reported success while writing nothing. /network returned a canned
{"status":"ok"}, /mainframe the same hard-coded string to every caller,
/screencast/stop a canned ok, /expose registered its binding but captured no
invocation, and /console and /errors had regressed to empty.
/evaluate and /evalhandle escaped their expression twice. A newline became
\n and then \\n, which decodes back to a literal backslash-n in the JS
source, so Chrome rejected every multi-line or quote-containing expression
with SyntaxError — the two routes whose whole purpose is running arbitrary
JS could not run any non-trivial JS.
stealth.js was a top-level script with a const in it, injected twice by
design (addScriptToEvaluateOnNewDocument plus a one-shot evaluate into the
loaded page). Re-running it in the same global re-declared the const; the
SyntaxError aborted the entire re-injection at parse time and surfaced in
the page's own error stream, so /errors reported a kuri-internal failure as
if it were the page's. Now an IIFE behind an idempotence guard.
Two memory bugs: the event rings handed out pointers into storage the next
wrapping event would overwrite, and extractObject/extractField scanned for
a closing brace or quote without honouring \" — truncating any string value
with an escaped quote and ending early on unbalanced literal braces, such as
a GraphQL query sent over GET.
Route dispatch was a linear chain of 148 std.mem.eql comparisons, so cost
depended on position in the chain and every unknown path paid all 148. It is
now a comptime StaticStringMap feeding an exhaustive switch: ~81ns to ~6ns
average, ~150ns to ~15ns worst case, with a permanent bench guard over all
148 routes. CdpClient is 62 KiB per tab rather than 520 KiB: ws_read_buf was
512 KiB of which ~97% was unreachable headroom (message bodies allocate
their own buffer), and ws_write_buf had no readers anywhere.
378 tests in the main suite, plus 76 for kuri-fetch and 22 for kuri-browse.
Every fix above has a regression test, and the dispatch rewrite was checked
live against Chrome for intercept, dialog, storage and clipboard behaviour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017yzwAh6c95Ygb4LYttdVCs
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.
Seven HTTP routes returned results they never obtained from the browser. Each answered
200with a well-formed body, so no caller could tell "Chrome reported nothing" apart from "kuri never asked Chrome".Routes that fabricated their answers
/react/tree{"react":false}on every React pageFunctionComponent,Context.Consumer,HostComponent, depths/trace/stop/expose/network{"status":"ok"}/mainframeframeId,loaderId,url/screencast/stopframe_count/console,/errors/react/treeis the interesting one. React double-buffers fibers, and__reactContainer$…is stamped at mount, so after the first commit it addresses the off-screen side of the pair, whose.childisnull— detection concluded there was no React at all. Confirmed on React 19.2.5:hasChild:false,altHasChild:true. It now follows whichever side actually rendered.Script injection
/evaluateand/evalhandleescaped their expression twice. A newline became\n, then\\n, which decodes back to a literal backslash-n in the JS source — Chrome rejected every multi-line or quote-containing expression withSyntaxError. The two routes whose whole purpose is running arbitrary JS could not run any non-trivial JS.stealth.jspolluted every page's error stream. A top-levelconstin a file injected twice by design (addScriptToEvaluateOnNewDocumentplus a one-shot evaluate into the loaded page). Re-running it in the same global re-declared the const; theSyntaxErroraborted the whole re-injection at parse time and surfaced in the page's own error stream, so/errorsreported a kuri-internal failure as if it were the page's. Now an IIFE behind an idempotence guard.Memory safety
extractObject/extractFieldscanned for a closing brace or quote without honouring\", truncating any string value with an escaped quote and ending early on unbalanced literal braces (a GraphQL query sent over GET, for instance).Performance
std.mem.eqlcomparisons — cost depended on position, and every unknown path paid all 148 — replaced by a comptimeStaticStringMapfeeding an exhaustive switch. ~81ns → ~6ns average, ~150ns → ~15ns worst case.zig build benchcarries a permanent guard over all 148 routes.CdpClient520 KiB → 62 KiB per tab (−88%).ws_read_bufwas 512 KiB of which ~97% was unreachable headroom (message bodies allocate their own buffer);ws_write_bufhad no readers anywhere.Tests
378 in the main suite, 76 for
kuri-fetch, 22 forkuri-browse— all passing, with a regression test per fix. CleanReleaseFastbuild. The dispatch rewrite was checked live against Chrome (storage/local→LOCAL_VALvsstorage/session→SESSION_VAL, clipboard read ≠ write, unknown path still 404s), which was its catastrophic-if-wrong failure mode, plus intercept and dialog round-trips.Note on macOS assets
This repository has no GitHub Actions secrets, so the workflow's sign-and-notarize step takes its
signed=falsebranch and CI publishes unsigned macOS binaries. The macOS tarballs for this release are signed withDeveloper ID Application: Rachit Pradhan (WWP9DLJ27P)and notarized locally via thecodedb-notaryprofile, then uploaded over CI's.🤖 Generated with Claude Code
https://claude.ai/code/session_017yzwAh6c95Ygb4LYttdVCs