Re: [checks/scripts] script_is_evil_and_wrong() and bashisms update
On Tue, 2008-03-25 at 22:58 +0000, Adam D. Barratt wrote:
> Hi,
>
> I've attached a few patches against current lintian SVN which update
> checks/scripts to incorporate some changes that have been made to
> checkbashisms recently (there are a few changes I've not included such
> as ignoring multi-line quoted text and checking for invalid function
> names, as I'd like to see how they stand up to an archive-wide test
> first).
and predictably enough, a couple of updates.
> scripts_are_more_evil_and_wrong.diff
> ------------------------------------
>
> The archive-wide checkbashisms runs have revealed more methods of
> disguising something as a shell script than script_is_evil_and_wrong()
> currently catches.
>
> This diff includes the regex fix from #471333 together with
Which was subtly broken in and of itself; I've now closed the bug. The
updated patch includes a version that works for cases where just $@ is
quoted, or where the whole of ${1:+$@} is quoted.
> - Allow arguments to eval to be double- as well as single-quoted
> - Increase the number of lines scanned
[...]
> - Match lines execing $var if $var has previously been assigned the vale
> of $0.
- Allow the exec to be preceded by && as well as a semicolon.
> bashisms.diff
> -------------
>
> - Update $FOO checks to also match ${FOO}
> - Allow a space between >& and the file descriptor
> - Move the herestring (<<<) check to string bashisms to allow matching
>
> bar="$(cut '-d|' -f2 <<< "$foo")"
>
> - Enhance the "read without variable" test to also catch attempts to
> pass options other than -r
> - Add checks for $SECONDS and $BASH_*
> - Add checks for suspend, caller, complete, compgen, declare, typeset,
> disown, builtin, set -[BHT], alias -p, unalias -a, local with options or
> an assigned value (i.e. "local -foo" or "local foo=bar") and VAR+=foo
I've disabled the "complete" check from checkbashisms as it's rather
prone to false positives, so I've removed it from the patch. There are
also a couple of additions:
- Add checks for pushd and popd.
- Enhance the "brace expansion" test to allow "${foo},${bar}".
Admittedly the FPs in the existing test are unlikely to be seen in full
shell scripts, but they crop up quite a lot in shell snippets in
makefiles (which I've been looking at adding at least basis support for
to checkbashisms)
Adam
--- scripts.orig 2008-03-29 18:18:41.000000000 +0000
+++ scripts 2008-03-29 18:20:30.000000000 +0000
@@ -551,11 +551,14 @@
'\$\{!\w+[\@*]\}', # ${!prefix[*|@]}
'\$\{!\w+\}', # ${!name}
'(\$\(|\`)\s*\<\s*\S+\s*(\)|\`)', # $(\< foo) should be $(cat foo)
- '\$RANDOM\b', # $RANDOM
- '\$(OS|MACH)TYPE\b', # $(OS|MACH)TYPE
- '\$HOST(TYPE|NAME)\b', # $HOST(TYPE|NAME)
- '\$DIRSTACK\b', # $DIRSTACK
- '\$EUID\b', # $EUID should be "id -u"
+ '\$\{?RANDOM\}?\b', # $RANDOM
+ '\$\{?(OS|MACH)TYPE\}?\b', # $(OS|MACH)TYPE
+ '\$\{?HOST(TYPE|NAME)\}?\b' # $HOST(TYPE|NAME)
+ '\$\{?DIRSTACK\}?\b' # $DIRSTACK
+ '\$\{?EUID\}?\b', # $EUID should be "id -u"
+ '\$\{?SECONDS\}?\b', # $SECONDS
+ '\$\{?BASH_[A-Z]+\}?\b', # $BASH_SOMETHING
+ '<<<', # <<< here string
);
my @bashism_regexs = (
'function \w+\(\s*\)', # function is useless
@@ -563,13 +566,13 @@
'(?:^|\s+)source\s+(?:\.\/|\/|\$)[^\s]+',
'(\[|test|-o|-a)\s*[^\s]+\s+==\s', # should be 'b = a'
'\s(\|\&)', # pipelining is not POSIX
- '[^\\\]\{([^\s]+?,)+[^\\\}\s]+\}', # brace expansion
+ '[^\\\]\{([^\s\\\}]+?,)+[^\\\}\s]+\}', # brace expansion
'(?:^|\s+)\w+\[\d+\]=', # bash arrays, H[0]
- '(?:^|\s+)read\s*(?:;|$)', # read without variable
+ '(?:^|\s+)(read\s*(-[^r])?(?:;|$))', # should be read [-r] variable
'(?:^|\s+)kill\s+-[^sl]\w*', # kill -[0-9] or -[A-Z]
'(?:^|\s+)trap\s+["\']?.*["\']?\s+.*[1-9]', # trap with signal numbers
'\&>', # cshism
- '(<\&|>\&)\s*((-|\d+)[^\s;|\)\`&]|[^-\d])', # should be >word 2>&1
+ '(<\&|>\&)\s*((-|\d+)[^\s;|\)\`&]|[^-\d\s])', # should be >word 2>&1
'\[\[(?!:)', # alternative test command
'(?:^|\s+)select\s+\w+', # 'select' is not POSIX
'(?:^|\s+)echo\s+-e', # echo -e
@@ -577,7 +580,20 @@
'(?:^|\s+)let\s', # let ...
'(?<![\$\(])\(\(.*\)\)', # '((' should be '$(('
'(\[|test)\s+-a', # test with unary -a (should be -e)
- '<<<', # <<< here string
+ '(?:^|\s+)suspend\s', # suspend
+ '(?:^|\s+)caller\s', # caller
+ '(?:^|\s+)compgen\s', # compgen
+ '(?:^|\s+)declare\s', # declare
+ '(?:^|\s+)typeset\s', # typeset
+ '(?:^|\s+)disown\s', # disown
+ '(?:^|\s+)builtin\s', # builtin
+ '(?:^|\s+)set\s+-[BHT]+', # set -[BHT]
+ '(?:^|\s+)alias\s+-p', # alias -p
+ '(?:^|\s+)unalias\s+-a', # unalias -a
+ '(?:^|\s+)local\s+-[a-zA-Z]+', # local -opt
+ '(?:^|\s+)local\s+\w+=', # local foo=bar
+ '(?:^|\s+)\w+\+=', # should be VAR="${VAR}foo"
+ '(?:^|\s+)(push|pod)d\b', # (push|pod)d
);
# since this test is ugly, I have to do it by itself
--- scripts.orig 2008-03-28 18:15:03.000000000 +0000
+++ scripts 2008-03-29 18:04:20.000000000 +0000
@@ -735,18 +735,22 @@
my $ret = 0;
open (IN, '<', $filename) or fail("cannot open $filename: $!");
my $i = 0;
+ my $var = "0";
local $_;
while (<IN>) {
chomp;
next if /^#/o;
next if /^$/o;
- last if (++$i > 20);
- if (/(^\s*|\beval\s*\'|;\s*)exec\s*.+\s*.?\$0.?\s*(--\s*)?(\${1:?\+)?.?\$(\@|\*)/o) {
- $ret = 1;
- last;
- }
+ last if (++$i > 55);
+ if (/(^\s*|\beval\s*[\'\"]|(;|&&)\s*)exec\s*.+\s*.?\$$var.?\s*(--\s*)?.?(\${1:?\+.?)?\$(\@|\*)/) {
+ $ret = 1;
+ last;
+ } elsif (/^\s*(\w+)=\$0;/) {
+ $var = $1;
+ }
}
close IN;
+
return $ret;
}
Reply to: