从命令行运行perl
个单行有security implications。
问题是,选项-n/-p
会触发菱形运算符<>
,它使用open
形式的两个参数,因此文件名包含特殊字符,perl
不会按预期工作:
$ perl -pe '' 'uname|'
Linux
当文件名称以>
开头时更加危险,例如>file
。在这种情况下,文件将被截断。
要解决此问题,我们可以:
ARGV::readonly
模块这样的功能:perl -pe 'BEGIN{$_.="\0" for @ARGV} ...' ./*
-i
选项,因为perl
会在处理之前检查文件是否存在。-T
选项启用污染模式。我认为所有解决方案都可以解决这个问题,但也会产生副作用。如果我们可以强制perl
始终使用open
的参数形式,那么这将是一个更好的解决方案。
我想我们可以这样做,强制perl
总是使用open
的参数形式吗?
注意
该问题仅适用于从命令行运行perl
一个内联的情况,因为(当然)我们总是可以在Perl脚本中使用open
的三个参数形式。
答案 0 :(得分:4)
您可能只希望为单行(使用-e
开关)执行此操作,因此您需要为此目的自定义ARGV::readonly
。
package Sanitize::ARGV;
if ($0 eq '-e') {
@ARGV = map { # from ARGV::readonly
s/^(\s+)/.\/$1/;
s/^/< /;
$_.qq/\0/;
} @ARGV;
}
1;
要强制在系统范围内使用此程序包,可以使用别名为Patrick J.S.表明
alias perl='perl -MSanitize::ARGV'
或设置系统范围的PERL5OPT
变量
PERL5OPT="-MSanitize::ARGV"
如果你想变得更偏执,请隐藏你的系统perl(将其重命名为难以理解的东西)并用包装器替换它
#!/bin/bash
THE_REAL_PERL=/usr/bin/glorbqweroinuerqwer
$THE_REAL_PERL -MSanitize::ARGV "$@"
shell包装器仍将尊重PERL5OPT
在一般情况下,您可以使用CORE::GLOBAL
机制覆盖内置open
功能,例如
package SafeOpen;
use Carp;
BEGIN {
*CORE::GLOBAL::open = sub (*;$@) {
goto &CORE::open if @_ > 2;
Carp::cluck("OH MY GOD USING THE TWO-ARG open ARE YOU LIKE INSANE?");
return CORE::open($_[0], '<', $_[1] // $_[0]);
};
}
1;
当使用此模块时,您将使用2-arg open
调用来捕获任何人。不幸的是,我认为ARGV
文件句柄魔法的机制会绕过这个,所以它仍然无法帮助单行中命令行上的愚蠢文件名。
答案 1 :(得分:2)
在我之前的回答中,我假设ARGV
文件句法魔术模拟了2参数打开,但实际上并没有通过Perl的内置open
函数。所以我对它进行了一些调查,我想我想出了一个解决这个问题的实际方法。
Perl_nextargv
中的函数doio.c
是perl在ARGV
文件句柄周围执行I / O的地方。
在该函数的while
循环中,有两种打开文件句柄的调用。当LIKELY(PL_inplace)
为真时(即,当使用-i
开关运行perl时),呼叫为do_open_raw
。如果LIKELY(PL_inplace)
为false,则呼叫为do_open6
。后一个调用能够打开外部命令的管道,这通常是OP不想要的。
所以这是解决方案:在源文件doio.c
中,将do_open6
中的Perl_nextargv
次调用替换为do_open_raw
,并从源代码重建perl
。对于perl-5.22.0,原始代码看起来像
if (LIKELY(!PL_inplace)) {
if (nomagicopen
? do_open6(gv, "<", 1, NULL, &GvSV(gv), 1)
: do_open6(gv, PL_oldname, oldlen, NULL, NULL, 0)
) {
return IoIFP(GvIOp(gv));
}
}
并且修改后的代码看起来像
if (LIKELY(!PL_inplace)) {
if (do_open_raw(gv, PL_oldname, oldlen, O_RDONLY, 0)) {
return IoIFP(GvIOp(gv));
}
}
(nomagicopen
参数在您使用the new <<>>
syntax from perl 5.22.0时为true。我猜我们可以将其设置为函数顶部的true
,而不必更改任何其他内容,但上面的更改是旧perl的一般解决方案。)
示例输出:
$ original-perl -pe '' a b c 'date|'
Can't open a: No such file or directory
Can't open b: No such file or directory.
Can't open c: No such file or directory.
Mon Sep 21 18:41:37 PDT 2015
$ modified-perl -pe '' a b c 'date|'
Can't open a: No such file or directory.
Can't open b: No such file or directory.
Can't open c: No such file or directory.
Can't open date|: No such file or directory.
答案 2 :(得分:1)
您可以将ARGV::readonly
与alias
结合使用:
alias perl='perl -MARGV::readonly'
或者只是教育那些正在输入“坏”oneliners的人。