Use the unofficial Bash strict mode
redsymbol.net
redsymbol.net
set -o errtrace
trap 'err_handler $?' ERR
err_handler() {
trap - ERR
let i=0 exit_status=$1
echo "Aborting on error $exit_status:"
echo "--------------------"
while caller $i; do ((i++)); done
exit $?
} The set -e option instructs bash to immediately exit if
any command has a non-zero exit status. You wouldn't
want to set this for your command-line shell,
...but, it's a great addition to your buddy's `.bashrc`. For maximum effectiveness, be physically present, perhaps with a camera. :) echo 'sleep .1' >> ~/.bashrc
By week 2 my poor friend had conditioned himself to work within 1 session since spawning new sessions became unbearably slow :)- with an unset 'hello' variable, try:
set -eu
echo "`echo $hello` world"
echo "huh still going?!"
foo="`echo $hello` world"
echo "not reaching this as expected"
Fun, now you have to manually write your whole program in what's basically SSA (static single assignment) form.- It's deactivated in if/then context, which at first makes sense, but then when you try to force it on explicitely like:
set -eu
if (set -eu; false; true); then
echo "huh why still true??"
else
echo false
fi
and your declaration is just ignored.. you begin to wonder how many places set -e misses really.Rule of thumb: quote all your variables.
It's unfortunate the semantics of bash don't have variable references behave like they are quoted by default. I really wish it did.
#!/bin/bash
items=(
'a'
'b c'
"d\te"
"f\ng"
)
echo "Unquoted:"
for item in ${items[@]}; do
echo -e ". $item"
done
echo "Quoted:"
for item in "${items[@]}"; do
echo -e ". $item"
done
set -euo pipefail
IFS=$'\n\t'
echo "Unquoted strict mode:"
for item in ${items[@]}; do
echo -e ". $item"
done
... I get this output: Unquoted:
. a
. b
. c
. d e
. f
g
Quoted:
. a
. b c
. d e
. f
g
Unquoted strict mode:
. a
. b c
. d e
. f
g
Note the output for "Quoted" and "Unquoted strict mode" are identical.(GNU bash, version 4.2.37(1)-release (x86_64-pc-linux-gnu))
My first thought: It seems like only $@, $* and variables read from the environment could be possible attack vectors. But since shell scripts can't be setuid, it's hard for me to immediately imagine how unquoting could enable a new exploit.
EDIT: the most dangerous example I can think of right now is a script like this, where a webapp invokes the script and passes a GET param as an argument:
#!/bin/bash
exec $1
But if I execute this like so: ./badscript.sh 'some-benign-command ; cat /etc/passwd'
... then passwd is not exposed, because the tokens ";", "cat" and "/etc/passwd" are passed into the argv of some-benign-command.(And if "cat /etc/password" itself can be passed as $1, then quoting won't help...)
E.g. when a function receives less arguments than necessary and you retrieve them like this:
function f()
{
declare -r x="$1"; shift
declare -r y="$1"; shift
declare -r z="$1"; shift
}
with less than three arguments passed and shift_verbose on you will get an
error message the moment you shift and with "set -e" in addition, execution
will be aborted.See https://www.gnu.org/software/bash/manual/html_node/The-Shopt...
Also, "set -o noclobber" might be useful. If you try to redirect to an existing file with ">", it will fail. If you explicitly want to overwrite the file without triggering the error, use ">|".
See https://www.gnu.org/software/bash/manual/html_node/Redirecti...
if [ -z "$1" ]; then
usage();
exit 1;
fi;
If I'm using `set -eu`, that dies on the `$1` with a nasty error message, rather than printing my usage message. I've resorted to moving `set -eu` to after these kind of checks, but that makes me uneasy.I'm still trying to recall which I got hit with (it's been a few years), but IIRC there were a few at both the 3.x and 4.x transition points.
Bottom line is - keep bash scripts small, but be prepared to deal with the cases when they grow like Jack's magic beanstalk.
Plus it’s some sort of middle ground between “BDFL tells you to put a space there“ and “one day, archaeologists will find a script written in Perl, awk and csh and it will become the new millenium’s Rosetta stone“.
See I'm of the opinion that if you need arrays and associative arrays, bash is the wrong tool for the job. If you have a recent bash it has both of those, just seems wrong in such a clunky language with awful scoping.
if [ $? != 0 ]; then
echo "{\"error\": \"Failed to connect to the database.\"}" >&2;
exit 1;
fi if ! command_that_may_error; then
echo "{\"error\": \"Failed to connect to the database.\"}" >&2;
exit 1;
fi for arg in $@; do
A better way to do it is to quote it: for arg in "$@"; do
because then you can capture newlines and tabs. Bash will automatically convert it to separate parameters, even though there is only one quoted variable.