keepcalculator

06 Numbers and Powers

Bash Input & Command Audit Checklist

Card No. 06·32141 · Updated · Standard formula, shown below

Auditing bash input and command use means systematically reviewing every command a user or script can execute to catch unsafe, unintended, or malicious actions before they happen. You check what data enters your shell, how commands are constructed, and what those commands can do to your system. This article gives you a practical checklist for that review, with the background you need to understand why each check matters.

Why Audit Bash Input and Commands

Bash is a powerful shell that runs on almost every Linux and macOS system. It interprets your typed commands and executes programs, often with the same privileges as your user account. A single mistake—like an unquoted variable or a command that unexpectedly runs as root—can delete files, expose secrets, or give an attacker a foothold. Auditing input and command use is how you find those mistakes before they become incidents.

There are three main reasons to perform this audit:

  • Security: Malicious input can exploit command injection, path traversal, or unsafe temporary files.
  • Reliability: A script that works on your machine may fail on another due to different paths, locales, or environment variables.
  • Maintainability: Clear, predictable commands are easier to debug and modify months later.

What Exactly Are You Auditing?

An audit covers two related but distinct areas:

  • Input: Everything that enters your script or command line—user arguments, environment variables, files read by the script, output from other commands, and even the script’s own name.
  • Command use: How you invoke programs, the flags you pass, how you handle their exit codes, and whether you are using the right tool for the job.

For example, a script that reads a filename from user input and then runs cat "$file" has a different risk profile than one that runs cat $file without quotes. The audit must catch both the missing quotes and the fact that the user controls the filename entirely.

How to Check Input Handling

Start with the data that enters your commands. For each input, ask: where does it come from, and what happens if it contains spaces, wildcards, or shell metacharacters?

  1. Quote all variable expansions. Always use double quotes around "$var" unless you explicitly want word splitting and globbing. Unquoted expansions are the single most common source of bash bugs and security holes.
  2. Validate input before using it. Check that a filename is not empty, that a number is within an expected range, and that a string matches a pattern. Use [[ $input =~ ^[0-9]+$ ]] for numeric checks.
  3. Beware of command substitution. $(command) runs a sub-shell and can execute arbitrary code if the command is built from untrusted data. Never put unvalidated user input inside $(...) or backticks.
  4. Check environment variables. Variables like PATH, IFS, and LD_PRELOAD can be set by an attacker to redirect commands or alter behavior. In scripts, set PATH to a known safe value at the top.
  5. Handle filenames safely. Use -- to end option parsing (e.g., rm -- "$file") and use find -print0 with read -d '' for filenames with newlines.

Checking Command Invocation and Options

Once input is safe, review the commands themselves. Are you calling the right program? Are you using its options correctly?

  • Use full paths or a controlled PATH. If an attacker can place a fake ls earlier in your PATH, you will execute it. In scripts, use /bin/ls or set PATH="/usr/bin:/bin" at the start.
  • Verify command existence. Use command -v to check if a program is available before calling it, and handle the case where it is not.
  • Check exit codes. Most commands return 0 on success and non-zero on failure. Use set -e to exit on error, or check $? explicitly. Ignoring exit codes can hide failures that leave your system in a bad state.
  • Avoid eval and sh -c. These execute strings as code. If any part of the string comes from user input, you have a command injection vulnerability. Find safer alternatives like arrays or functions.
  • Be careful with globs. *.txt expands to all matching files. If no files match, the glob remains literal (unless nullglob is set), which can cause errors. Use set -o nullglob or check for matches.

Common Problems and How to Spot Them

During an audit, look for these recurring issues:

  • Unquoted variables—the classic. Search for $var without quotes in your scripts.
  • Missing -- before filenames—a file named -rf can be interpreted as an option.
  • Relying on ls parsing—ls output is not safe for programmatic use. Use find or globs instead.
  • Using cat on large files—not a security issue, but inefficient. Use read or mapfile.
  • Not using set -u—this makes bash error on undefined variables, catching typos early.
  • Hard-coded paths—if a script uses /home/user/scripts and the user changes, it breaks. Use relative paths or configuration.

Tools and Techniques for Auditing

You do not have to audit manually. Several tools can help you find issues:

  • ShellCheck—a static analysis tool that flags many common bash pitfalls, including unquoted variables, missing --, and unsafe eval. Run shellcheck myscript.sh.
  • Bash’s -n flag—bash -n script.sh checks syntax without running it.
  • set -x—traces each command as it runs, showing exactly what is being executed. Use it to see if variables expand as expected.
  • Manual review with grep—search for dangerous patterns: eval, $(, backticks, unquoted variables, rm -rf.

Safety and Best Practices

An audit is only as good as the practices you follow. Adopt these habits to reduce risk:

  • Use set -euo pipefail at the top of every script. This exits on errors, treats undefined variables as errors, and catches failures in pipelines.
  • Prefer arrays over strings when building commands. An array like args=(--force "$file") avoids word-splitting issues.
  • Never run scripts as root unless absolutely necessary. If you must, audit even more carefully.
  • Use temporary files with mktemp and clean them up with a trap. Avoid predictable names like /tmp/tmpfile.
  • Read the manual—man bash explains quoting, expansions, and builtins. Knowing the exact behavior prevents surprises.

Frequently Asked Questions

What is command injection?

Command injection is when an attacker can make your shell execute arbitrary commands. It happens when unvalidated input is placed into a command string that is then evaluated, such as with eval or sh -c. For example, if a script does ping $host and the user enters ; rm -rf /, the shell sees two commands. Always quote and validate input, and avoid eval entirely.

Why is quoting so important in bash?

Quoting prevents the shell from interpreting special characters. Double quotes preserve spaces and most special characters but still allow variable expansion. Single quotes preserve everything literally. Without quotes, the shell performs word splitting and globbing, which can turn a filename with spaces into multiple arguments or expand a * into a list of files. This leads to bugs and security holes.

What does set -e do?

set -e makes the script exit immediately if any command returns a non-zero exit status. This prevents the script from continuing after an error, which is often safer. However, it can be tricky with commands that are expected to fail, like grep when no match is found. Use if grep ... or append || true to handle those cases.

How do I check if a command exists before using it?

Use command -v in an if statement: if command -v curl >/dev/null; then curl ...; else echo "curl not found"; fi. This is more reliable than checking a specific path like /usr/bin/curl because it respects the user’s PATH.

Is it safe to use rm -rf in a script?

rm -rf is dangerous because it recursively deletes without confirmation. It is safe only if you are absolutely certain the path is correct and cannot be influenced by user input. Always quote the path, use -- before it, and consider adding a safety check like [[ $dir =~ ^/tmp/ ]] to ensure you are deleting the right place.

What is the difference between $(...) and backticks?

Both perform command substitution, but $(...) is preferred because it nests better and is easier to read. Backticks require escaping inner backticks and can be ambiguous. For example, echo $(date) is clearer than echo `date`. In an audit, flag any use of backticks and suggest replacing them.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →