短版本
在下面的代码中,$1
被污染了,我不明白为什么。
长版本
我在启用了-T
污染检查模式的perl v5.14.2系统上运行Foswiki。调试该设置的一个问题时,我设法构建了以下SSCCE。(注意,我编辑了这篇文章,第一个版本更长更复杂,评论仍然提到了这一点。)
#!/usr/bin/perl -T
use strict;
use warnings;
use locale;
use Scalar::Util qw(tainted);
my $var = "foo.bar_baz";
$var =~ m/^(.*)[._](.*?)$/;
print(tainted($1) ? "taintedn" : "untaintedn");
尽管输入字符串$var
未被污染并且正则表达式是固定的,但是得到的捕获组$1
被污染。我觉得很奇怪。
perlsec手册对污点和正则表达式有这样的描述:
通过将值用作哈希中的键,可以对其进行无损处理;否则绕过污点机制的唯一方法是引用正则表达式中的子模式匹配。Perl假定如果您使用
$1
、$2
等引用子字符串,您知道当你写图案的时候。
我可以想象,即使输入被污染,输出也不会被污染。从未污染的输入中观察相反的、受污染的输出,感觉就像perl中的一个奇怪的错误。但是,如果你阅读了更多的perlsec,它也会将用户指向perlscale的SECURITY部分。我们读到:
当使用区域设置生效时,Perl使用污染机制(请参阅perlsec)标记依赖于区域设置的字符串结果,以及其结果可能是不可信的。以下是可能受到以下影响的运算符和函数的污染行为区域设置:
比较运算符(
lt
、le
、ge
、gt
和cmp
)[…]案例映射插值(使用
l
、L
、u
或U
)[…]匹配运算符(
m//
):标量真/假结果从未受到污染。
子模式,作为列表上下文结果或
$1
交付如果使用区域设置(但不是use locale ':not_characters'
)有效,并且子模式正则表达式包含w
(用于匹配字母数字字符)和W
(非字母数字字符)、s
(空白字符)或S
(非空白字符)。匹配模式变量$&
、$`
(预赛)、$'
(赛后)和$+
(最后一场比赛)也是如果使用区域设置有效并且正则表达式包含w
、W
、s
或S
。替换运算符(
s///
)[…]nbsp nbsp nbsp nbsp;〔⋮〕
这看起来应该是一个详尽的列表。我不知道它是如何应用的:我的regex没有使用w
、W
、s
或S
中的任何一个,所以它不应该依赖于语言环境。
有人能解释一下为什么这个代码会污染变量$1
吗?
问题中引用的文档与perl 5.18.1的实际实现之间目前存在差异。问题出在字符类上。文档中提到了w
、s
、W
和S
,这听起来像是一个详尽的列表,而实现几乎影响了[…]
的每一次使用。
正确的解决方案可能介于两者之间:像[[:word:]]
这样的字符类应该有污点,因为这取决于区域设置。我的固定列表不应该。像[a-z]
这样的字符范围取决于排序规则,所以在我个人看来,它们也应该有污点。d
取决于区域设置对数字的看法,因此,即使它既不是迄今为止提到的转义序列之一,也不是带括号的类,它也应该是有污点的。
因此,在我看来,文档和实现都需要修复。Perl开发人员正在研究这个问题。有关进度信息,请查看我提交的perl错误报告。
对于固定的字符列表,一个可行的解决方法似乎是将公式化为析取,即(?:.|_)
而不是[._]
。它更详细,但即使在当前(在我看来是bug)的perl版本中也应该可以使用。