fosstodon.org is one of the many independent Mastodon servers you can use to participate in the fediverse.
Fosstodon is an invite only Mastodon instance that is open to those who are interested in technology; particularly free & open source software. If you wish to join, contact us for an invite.

Administered by:

Server stats:

10K
active users

Kees Cook :tux:

@yossarian The use of '[[' is the problem. That's an evaluating comparison and is as dangerous as 'eval' (as shown). All scripts should be using just the single '['. Using '[[' is for compatibility with ancient shells.

@kees @yossarian Do we need to do something about this?

$ git grep '\[\[.*\]\]' origin/master -- ':*.sh' | wc --lines
1065

edit: there is some amount of false positives from sed regexes embedded in shell scripts. but also enough real cases

@vegard @kees @yossarian more like git grep '\[' ...

at least for me the `oh bummer' part was the observation that [ also behaves this way, because in bash it's a builtin. perhaps that can actually be considered a bug?

@kees @yossarian uh, good to know. I'm pretty sure at some point I've been told (maybe by some linting tool?) that [[ is preferrable to [.

@kees @yossarian shellcheck.net/wiki/SC2292 says "[[ .. ]] suppresses word splitting and globbing, supports a wider variety of tests, and is generally safer and better defined than [ .. ]"
Not enabled by default in current version, but I believe it was in the past.

www.shellcheck.netShellCheck: SC2292 – Prefer `[[ ]]` over `[ ]` for tests in Bash/Ksh.

@hanno @kees huh yeah, I guess that’s where I got the idea that [[ was preferred over [ from! thanks for finding that!

@hanno @yossarian Yeah this was a common recommendation long ago to "avoid bash-isms" for compatibility. Since then busybox and dash (the common non-bash "/bin/sh" instances) grew '[' support either internally or via /usr/bin/[

@hanno @kees yeah, I thought it was the other way around — [[ is always a builtin and [ is the standard test, which is *also* sometimes a builtin (e.g. within bash in some contexts)

@hanno @yossarian Ah, I may have it backwards then, but '[' remains the safe one. 😅

@kees @hanno @yossarian `[` is the POSIX one, but it has silly semantics around strings, which is you you end up doing stuff like `[ "x$foo" -ne "x" ]` to test non-emptiness.

`[[` on the other hand behaves "sanely", but it's definitely a bashism and not portable.

@kees @hanno @yossarian There are many misconceptions in this thread. The test command `[` is well-defined[1] and works as expected. It can be argued that `[[` doesn't work as expected since it introduces semantics which are incompatible with the rest of the shell language.

@flameeyes Your example is wrong, `-ne` is for math. You can test for null or unset simply: `[ "$foo" ]`.

Neither option is unsafe, OP issue stems from array indexing in bash, specifically.

[1]: pubs.opengroup.org/onlinepubs/

pubs.opengroup.orgtest

@jntesteves @hanno @yossarian @flameeyes
Ah! That's why this doesn't work:

$ foo='0$(echo bad >&2) + 7' $ if [[ "$foo" -eq 0 ]]; then echo zero; fi
-bash: [[: 0$(echo bad >&2) + 7: syntax error: invalid arithmetic operator (error token is "$(echo bad >&2) + 7")

@jntesteves @kees @hanno @yossarian @flameeyes Jup, the x-in-variable expansion was for buggy implementations of `[` but it can be implemented correctly without requiring this workaround.

As @jntesteves said, `[` is well-defined. Arguably, `[[` is a bit "safer" for word expansions (after all, that's what it was designed to be better at), but you can do `[` quite safely.

@hanno @kees @yossarian Yeah [[ is a bashism. For example, with FreeBSD's default sh:

[[ x ]] && echo true
/bin/sh: [[: not found

[ via a builtin or /usr/bin/[ has existed as far back as I can see

@kees this itself is a TIL for me, since I was taught/received that [[ is newer than [!

but also, is this always true? [ and ‘test’ will also use eval if the builtin version gets used first, but I don’t know the rules around when the builtins are used vs. not (for my interactive shell, they seem to always have precedence)

@yossarian

$ type [
[ is a shell builtin
$ foo='a[$(echo bad >&2)] + 7'
$ if [ "$foo" -eq 0 ]; then echo zero; fi
-bash: [: a[$(echo bad >&2)] + 7: integer expression expected
$ if /usr/bin/[ "$foo" -eq 0 ]; then echo zero; fi
/usr/bin/[: invalid integer ‘a[$(echo bad >&2)] + 7’

Both built-in and binary appear safe to me. The [[ remains unsafe:

$ if [[ "$foo" -eq 0 ]]; then echo zero; fi
bad

@yossarian

Dash and busybox are safe too:

dash: 3: [: Illegal number: a[$(echo bad >&2)] + 7

sh: a[$(echo bad >&2)] + 7: bad number

@kees out of curiosity, which version of bash was that? I got the [ and test variants working locally; I’ll find and share those tonight

@yossarian My bash is from Ubuntu 22.04:

GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)

If you have a PoC working with [ I am excited to see it! I wonder if there is some distro shell patching going on...

@kees @hanno

here's a PoC for `test` and `[` working with the `-v` case:

```
# case 1
test -v 'x[$(cat /etc/passwd)]'

# case 2
[ -v 'x[$(cat /etc/passwd)]' ]
```

OTOH the arithmetic case doesn't work, and i get the same errors as you two:

```
$ test 'a[$(cat /etc/passwd > /tmp/pwned)]' -eq 0
bash: test: a[$(cat /etc/passwd > /tmp/pwned)]: integer expression expected

$ [ 'a[$(cat /etc/passwd > /tmp/pwned)]' -eq 0 ]
bash: [: a[$(cat /etc/passwd > /tmp/pwned)]: integer expression expected
```

...so my post is a little wrong, the "it works for [ and test" should be only under the `-v` case 🙂

@kees @hanno

also, that's with `GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)`

@yossarian @hanno

Hah! Nice find. I didn't even know about "-v", and it seems to be a bashism (dash, busybox, and test all balk at it). I've always used "-z" for that kind of thing.

Looking at the bash manpage, it seems it's not everything that takes a "varname" (i.e. -R doesn't trip), just -v exposing the array parser again.

@kees @yossarian oh that's terrible though, because [ forks and [[ does not.

@kees @yossarian oh this is one of those things where it was different in the past for dumb reasons, isn't it?

@vathpela @kees @yossarian It's mostly that POSIX defines [ to exist as a binary but allows for a shell to instead implement it as a built-in.

@kees @yossarian I tried with [ and it did not work. And 'type [' says "shell builtin" whereas 'type [[' says "shell keyword"

@kees @yossarian There is a problem with '[': it can confuse values coming from variables with operators. For example, suppose that you have
[ "$x" == a -a "$y" == b ]
Most of the time, it works. However, if $x is "!", it breaks. The correct version is
[ "x$x" == xa -a "x$y" == xb ]
Similarly,
[ -n "$x" -a -n "$y" ]
breaks if $x is "==".