#77 build: -o guard bypass via .. / symlink deletes parent or repo (data loss)
What
prepare_out protects the repo with a lexical path check:
src/build.rs:1144—absolute(out)only joins cwd (./..are left in).- guard:
out_abs == repo_abs || repo_abs.starts_with(&out_abs).
Path::starts_with normalizes . but not .., so <repo>/.. passes the guard and remove_dir_all resolves through .. and deletes the parent directory.
Reproduced
mkdir -p /tmp/prep/repo && cd /tmp/prep/repo && git init -b main
echo keep > README.md && git add -A && git commit -m one
echo CANARY > /tmp/prep/keep.txt
hu13 build . -o ..
Observed: /tmp/prep/keep.txt, the repo's .git and all sources were deleted; repo/ was recreated empty and the build failed with Unable to read current working directory. A symlinked ancestor (-o /tmp/alias/src where alias -> repo) deletes repo contents the same way.
Impact
Data loss: a plausible -o .. typo or a bad build command wipes the checkout and its parent. The #67 guard explicitly claims to refuse ancestors.
Fix
- Lexically normalize
out(resolve./..) and canonicalize the deepest existing ancestor before any comparison. - Compare resolved paths; refuse when resolved
outequals the repo or contains it. - Also refuse an in-repo
outthat contains tracked files (src/,.forge/) while keepingdist/legal; or require it to be empty/gitignored. - Tests: absolute parent, relative
..,../.., symlinked ancestor; assert a canary outside the output survives.
Comments (0)
No comments yet.