perl从命令行运行单行具有安全隐患.
问题是选项-n/-p触发菱形运算符<>,它使用两个参数形式open,因此文件名包含特殊字符,perl不能按预期工作:
$ perl -pe '' 'uname|'
Linux
Run Code Online (Sandbox Code Playgroud)
或者当文件名开头时更危险>,比如>file.在这种情况下,文件将被截断.
要解决此问题,我们可以:
ARGV::readonly通过自己实现模块之类的功能:perl -pe 'BEGIN{$_.="\0" for @ARGV} ...' ./*
Run Code Online (Sandbox Code Playgroud)
-i选项,perl在处理之前检查文件是否存在.-T选项启用污染模式.我认为所有解决方案都可以解决这个问题,但也会产生副作用.如果我们可以强制perl总是使用open它的论证形式,那将是一个更好的解决方案.
我想我们可以做到这一点,强制perl总是使用你的论证形式open?
注意
这个问题仅适用于perl从命令行运行一个内联的情况,因为(当然)我们总是可以open在Perl脚本中使用三个参数形式.
在我之前的回答中,我假设ARGV文件句柄魔法模拟了 2 参数 open,但实际上并没有通过 Perl 的内置open函数。所以我进一步研究了它,我想我想出了一个解决这个问题的实际解决方案。
Perl_nextargv中的函数doio.c是perl 围绕文件句柄执行I/O 的地方ARGV。
在该函数的while循环内,有两种打开文件句柄的调用。当LIKELY(PL_inplace)为 true 时(即,当使用开关运行 perl 时-i),调用为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));
}
}
Run Code Online (Sandbox Code Playgroud)
修改后的代码看起来像
if (LIKELY(!PL_inplace)) {
if (do_open_raw(gv, PL_oldname, oldlen, O_RDONLY, 0)) {
return IoIFP(GvIOp(gv));
}
}
Run Code Online (Sandbox Code Playgroud)
(nomagicopen当您使用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.
Run Code Online (Sandbox Code Playgroud)