What's the problem
That is explains in perldoc perlop documentation about the <> operator which -n and -p use.
That special operator is used to read one line (one record, records being lines by default) of input, when that input is coming from each of the arguments in turns passed in @ARGV.
In:
perl -pe '' a b
-p implies a while (<>) loop around the code (here empty).
<> will first open a, read records one line at a time until the file is exhausted and then open b.
The problem is that, to open the file, it uses the first, unsafe form of open:
open ARGV, "the file as provided"
With that form, if the argument is
"> a", it opensain writing mode,"cmd|", it runscmdand reads it's output."|cmd", you've a stream open for writing to the input ofcmd.
So for instance:
perl -pe '' 'uname|'
Doesn't output the content of the file called uname| (a perfectly valid file name btw), but the output of the uname command.
If you're running:
perl -ne 'something' *
And someone has created a file called rm -rf "$HOME"| (again a perfectly valid file name) in the current directory (for instance because that directory was once writeable by others, or you've extracted a dodgy archive, or you've run some dodgy command, or another vulnerability in some other software was exploited), then you're in big trouble.
Files called > foo, foo|, |foo are a problem. But to a lesser extent < foo and foo as well as that means those files won't be processed or the wrong one will be.
How to fix/work around
AFAIK, there is nothing you can do to change that unsafe default behaviour of perl once and for all system-wide.
First, the problem occurs only with characters at the start and end of the file name. So, while perl -ne '' * or perl -ne '' *.txt are a problem,
perl -ne 'some code' ./*.txt
is not. More generally, it's a good idea to prefix globs with ./. That also avoids problems with files called - or starting with - with many other utilities.
Using -T to turn on taint mode helps to some extent. It will abort the command if such malicious file is encountered.
If you do want to process every file, regardless of their name, you can use the ARGV::readonly perl module on CPAN. That's a very short module that does:
sub import{
# Tom Christiansen in Message-ID: <24692.1217339882@chthon>
# reccomends essentially the following:
for (@ARGV){
s/^(\s+)/.\/$1/; # leading whitespace preserved
s/^/< /; # force open for input
$_.=qq/\0/; # trailing whitespace preserved & pipes forbidden
};
};
Basically, it sanitises @ARGV by turning " foo|" for instance into "< ./ foo|\0".
You can do the same in a BEGIN statement in your perl -n/-p command:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Here we simplify it on the assumption that ./ is being used.