Stop 29 of 42 · The Building · Risky shell scripts

Maintenance crews that trust every instruction

The bank’s maintenance crew carries the full ring of building keys. Their standing order is: “fetch today’s instructions from a notice board and do exactly what they say.” They never read them first, which is a gift to anyone who can edit the board.

Severity Low–HighCategory Injection RiskThe BuildingSources checked 25 Oct 2026

Watch it happen

Play it as

The maintenance crew downloads instructions from a notice board and runs them with full keys. Mallory edits the board, so the crew opens the vault for her.

Arrival

Each day the crew downloads the notice from the board outside, and the instructions walk in through the front door.

Website notice board to Instructions: download.
1 / 5

In bank terms and app terms

In the bankIn your app
Maintenance crewDeploy, CI/CD and cron shell scripts
Full ring of keysScripts running as root or with cloud credentials
Run whatever the board sayscurl … | sh and eval "$VARIABLE"
Blank name in the instructionUnquoted or empty variables like rm -rf $DIR/
Keys written on the noticePasswords or tokens hardcoded in the script

Are you exposed?

You might be exposed if…
  • Your install or deploy scripts use curl … | sh or curl -k / --insecure.
  • Scripts use eval with a variable, or hardcode tokens, passwords and cloud keys.
  • Scripts have no set -euo pipefail, use chmod 777, or echo secrets into CI logs.
What to do about it
  1. Download scripts to a file, review or verify them, then run them; never pipe from the internet into a shell.
  2. Quote every variable ("$DIR"), guard destructive paths with "${DIR:?}" so an empty value stops the script, avoid eval, and start bash scripts with set -euo pipefail.
  3. Read secrets from the environment or a secrets manager, and give scripts the minimum permissions.

Track these in your to-do list →

What it looks like in code

# BAD: eval with variable
eval "$USER_INPUT"

# GOOD: Use arrays and explicit commands
# Match the exact command with case (a substring test would let "list status" through)
case "$1" in
  list|status|restart) systemctl "$1" myservice ;;
  *) echo "denied" >&2; exit 1 ;;
esac

# BAD: Unquoted variable
rm -rf $DEPLOY_DIR

# GOOD: Always quote variables
rm -rf "${DEPLOY_DIR:?}"

# BAD: Curl pipe to shell
curl -fsSL https://example.com/install.sh | sh

# GOOD: Download, inspect, then execute
curl -fsSL https://example.com/install.sh -o install.sh
# Review install.sh
bash install.sh

Ask your AI tool to fix it

Paste this into Cursor, Claude, Lovable, Bolt or whatever you build with.

Prompt
Review every shell script (.sh, .bash, CI deploy scripts). Replace curl-pipe-to-shell with download, verify, then run; remove eval on variables; quote all variable expansions and guard rm -rf paths with ${VAR:?}; add set -euo pipefail to bash scripts; remove curl -k/--insecure; replace chmod 777 with the narrowest mode; and move hardcoded secrets to environment variables. List what changed.

Where this comes from

The facts, figures and fixes on this page were checked against these sources on 25 Oct 2026. They are linked so you can check for yourself.

Spot a mistake or something out of date? Report a correction · How these guides are made and checked

Go deeper in isitsecure

The free, open-source scanner checks for most of the problems in these guides.