2014-12-06 20:19:21 -0500, Chet Ramey: > On 12/6/14 6:24 PM, Stephane Chazelas wrote: > > Hiya, > > > > this is potentially dangerous: > > > > If $a was previously used as an array (or hash), then: > > > > declare a=$external_input > > So what you're saying is that blindly using external input can sometimes > have negative consequences?
In this case, it's not about sanitizing external input, at least as far as the $external_input variable is concerned. When a script writer writes: declare -l a="$external_input" he's entitled to expect $a to contain the lower case version of $external_input whatever $external_input contain. If $external_input happens to contain "($(echo FOO))", I'd expect $a to contain "($(echo foo))", not "foo" just because $external_input happens to follow a specific pattern. Where the script writer may be to blame is for running "declare" when that "a" variable was previous declared as an array (though that could have been done by code he invoked from 3rd party libraries), or not anticipating that his code would be invoked in contexts where that "a" variable may have been declared as an array. But even then, he can be excused not to imagine that declare -l a="$external_input" could ever run an arbitrary commands. And even if he intended to declare "a" as an array beforehand, he could reasonably expect declare -l a="$external_input" to do the same as declare -l a; a="$external_input" That is, the same as a[0]="$external_input". I'd say we have at least a documentation issue. [...] > At some point, you have to put some burden of responsibility onto the > script writer. Yes, definitely. It's up to everyone to try and make this digital world a safer place. In this case though, bash can easily do its bit by changing this behaviour (I can't imagine anyone relying on the behaviour as it is now). > > > > If not changed, that behaviour may be worth documented. > > declare's behavior is already documented: > > "Arrays are assigned to using compound assignments of the form > name=(value1 ... valuen), where each value is of the form > [subscript]=string." ... "This syntax is also accepted by the declare > builtin." a=$external_input is not of the form name=(value ... valuen), it's of the form var=$other_var. And when doing it without "declare", it works as expected. The problem is when using "declare" Similarly, a="(foo)" Assigns "(foo)" to the element of indice 0 in the array "a" and one could expect: declare a="(foo)" to do the same. > > > > ksh behaviour that only accepts "(" when litteral and unquoted > > (or zsh that doesn't accept it at all) is a bit saner IMO. > > declare is a builtin, not a reserved word. At the point when it is > invoked, its arguments have already been expanded. The question is > how much argument-mangling you want to do, and how much is worth it. It's already parsed differently from other builtins. declare a=(foo bar) works echo a=(foo bar) doesn't (and declare a=$b doesn't do word splitting on $b for instance) $ b='[0]=foo' bash -c 'declare a=($b); echo $a' [0]=foo does already the right thing (remember you fixed that after I reported the bug a few years ago). I argue it's a similar issue here. [...] > > $ b='($(uname))' bash -c 'declare -a a; declare a="$b"; printf "<%s>\n" > > "$a" "${a[0]}"' > > <> > > <Linux> > > $ b='($(uname))' bash -c 'a=(); declare a=$b; printf "<%s>\n" "$a" > > "${a[0]}"' > > <Linux> > > <Linux> > > > > (I'd expect the same result for both). > > You don't say what version of bash you're using, but I get the same results > for both commands back to bash-4.0 (when I quit testing). ~$ bash --version GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. ~$ b='($(uname))' bash --norc -c 'declare -a a; declare a="$b"; printf "<%s>\n" "$a" "${a[0]}"' <> <Linux> (on Linux Mint amd64). > > It may be worth recommending people do a "unset var" before > > doing a "declare [-<option>] var" unless they do intend to retain the > > previous value and/or type > > Sure, that's good programming practice. It should be recommended in a > bash programming guide -- and there are several excellent ones. [...] At least the bash doc could document what happens when using "declare" on a variable that was already assigned or declared (in the current context and not). -- Stephane