Stop .preflight lines escaping to other binaries - #220
Conversation
The file format's contract is that every line runs preflight. ParseFile
enforced it with HasPrefix, so any token merely starting with "preflight"
counted as already-prefixed — while cmd_run's substitution tested for the
exact token and so did not fire. A line reading
preflight/../evil.sh
satisfied the first check, skipped the second, and because the token
contains a slash exec.Command ran it relative to the working directory
without consulting PATH. Reproduced: arbitrary code executed, exit 0.
That makes `git clone && cd repo && preflight run` remote code execution
from repo content, since FindFile discovers .preflight by walking up from
the working directory.
ParseFile now compares the first token rather than the prefix, and cmd_run
substitutes the resolved preflight binary unconditionally, so a future
parser change cannot reintroduce a path here.
|
Warning Review limit reached
Next review available in: 42 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (50.00%) is below the target coverage (70.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #220 +/- ##
==========================================
+ Coverage 93.19% 93.24% +0.04%
==========================================
Files 50 50
Lines 1865 1864 -1
==========================================
Hits 1738 1738
+ Misses 92 91 -1
Partials 35 35
🚀 New features to boost your workflow:
|
A
.preflightfile could execute an arbitrary binary, contradicting the documented contract that every line runspreflight.The mismatch
Two checks disagreed about what "already prefixed" means:
preflightfile.go:75strings.HasPrefix(trimmed, "preflight")→ don't prependcmd_run.go:56parts[0] == "preflight"→ substitute the real binaryA token that starts with
preflightbut isn't equal to it satisfies the first and escapes the second. And because the token contains a slash,exec.Commandskips$PATHand runs it relative to the working directory:FindFilediscovers.preflightby walking up from the working directory, sogit clone && cd repo && preflight run— or a CI step that does the same — is remote code execution from repo content.Fix
ParseFilecompares the first token rather than the prefix, sopreflight/../evil.shbecomespreflight preflight/../evil.shand resolves as an unknown subcommand.cmd_runsubstitutes the resolved preflight binary unconditionally.ParseFilenow guarantees the token, so this always fired anyway — making it unconditional means a future parser change can't turn a line back into a path to some other program.After:
exit=1, andPWNEDnever appears. Normal files (comments, bare commands, explicitly-prefixed lines) are unchanged.Tests
Table written first, confirmed red on the escape cases: paths starting with
preflight, lookalike tokens likepreflightfoo, and absolute/relative paths all now get prefixed rather than executed.Related, not fixed here
The residual error from the exploit is
unknown command "echo", notunknown command "preflight/../evil.sh"— becausetransformArgsForHashbangsaw an existing file as the first argument and rewrote the invocation torun --file, then parsed the payload script as a preflight file. Harmless now that lines can only invoke preflight, but it confirms the separate finding that any existing file passed as the first argument is treated as a preflight script, with no executable-bit or shebang check. That's theknownSubcommandsissue — next PR.