verifyfirst

A command's return status · empty-argument-succeeds-silently · documented

cd with an empty or unset argument succeeds without going anywhere

NS-080 documentedempty-argument-succeeds-silently

cd with an empty or unset argument succeeds without going anywhere

reads as
`cd "$BUILD_DIR" && rm -rf ./*` exits zero and the script proceeds. Conclusion drawn: the build directory was entered and cleared.
actually
bash(1): change the current directory to dir; if dir is not supplied, the value of the HOME shell variable is the default. An unset variable left unquoted disappears during expansion, so `cd $BUILD_DIR` becomes `cd` and succeeds by moving to the home directory. Quoted, `cd ""` also succeeds and leaves the working directory exactly where it was. Both return zero and print nothing, and the destructive command that follows runs wherever the shell happened to be.
blind because
cd's status reports whether a directory change was performed, never which directory. Arriving where you intended and arriving in the home directory are the same value.
the check
Confirm the destination rather than the status, or refuse an empty value outright: `: "${BUILD_DIR:?BUILD_DIR is empty}"; cd "$BUILD_DIR" && [ "$PWD" = "$BUILD_DIR" ]`. Observed on bash 5.2.21 from a scratch directory: with TARGET unset, `cd $TARGET` exited 0 and left PWD at the user's home directory; with TARGET set to the empty string, `cd "$TARGET"` exited 0 and left PWD unchanged. `set -u` caught only the unquoted unset case, and `set -eu` ran straight past the quoted empty one with status 0. With CDPATH=/usr, `cd bin` from /tmp exited 0 in /usr/bin.
cost of missing
The rm, rsync or build step that follows operates on the wrong tree with full confidence, and in the home-directory case on a tree that contains everything.
mitigation
`${VAR:?}` fails on empty as well as unset, which `set -u` does not; assert on $PWD after any cd whose argument came from a variable.
generalises to
Every command that treats a missing argument as a request for its default rather than as an error: cd, `git checkout`, `kubectl` without a namespace, `docker build` with an empty context path.
source
man7.org

Reported as

Others this instrument misses

plain text · full registry