Perl 对用户输入的正则表达式进行编码以用于File::Find::Rule是否安全?

Perl 对用户输入的正则表达式进行编码以用于File::Find::Rule是否安全?,perl,utf-8,Perl,Utf 8,我正在开发一个应用程序,该应用程序在命令行中接受一个正则表达式作为用户的输入,然后应用该正则表达式来查找当前目录下的某些文件。应用程序支持UTF-8输入,并且应该能够找到UTF-8编码的文件名。以下是一个例子: use feature qw(say); use open qw( :std :utf8 ); use strict; use utf8; use warnings; use Encode (); use File::Find::Rule; system 'touch', 'aæ',

我正在开发一个应用程序,该应用程序在命令行中接受一个正则表达式作为用户的输入,然后应用该正则表达式来查找当前目录下的某些文件。应用程序支持UTF-8输入,并且应该能够找到UTF-8编码的文件名。以下是一个例子:

use feature qw(say);
use open qw( :std :utf8 );
use strict;
use utf8;
use warnings;

use Encode ();
use File::Find::Rule;

system 'touch', 'aæ', 'bæ', 'aa'; # some test files, 

my $pat = 'æ$';
my $pat_encode = encode( $pat );
run_test( $pat_encode, 'With encode()' );
run_test( $pat, 'Without encode()' );
my $pat2 = '[æ]$';
my $pat2_encode = encode( $pat2 );
run_test( $pat2_encode, 'With encode()' );

sub encode {
    return Encode::encode('UTF-8', $_[0], Encode::FB_CROAK | Encode::LEAVE_SRC);
}

sub run_test {
    my ( $pat_encode, $test_str ) = @_;

    say $test_str;
    say '-' x length $test_str;
    say "";
    my @files = File::Find::Rule->new->name( qr/$pat_encode/ )->in('.');
    for (@files) {
        $_ = Encode::decode('UTF-8', $_, Encode::FB_CROAK | Encode::LEAVE_SRC );
    }

    say $_ for @files;
}
输出为:

With encode()
-------------

aæ
bæ
Without encode()
----------------

With encode()
-------------

aæ
bæ
 With encode()
-------------

aæ1
Without encode()
----------------

With encode()
-------------

aæ
aæ1
我希望最后一个正则表达式
[æ]$
在编码后不会工作,因为
æ
将扩展到两个字节
0xC3A6
,但不知何故,Perl似乎知道正则表达式是用UTF-8编码的,并为它的工作发挥了一些魔力


我想知道是否有人知道后一个例子为什么起作用,以及是否有其他情况下编码正则表达式不起作用?(因此,我试图决定是否可以使用或是否应该切换到允许我避免对正则表达式进行编码的模式。)

结果表明,对正则表达式进行编码是不安全的。特别是,如果括号表达式后跟一个或多个字符,正则表达式可能会选择不需要的文件。原因是UTF-8编码版本中只有一个字节与括号表达式匹配。考虑一下我的脚本的修改:

system 'touch', 'aæ', 'aæ1', 'aa'; # some test files, 

my $pat = 'æ.$';
my $pat_encode = encode( $pat );
run_test( $pat_encode, 'With encode()' );
run_test( $pat, 'Without encode()' );
my $pat2 = '[æ].$';
my $pat2_encode = encode( $pat2 );
run_test( $pat2_encode, 'With encode()' );
现在,这应该只返回文件
aæ1
,但是
$pat2
regex也将返回
,因为括号表达式只会使用编码的
æ
的两个字节中的第一个,而最后一个字节将由
$pat2
中的尾部
匹配

输出为:

With encode()
-------------

aæ
bæ
Without encode()
----------------

With encode()
-------------

aæ
bæ
 With encode()
-------------

aæ1
Without encode()
----------------

With encode()
-------------

aæ
aæ1
解决方案似乎是使用:

use File::Find ();

system 'touch', 'aæ', 'aæ1', 'aa'; # some test files, 

my $pat = '[æ].$';
my $files = find_files( $pat );

say $_ for @$files;

sub decode {
    return Encode::decode('UTF-8', $_[0], Encode::FB_CROAK | Encode::LEAVE_SRC );
}

sub find_files {
    my ( $pat ) = @_;

    my @files;
    File::Find::find( sub { wanted( $pat, \@files ) }, '.' );
    return \@files;
}

sub wanted {
    my ( $pat, $files ) = @_;
    my $name = decode( $_ );
    my $full_name = decode( $File::Find::name );
    push @$files, $full_name if $name =~ /$pat/;
}
现在输出正确:

./aæ1
更新

事实上,毕竟可以使用
File::Find::Rule
。只需使用
exec
规则而不是
name
规则:

my $pat = '[æ].$';
my $files = find_files( $pat );

say for @$files;

sub find_files {
    my ( $pat ) = @_;

    my @files = File::Find::Rule->new->exec( sub { wanted( $pat ) } )->in('.');
    for (@files) {
        $_ = decode( $_ );
    }
    return \@files;
}

sub wanted {
    my ( $pat ) = @_;
    my $name = decode( $_ );
    return ( $name =~ /$pat/ ) ? 1 : 0;
}
现在输出为:

aæ1