JUnit report and GitHub Actions annotation: print a frame source that is not a file path as-is - #39828
Conversation
… report and the GitHub Actions annotation The JUnit reporter and the GitHub Actions annotation passed every frame's source URL through resolve_path::relative. That function normalizes both operands in fixed path buffers. A source URL that is not a path (a data: URL, a //# sourceURL= name) can be longer than a path buffer, and then bun aborted with "panic: range end index N out of range for slice of length 4096" while it reported the error. A short non-path URL was mangled instead (webpack://app/./x.ts printed as webpack:/app/x.ts). ZigStackFrame::relative_source_url makes only an absolute path that fits a path buffer relative to the report directory. Every other source URL is printed as-is, as the plain error printer prints it.
|
Warning Review limit reachedYour included review limit has been reached. You’re in a promotional period — use the checkbox below to run this review for free:
On-demand reviews are free for the next 31 days. After that, they cost $0.25 per reviewed file. How can I continue?Run this review now using the option above, or comment You can also wait for the limit to reset (next review available in 2 minutes), then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (6)
Comment |
|
Updated 5:17 PM PT - Aug 20th, 2026
❌ @robobun, your commit f2ec311 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 39828That installs a local version of the PR into your bun-39828 --bun |
|
Status: merged. A follow-up (#39870) that resolved relative source names (a How I reproduced the bug, on the released 1.4.0 build (Linux x64):
|
There was a problem hiding this comment.
LGTM — a small, well-scoped guard that stops resolve_path::relative panicking on non-path frame sources in the JUnit reporter and GitHub Actions annotation.
What was reviewed:
relative_source_urlgates onis_absolute+MAX_PATH_BYTES; checked against POSIX and Windowsis_absolute—data:,webpack://,node:fsall fall through to as-is on both.- Return lifetime:
relativeyields&'static(thread-local buffer) which coerces to&'a; each of the three call sites consumesfilebefore the nextrelativecall in the same loop iteration. - Tests cover all three sites (JUnit
record_failure, bothprint_github_annotationpaths, andbun runviastack.test.ts) plus the positive case that real absolute paths still relativize.
Extended reasoning...
Overview
Adds ZigStackFrame::relative_source_url (src/jsc/ZigStackFrame.rs) — a 5-line guard around resolve_path::relative that only relativizes when the input is an absolute path shorter than MAX_PATH_BYTES, returning the source URL unchanged otherwise. Three existing call sites in print_github_annotation (src/jsc/VirtualMachine.rs:6621, :6683) and JunitReporter::record_failure (src/runtime/cli/test_command.rs:386) switch to it. Four new tests across three files exercise the crash (long data: URL, long //# sourceURL=), the mangling (webpack:// URL), and the still-works case (absolute path).
Security risks
None. This is diagnostic output formatting for test/CI reports. No parsing of untrusted structured data, no auth, no filesystem writes beyond what the reporters already did. The change is strictly more defensive than before (turns unbounded input away from a fixed-buffer routine).
Level of scrutiny
Low-to-moderate. The fixed change is a panic in report formatting — user-visible but not runtime-semantic. The Rust change is 15 net lines with a single associated function reused at three sites, following the "fix the whole class in the same PR" guideline. relative's &'static return coerces cleanly into the helper's &'a signature; all three call sites consume the borrowed slice within the same loop iteration before any subsequent relative call, so the thread-local buffer isn't clobbered. On Windows, is_absolute correctly rejects data:, webpack://, etc. (checked against is_absolute_windows_t).
Other factors
The PR description is unusually thorough — it names the exact panic, the mechanism, what's intentionally not covered (the near-MAX_PATH_BYTES ../-chain overflow that #39658/#38392/#38696 address in resolve_path itself), and confirms the new tests fail on the released build. Tests use Buffer.alloc per repo convention, drain pipes concurrently, and pin both the crash cases and the positive contract (real file paths still relativized, GITHUB_WORKSPACE still respected). No prior human review comments to address. The one dev-server-only edge (source_url_formatter's root_path prefix-strip when origin is set) is explicitly acknowledged in the description and is not made worse than before.
Problem
data:URL module aborts the JUnit reporter and theGITHUB_ACTIONSannotation:panic: range end index 6103 out of range for slice of length 4096. A long//# sourceURL=name does the same.record_failure(src/runtime/cli/test_command.rs:386) andprint_github_annotation(src/jsc/VirtualMachine.rs:6621,:6683) callresolve_path::relativeon the source URL of every frame.relativewrites into fixed path buffers with no length check. A source URL is not always a path.webpack://app/./x.tsprints aswebpack:/app/x.ts.Fix
ZigStackFrame::relative_source_urlcallsrelativeonly for an absolute path shorter thanMAX_PATH_BYTES, and returns any other source URL unchanged. The three sites use it.resolve_path.rsis not changed. paths: bounds-check path normalization and spill thread-local results to the heap #39658, paths: heap-backed relative_alloc and join_abs_string_buf_spill; use them in _nodeModulePaths and the runtime linker #38392 and bundler: fix panic relativizing a source path close to MAX_PATH_BYTES #38696 each add a length-saferelativethere.test/js/junit-reporter/junit.test.js,test/cli/test/bun-test.test.tsandtest/js/bun/test/stack.test.ts. They fail on the released build. The three files pass in full.Background
source_urlof a frame is what JSC recorded for its code. For a file module it is the absolute path. For adata:module it is the whole URL. For eval'd code it is the//# sourceURL=name.resolve_path::relative(from, to)normalizes both arguments into thread-localPathBuffers and builds the../form in a third. It first joins atothat is not absolute onto the cwd.bun testin GitHub Actions hits this too.Notes
throw.mjswithawait import("data:text/javascript," + encodeURIComponent("throw new Error('boom');" + "//".padEnd(6000, "x"))).GITHUB_ACTIONS=true bun throw.mjsprintserror: boom, then panics withrange end index 6055, exit 134. The same module imported from a test, run withbun test --reporter=junit --reporter-outfile=out.xml, panics withrange end index 6103and writes no report.GITHUB_ACTIONS=true bun teston that test panics too.//# sourceURL=/followed by 100000 bytes panics withrange end index 100000 out of range for slice of length 4095(the absolute branch ofrelative).relativenormalizes the URL (//to/,.segments dropped), joins it onto the cwd and relativizes it againstdir. WithGITHUB_WORKSPACEoutside the cwd it also gets a../prefix.test/js/bun/test/err-custom-fixture.jsshows the shape on an error object with anhttp:sourceURL: the released build printsfile=http%3A/example.com/test.js, this change printsfile=http%3A//example.com/test.js. A remapped frame whose sourcemapsourcesentry is awebpack://URL is the same case.file=property for a non-path top frame keeps the content it has today for a short URL (file=data%3Atext/javascript...). Which frame the annotation points at is GitHub Actions annotation and code frame caret: use the first frame that has a file, not a builtin frame on top #38335. That PR edits the same header block, so one of the two gets a small textual conflict. The frame list of the annotation already printed the raw URL throughsource_url_formatter. It usesfileonly for the empty check and, on the dev server, as the prefix to strip.MAX_PATH_BYTEScan still overflow insiderelativewhen the../chain fordirdoes not fit next to it. Every caller ofrelativehas that defect (bundler: fix panic relativizing a source path close to MAX_PATH_BYTES #38696 describes it) and paths: bounds-check path normalization and spill thread-local results to the heap #39658 and paths: heap-backed relative_alloc and join_abs_string_buf_spill; use them in _nodeModulePaths and the runtime linker #38392 fix it. It needs a path within about3 * depth(dir)bytes of the limit. The length check here only turns the unbounded input (a URL or asourceURLname of any length) away, and becomes redundant once one of those PRs lands.print_error_instance_bodyruns the JUnitrecord_failurecallback and, underGITHUB_ACTIONS,print_github_annotationfor every error bun prints. Sobun testin GitHub Actions hits this without--reporter=junit, and so doesbun run. The buffers areMAX_PATH_BYTESlong: 4096 on Linux, 1024 on macOS, 98302 on Windows.sourceURLare still relativized. TheGITHUB_WORKSPACEtest pins that a test file is reported relative to the workspace and that awebpack://URL is not. On the released build the tests fail with exit 134 and with the mangled URL.cargo fmt --all -- --check, prettier on the three test files,test/internal/source-lints,bun bd testonjunit.test.js(9 pass),stack.test.ts(7 pass, 1 todo) andbun-test.test.ts(97 pass, 6 todo). Clippy was not run locally.