diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/action.source.sh | 10 | ||||
| -rw-r--r-- | src/lib/config.validate.source.sh | 84 |
2 files changed, 94 insertions, 0 deletions
diff --git a/src/lib/action.source.sh b/src/lib/action.source.sh index 12be053..edececa 100644 --- a/src/lib/action.source.sh +++ b/src/lib/action.source.sh @@ -188,6 +188,16 @@ run_configured_action() { case "$SHURIKEN_CLI_ACTION" in --clean) + # Validate DIST_DIR before any destructive rm -rf. Unlike the bare + # check that used to live here, validate_clean_dist_dir rejects unset, + # empty, and dangerous paths (/, HOME, cwd, system dirs) so a + # misconfigured DIST_DIR can never nuke the wrong tree. + validate_clean_dist_dir + status=$? + if (( status != 0 )); then + return "$status" + fi + if [ -d "$DIST_DIR" ]; then log_info "Cleaning $DIST_DIR" rm -rf "$DIST_DIR" diff --git a/src/lib/config.validate.source.sh b/src/lib/config.validate.source.sh index 7519094..28aeace 100644 --- a/src/lib/config.validate.source.sh +++ b/src/lib/config.validate.source.sh @@ -74,6 +74,90 @@ validate_dist_dir() { fi } +# Resolve DIST_DIR to a canonical absolute path so the --clean guard cannot be +# bypassed via "." / trailing slashes / symlinks / relative paths. The directory +# itself may not exist yet (cleaning a stale config), so when it is missing we +# resolve its existing parent and append the basename. Output is printed; the +# caller captures it. Returns non-zero only if even the parent cannot resolve. +resolve_dist_dir_path() { + local -r target_dir="$1"; shift + local parent base + + if [ -d "$target_dir" ]; then + ( cd "$target_dir" && pwd -P ) && return + return 1 + fi + + # DIST_DIR does not exist: canonicalize the deepest existing ancestor and + # re-attach the remaining path so symlinked parents are still resolved. + parent=$(existing_parent_dir "$target_dir") + base=${target_dir#"$parent"} + base=${base#/} + parent=$( cd "$parent" && pwd -P ) || return 1 + if [ -n "$base" ]; then + printf '%s/%s\n' "$parent" "$base" + else + printf '%s\n' "$parent" + fi +} + +# Guard for the destructive --clean action: refuse to "rm -rf" DIST_DIR when it +# resolves to an empty value or a clearly dangerous location (filesystem root, +# the user's HOME, the current working directory, or a well-known system tree). +# This is the single place where the dangerous-path policy lives so it stays in +# sync. NOTE (ln0): this only guards against deleting the wrong tree; leftover +# staging artifacts are a separate concern handled by task ln0. +validate_clean_dist_dir() { + local resolved home_resolved cwd_resolved + local -a forbidden=( + / /home /root /tmp /usr /etc /var /bin /sbin /lib /boot /opt + ) + local entry + + require_config_var DIST_DIR || return + + # Reuse the shared DIST_DIR sanity checks (must be a directory, writable, + # parent writable) before applying the destructive-path policy. + validate_dist_dir || return + + resolved=$(resolve_dist_dir_path "$DIST_DIR") || { + config_error "DIST_DIR $DIST_DIR could not be resolved" + return 1 + } + + if [ -z "$resolved" ]; then + config_error 'refusing to clean an empty DIST_DIR' + return 1 + fi + + # Reject the filesystem root and well-known system directories outright. + for entry in "${forbidden[@]}"; do + if [ "$resolved" = "$entry" ]; then + config_error \ + "refusing to clean DIST_DIR $resolved (dangerous path)" + return 1 + fi + done + + # Reject HOME and the current working directory themselves (deleting either + # would be catastrophic and is never the intended DIST_DIR). + if [ -n "${HOME:-}" ]; then + home_resolved=$( cd "$HOME" 2>/dev/null && pwd -P ) || home_resolved='' + if [ -n "$home_resolved" ] && [ "$resolved" = "$home_resolved" ]; then + config_error \ + "refusing to clean DIST_DIR $resolved (is HOME)" + return 1 + fi + fi + + cwd_resolved=$(pwd -P) + if [ "$resolved" = "$cwd_resolved" ]; then + config_error \ + "refusing to clean DIST_DIR $resolved (is current directory)" + return 1 + fi +} + validate_template_dir_access() { if [[ ! -d "$TEMPLATE_DIR" || ! -r "$TEMPLATE_DIR" \ || ! -x "$TEMPLATE_DIR" ]]; then |
