3

When I try the last example from perlfaq5: How-do-I-count-the-number-of-lines-in-a-file? I get an error-message. What should I do to get the script working?

#!/usr/local/bin/perl -T
use warnings;
use 5.012;

$ENV{PATH} = undef;

my $filename = 'perl2.pl';

if( $filename =~ /^([0-9a-z_.]+)\z/ ) {
    my $lines = `/usr/bin/wc -l $1`;
    print $lines;
}

Output:

Insecure $ENV{ENV} while running with -T switch at ./perl1.pl line 10.
sid_com
  • 24,137
  • 26
  • 96
  • 187
  • Good question. But BTW, even though `wc -l` is likely to be the fastest on a big file, trying to grab the filename with a regex like this will fail on filenames containing unusual characters (and if you relax the regex, you'll need to add quotes to the shell command, and you'll never be certain you've quoted everything exactly right...) – j_random_hacker Dec 29 '10 at 07:22
  • Is there an other way to untaint the file-name? – sid_com Dec 29 '10 at 09:43
  • The security chapter in _Mastering Perl_ pulls together just about everything you need to know about tainting. – brian d foy Dec 29 '10 at 15:49

1 Answers1

5

2nd Edition of Answer

The perldoc perlsec manual describes taint mode (there is also perldoc Taint for a module related to Taint mode).

In part, it illustrates:

$path = $ENV{'PATH'};       # $path now tainted

$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

$path = $ENV{'PATH'};       # $path now NOT tainted
system "echo $data";        # Is secure now!

After the $ENV{PATH} = undef; in your code, I was warned about CDPATH. So, adapting that code, I used (perl2.pl again):

#!/usr/bin/env perl -T
use warnings;
use 5.012;

delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

my $filename = 'perl2.pl';

if ($filename =~ /^([0-9a-z_.]+)\z/)
{
    my $lines = `/usr/bin/wc -l $1`;
    print $lines;
}

With the answer '13 perl2.pl' this time. This is far less draconian than the 1st Edition of the answer.

1st Edition of Answer

This draconian solution 'works':

#!/usr/bin/env perl -T
use warnings;
use 5.012;

foreach my $env (keys %ENV)
{
    undef $ENV{$env};
}

my $filename = 'perl2.pl';

if ($filename =~ /^([0-9a-z_.]+)\z/)
{
    my $lines = `/usr/bin/wc -l $1`;
    print $lines;
}

If the script is called 'perl2.pl', then running perl -T perl2.pl yields the answer '16 perl2.pl' (if you don't have any trailing blank lines).

I call it 'draconian' because I've unset every environment variable, piecemeal.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I suppose you didn't try your examples with a the "#!/usr/bin/env perl -T" Shebang. – sid_com Dec 29 '10 at 09:29
  • 2
    `foreach my $env (keys %ENV) { undef $ENV{$env}; }` Why `undef` instead of `delete`? Why not `local %ENV = ();` to empty the hash in one instruction? ↑ sid_com above ↑ hints that `env perl -T` does not work. Improve all this. – daxim Dec 29 '10 at 11:00
  • @daxim: because it is Perl, TMTOWTDI, and it was late at night. – Jonathan Leffler Dec 29 '10 at 14:15