1. Shell-escaping all values used to construct commands
Avoid shell-escaping and the shell altogether! Use list form program execution wherever possible.
In Perl:
open(my $fh, "-|", "find", $dir, "-type", "f", "-print0") or die;
In Python:
f = os.popen2(["find", dir, "-type", "f", "-print0"])
If you're executing a pipeline, you do need to get the shell involved. But I'd probably write the author's examples like so (once again avoiding the need to shell escape arguments):
f = os.popen2(["sh", "-c", \
'find "$0" -type f -print0 | xargs -0 grep foo | wc -l', dir])
2. Prefixing each multi-command pipeline with “set -o pipefail;
Alas, pipefail is not in POSIX /bin/sh, which many of us prefer for shell scripting. It's in bash and ksh though.
3. Explicitly checking for failure after each shelled out command
Valid. You should always do this. In shell scripts, "set -e" is a good thing, although pipelines without "set -o pipefail" are still a problem.
The article does not address another significant point: filenames should also be classified as untrusted input. Using -- to separate flags from arguments can help:
/tmp$ mkdir x
/tmp$ cd x
/tmp/x$ echo hello > a
/tmp/x$ grep hello *
hello
/tmp/x$ touch -- -q
/tmp/x$ grep hello *
/tmp/x$ grep hello -- *
a:hello
/tmp/x$
It's rare to see protection against this problem either when shelling out or in native shell scripts.
* hsh https://github.com/jgoerzen/hsh/wiki makes pipelines in haskell, using operators so they really look like pipelines, but without involving the shell. Example: "ls -l" -|- "wc -l"
My mistake, not knowing python's variable interpoltion well, I thought that the $0 was expanded by it, not the shell. Which, if it were the case, would indeed be vulnerable.
Isn't it a bit harsh to downvote to -1 a comment which links to two completly on-topic libraries?
Avoid shell-escaping and the shell altogether! Use list form program execution wherever possible.
In Perl:
In Python: If you're executing a pipeline, you do need to get the shell involved. But I'd probably write the author's examples like so (once again avoiding the need to shell escape arguments): 2. Prefixing each multi-command pipeline with “set -o pipefail;Alas, pipefail is not in POSIX /bin/sh, which many of us prefer for shell scripting. It's in bash and ksh though.
3. Explicitly checking for failure after each shelled out command
Valid. You should always do this. In shell scripts, "set -e" is a good thing, although pipelines without "set -o pipefail" are still a problem.