cmac has asked for the wisdom of the Perl Monks concerning the following question:

The following sequence in an Apache2 post_config handler gets a taint error:
opendir DIR, DB_DIREC or die "can't open direc ".DB_DIREC.": $!"; my @dels = grep /^__/, readdir(DIR); closedir DIR; for (@dels) {substr($_, 0, 0) = DB_DIREC.'/'} if (@dels) {unlink @dels}
This site runs with taint checking. The code above gets the following fatal error during Apache startup:
[error] Insecure dependency in unlink while running with -T switch at +/usr/local/apache2/plib/Central.pm line 189.
There is nothing like "user input" anywhere near this sequence. Suggestions as to how to get rid of this error will be much appreciated.

Thanks in advance,
cmac
www.animalhead.com

Replies are listed 'Best First'.
Re: hard-to-understand taint error
by CountZero (Bishop) on Feb 18, 2009 at 06:07 UTC
    readdir is an unsafe operation and therefore its results are tainted. grep doesn't clean the tainted data as it will pass on the original data when its value is TRUE. You have to pass the data itself through a regular expression to clean them.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      I cooked up a regexp for "how the filenames to be deleted should look" and now the taint checking is happy:
      my @dels = (); opendir DIR, DB_DIREC or die "can't open direc ".DB_DIREC.": $!"; for (readdir DIR) { if (/^(__[-!#.0-9@-Z_a-z]+)$/) {push @dels, DB_DIREC."/$1"} } closedir DIR; if (@dels) {unlink @dels}
      Thanks for your help,
      cmac
      www.animalhead.com

        That regex won't capture files with UC letters

        #!/usr/bin/perl use warnings; use strict; # 744661 my @var = [ 'foo.Bar', 'for.bar', ]; for my $var(@var) { if ( $var =~ /^(__[-!#.0-9@-Z_a-z]+)$/ ) { print "\$var: $var\n"; } else { print "no match!\n"; } }

        If you're concerned about files like "foo.Bar", use the /i modifier.

        It looks like your original requirement was files starting with '__'.

        Similar to what ww is saying, you can simplify your capture to /^(__.*)$/; then you won't have to worry about upper-case matching.

      Changed the for statement in the previous sequence to:
      for (@dels) {s!(.+)!DB_DIREC."/$1"!e}
      Now everything goes through a regular expression and the same error results.
        As you have found out yourself, you must take the captured data outside of the regex in order to untaint it.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: hard-to-understand taint error
by ikegami (Patriarch) on Feb 18, 2009 at 05:52 UTC

    There is nothing like "user input" anywhere near this sequence

    The contents of the directory are hardcoded? No.

    >perl -MScalar::Util=tainted -Tle"opendir $dh, '.'; print(tainted(read +dir($dh))?1:0); 1
Re: hard-to-understand taint error
by Anonymous Monk on Feb 18, 2009 at 06:03 UTC
    Read perlsec
    All command line arguments, environment variables, locale information (see perllocale), results of certain system calls (readdir(), readlink(), the variable of shmread(), the messages returned by msgrcv(), the password, gcos and shell fields returned by the getpwxxx() calls), and all file input are marked as "tainted".