[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: