I've been mucking with the beginning of an insecure-ish perl module. Basically, if you run taint, you have to regexp and untaint your input. This module presents a shell() function that works like the backtick operator, but will shell-escape anything passed to it.

While working on it, I was befuddled by the following:

  1. Quoted regexps for character classes can't be interpolated
  2. Quoted regexps for character classes can't use the qr//x for extended regexps.
  3. You have to map { s/$evil/\\$1/g; $_ } (my @a = @_);
    instead of map { s/$evil/\\$1/g; $_ } @_;
    to get around the Modification of a read-only value attempted. I don't understand why map is trying to alter @_ with the s///.

package InSecurity; use base 'Exporter'; our @EXPORT = ('shell', 'shell_escape'); # kermit may like SUVs, but I keep my environment clean. delete @ENV{ grep { !/^(HOME|LOGNAME|USER)$/ } keys %ENV }; $ENV{'PATH'} = '/bin:/usr/bin'; my $evil_characters = qr![\;\|\!\$\`\&\*\(\)\>\<\'\"\\]!; # no q//x or interpolation with character classes = the suck. sub shell { local($_); my $cmd = join ' ', map { shell_escape($_) } @_; return `$cmd `; } sub shell_escape { local($_); $_ = $_[0]; s/($evil_characters)/\\$1/g; /(.*)/ and return $1; } ! caller() and do { print "Running insecurity tests\n"; for (keys %ENV) { print "$_ => $ENV{$_}\n" } printf("\`echo hi | grep hi\` => %s", shell("echo hi | grep hi")); }; caller() and return 1;

$_ (the bit s/// operates on) is an alias to each item on @_ inside of map{}. It isn't a copy of the item on the list. Thus, when you modify $_ through s///, you are actually modifying the elements of @_. I would guess that you are calling shell() here as shell("testing"); rather than shell($test); http://lists.debian.org/debian-user/2003/01/msg00940.html http://dev.perl.org/perl6/rfc/344.html
Thanks! You're spot on; one has to take care regarding where the elements of @_ came from, making the @_ copy to @a almost obligatory. -- Patrick.