Skip to content

Stop .preflight lines escaping to other binaries - #220

Merged
vertti merged 2 commits into
mainfrom
fix-preflight-file-escape
Aug 12, 2026
Merged

Stop .preflight lines escaping to other binaries#220
vertti merged 2 commits into
mainfrom
fix-preflight-file-escape

Conversation

@vertti

@vertti vertti commented Aug 11, 2026

Copy link
Copy Markdown
Owner

A .preflight file could execute an arbitrary binary, contradicting the documented contract that every line runs preflight.

The mismatch

Two checks disagreed about what "already prefixed" means:

Location Test
preflightfile.go:75 strings.HasPrefix(trimmed, "preflight") → don't prepend
cmd_run.go:56 parts[0] == "preflight" → substitute the real binary

A token that starts with preflight but isn't equal to it satisfies the first and escapes the second. And because the token contains a slash, exec.Command skips $PATH and runs it relative to the working directory:

.preflight:
    env HOME
    preflight/../evil.sh

$ preflight run
[OK] env: HOME
PWNED: arbitrary code executed

FindFile discovers .preflight by walking up from the working directory, so git clone && cd repo && preflight run — or a CI step that does the same — is remote code execution from repo content.

Fix

  • ParseFile compares the first token rather than the prefix, so preflight/../evil.sh becomes preflight preflight/../evil.sh and resolves as an unknown subcommand.
  • cmd_run substitutes the resolved preflight binary unconditionally. ParseFile now 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, and PWNED never 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 like preflightfoo, 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", not unknown command "preflight/../evil.sh" — because transformArgsForHashbang saw an existing file as the first argument and rewrote the invocation to run --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 the knownSubcommands issue — next PR.

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.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@vertti, you've reached your PR review limit, so we couldn't start this review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d68c3093-5eab-4d9f-b27d-9739fba28b10

📥 Commits

Reviewing files that changed from the base of the PR and between b9cdca9 and 63ebb50.

📒 Files selected for processing (3)
  • cmd/preflight/cmd_run.go
  • pkg/preflightfile/preflightfile.go
  • pkg/preflightfile/preflightfile_test.go

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.24%. Comparing base (b9cdca9) to head (63ebb50).

Files with missing lines Patch % Lines
cmd/preflight/cmd_run.go 0.00% 1 Missing ⚠️

❌ 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              
Files with missing lines Coverage Δ
pkg/preflightfile/preflightfile.go 90.24% <100.00%> (ø)
cmd/preflight/cmd_run.go 25.00% <0.00%> (+0.75%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vertti
vertti merged commit 8045a41 into main Aug 12, 2026
17 of 19 checks passed
@vertti
vertti deleted the fix-preflight-file-escape branch August 12, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant