diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-16 23:11:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-16 23:11:32 +0300 |
| commit | ca5bcf37e40d4dbd41dc9d9d001d6f4b9c6dcd65 (patch) | |
| tree | 9deb2da9693d422ea01995e216862239194b4fd7 /tests/cli.sh | |
| parent | 72f72995a9c4a1ddd2ea3adb04cc2d7641410245 (diff) | |
8n0 validate DIST_DIR before --clean rm -rf
The --clean action ran `rm -rf "$DIST_DIR"` after only an `[ -d ]` check,
so a misconfigured DIST_DIR (empty, /, $HOME, system dirs, etc.) could
recursively delete the wrong tree.
Add validate_clean_dist_dir (and resolve_dist_dir_path) in
config.validate.source.sh and call it in the --clean case before any
deletion. The guard canonicalizes DIST_DIR with `pwd -P` (handling ./
trailing slashes, symlinks and relative paths; for a not-yet-existing
dir it resolves the existing parent and re-attaches the basename) and
refuses to clean when the resolved path is empty, the filesystem root, a
well-known system directory, the resolved $HOME, or the current working
directory. Rejection uses config_error with a clear message and a
non-zero exit, so nothing is deleted. Normal DIST_DIRs still clean.
Tests (tests/cli.sh, registered in main): a HOME-as-DIST_DIR case (uses
a fake HOME under TEST_TMPDIR with a sentinel file, so a regression can
only touch the throwaway temp dir) and an empty-DIST_DIR case both assert
rejection and that nothing is removed.
Note: leftover staging artifacts on --clean are out of scope (task ln0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'tests/cli.sh')
| -rwxr-xr-x | tests/cli.sh | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/tests/cli.sh b/tests/cli.sh index 705ee52..859d104 100755 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -305,6 +305,54 @@ test_clean_cli_dist_overrides_config() { test::teardown } +# --clean must refuse to delete a DIST_DIR that resolves to a dangerous path +# (here: the user's HOME). We point HOME at a directory we fully control under +# TEST_TMPDIR and seed it with a sentinel file, so a regression can only "delete" +# our throwaway temp dir -- never a real HOME -- and we assert nothing was +# removed and the command failed with a clear error. +test_clean_rejects_dangerous_dist_dir() { + local config_file + local fake_home + local output + + test::setup + fake_home="$TEST_TMPDIR/fake-home" + mkdir -p "$fake_home" + touch "$fake_home/sentinel" + config_file="$TEST_TMPDIR/shuriken.conf" + printf 'DIST_DIR=%q\n' "$fake_home" > "$config_file" + + output=$( + cd "$TEST_TMPDIR" + HOME="$fake_home" \ + test::capture_failure_output "$TEST_SHURIKEN" --clean + ) + + test::assert_contains 'refusing to clean DIST_DIR' "$output" + test::assert_contains 'is HOME' "$output" + test::assert_dir_exists "$fake_home" + test::assert_file_exists "$fake_home/sentinel" + test::teardown +} + +# --clean must also refuse an empty DIST_DIR (which would otherwise rm -rf ""). +test_clean_rejects_empty_dist_dir() { + local config_file + local output + + test::setup + config_file="$TEST_TMPDIR/shuriken.conf" + printf "DIST_DIR=''\n" > "$config_file" + + output=$( + cd "$TEST_TMPDIR" + test::capture_failure_output "$TEST_SHURIKEN" --clean + ) + + test::assert_contains 'DIST_DIR must be set' "$output" + test::teardown +} + test_missing_config_fails_without_legacy_fallbacks() { local default_rc local home_dir @@ -5685,6 +5733,10 @@ main() { test::run_case '--clean --config succeeds' test_clean_with_config test::run_case '--clean --dist overrides config' \ test_clean_cli_dist_overrides_config + test::run_case '--clean rejects dangerous DIST_DIR' \ + test_clean_rejects_dangerous_dist_dir + test::run_case '--clean rejects empty DIST_DIR' \ + test_clean_rejects_empty_dist_dir test::run_case \ 'missing config ignores legacy fallbacks' \ test_missing_config_fails_without_legacy_fallbacks |
