Skip to content

VNX-BASH-003: Failure ignored in a script without set -e / set -u

In a shell script that does not enable errexit, reports an unchecked cd followed by a relative-path delete, an integrity check whose result nothing reads, and (without nounset) rm -r of a path built from a variable that can be empty.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-BASH-003
Severity Medium
SARIF level warning
Kind sast
Languages Shell
CWE CWE-755
CWSS vector CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags bash error-handling set-e best-practice
Source internal/sast/rules/vnx-bash-003.rego

Overview

In an executable shell script (one with a sh/bash/dash/ksh/zsh shebang) that does not enable errexit (set -e, set -o errexit, a -e shebang flag or an ERR trap), this rule reports the three places where a silently ignored failure does damage: a bare cd or pushd that nothing checks (no || exit, &&, if or $? test) followed within ten lines by a delete with a relative path (rm ./*, rm -rf build, find . -delete, git clean -f, git reset --hard); an integrity check (sha256sum -c, gpg --verify, cosign verify, minisign -V, openssl dgst -verify) run as a bare statement whose result nothing reads; and, when nounset is also off, rm -r "$VAR/..." where VAR can be empty (an unchecked positional parameter, an environment variable the script never sets, or a command-substitution result) with no ${VAR:?}, default or -z/-n test. Not reported: scripts that enable errexit (and nounset, for the rm case), checked cd and checks, cd followed by non-destructive commands or absolute paths, a check that is the last command or a function’s return value, sourced libraries without a shebang, test paths and .bats files. The mere absence of set -euo pipefail is not reported.

Without set -e, a script continues executing even when a command fails with a non-zero exit code. A failed cp, curl, or database migration command will be silently ignored and subsequent steps will operate on incomplete or incorrect state. Without set -u, any typo in a variable name expands to the empty string rather than producing an error, so rm -rf "$MY_DIR/" becomes rm -rf "/" if MY_DIR is unset. Without pipefail, a failing command early in a pipeline (curl ... | jq ...) is invisible because the pipeline exit code is taken from the last command only.

Why This Matters

Silent failure in shell scripts is a common root cause of data loss, partial deployments, and security configuration mistakes. A deployment script that continues past a failed gpg --verify check may install a tampered artifact. A backup script that continues past a failed tar invocation may overwrite the previous backup with an empty file, believing the operation succeeded.

From a security perspective, scripts that silently swallow errors are particularly dangerous in CI/CD pipelines, bootstrap scripts, and container entrypoints. An attacker who can cause one step to fail, for example by removing a file, exhausting disk space, or providing malformed input, may be able to force the script into an unexpected code path where later security checks are skipped entirely, or where subsequent commands operate on attacker-controlled fallback values.

The pipefail option is especially important when scripts parse the output of external commands through pipes. Without it, a command injection attempt that causes the first stage of a pipeline to fail goes undetected while the rest of the pipeline continues to execute with potentially attacker-influenced output from a partial run.

What Gets Flagged

#!/bin/bash
cd "$BUILD_DIR"                 # FLAGGED: if cd fails, the rm runs here
rm -rf ./*

sha256sum -c app.tar.gz.sha256  # FLAGGED: a failed check is ignored
tar -xzf app.tar.gz -C /opt/app

rm -rf "${CACHE_DIR}/"*         # FLAGGED: CACHE_DIR unset means rm -rf /*

Not flagged:

#!/bin/bash
set -euo pipefail
cd "$BUILD_DIR"
rm -rf ./*

cd "$BUILD_DIR" || exit 1
rm -rf "${CACHE_DIR:?}/"*

Remediation

  1. Add set -euo pipefail immediately after the shebang line in every Bash script.
  2. For POSIX sh scripts (which lack pipefail), use set -eu as a minimum.
  3. When you intentionally need to allow a command to fail, use the || true idiom or a conditional rather than omitting the flag globally.
  4. For commands where failure is expected, capture the exit code explicitly: some_command || exit_code=$?.
# SAFE: error handling enabled immediately after shebang
#!/usr/bin/env bash
set -euo pipefail

DEPLOY_DIR="/opt/app"

# Intentional fallback where failure is acceptable
mkdir -p "$DEPLOY_DIR" || true

cp -r ./dist "$DEPLOY_DIR"
chown -R www-data "$DEPLOY_DIR"
systemctl restart app

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-bash-003
// nosec vnx-bash-003

Or record an ignore rule: vulnetix ignore add --rule vnx-bash-003 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-bash-003.rego, cli/internal/sast/docs/vnx-bash-003.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.