xref: /openbsd-src/gnu/usr.bin/perl/ext/File-Find/t/taint.t (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
16fb12b70Safresh1#!./perl -T
2*5486feefSafresh1
36fb12b70Safresh1use strict;
4f2a19305Safresh1use lib qw( ./t/lib );
5256a93a4Safresh1
6256a93a4Safresh1BEGIN {
7256a93a4Safresh1    require File::Spec;
8256a93a4Safresh1    if ($ENV{PERL_CORE}) {
9256a93a4Safresh1        # May be doing dynamic loading while @INC is all relative
10256a93a4Safresh1        @INC = map { $_ = File::Spec->rel2abs($_); /(.*)/; $1 } @INC;
11256a93a4Safresh1    }
12256a93a4Safresh1    if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'VMS') {
13256a93a4Safresh1        # This is a hack - at present File::Find does not produce native names
14256a93a4Safresh1        # on Win32 or VMS, so force File::Spec to use Unix names.
15256a93a4Safresh1        # must be set *before* importing File::Find
16256a93a4Safresh1        require File::Spec::Unix;
17256a93a4Safresh1        @File::Spec::ISA = 'File::Spec::Unix';
18256a93a4Safresh1    }
19256a93a4Safresh1}
20*5486feefSafresh1
216fb12b70Safresh1use Test::More;
22f2a19305Safresh1use File::Find;
23f2a19305Safresh1use File::Spec;
24f2a19305Safresh1use Cwd;
256fb12b70Safresh1use Testing qw(
266fb12b70Safresh1    create_file_ok
276fb12b70Safresh1    mkdir_ok
286fb12b70Safresh1    symlink_ok
296fb12b70Safresh1    dir_path
306fb12b70Safresh1    file_path
31f2a19305Safresh1    _cleanup_start
326fb12b70Safresh1);
33256a93a4Safresh1use Errno ();
34f2a19305Safresh1use Config;
35f2a19305Safresh1use File::Temp qw(tempdir);
36f2a19305Safresh1
37f2a19305Safresh1BEGIN {
38f2a19305Safresh1    plan(
39f2a19305Safresh1        ${^TAINT}
40f2a19305Safresh1        ? (tests => 48)
41f2a19305Safresh1        : (skip_all => "A perl without taint support")
42f2a19305Safresh1    );
43f2a19305Safresh1}
446fb12b70Safresh1
456fb12b70Safresh1my %Expect_File = (); # what we expect for $_
466fb12b70Safresh1my %Expect_Name = (); # what we expect for $File::Find::name/fullname
476fb12b70Safresh1my %Expect_Dir  = (); # what we expect for $File::Find::dir
486fb12b70Safresh1my ($cwd, $cwd_untainted);
496fb12b70Safresh1
506fb12b70Safresh1BEGIN {
516fb12b70Safresh1    if ($^O ne 'VMS') {
526fb12b70Safresh1        for (keys %ENV) { # untaint ENV
536fb12b70Safresh1            ($ENV{$_}) = $ENV{$_} =~ /(.*)/;
546fb12b70Safresh1        }
556fb12b70Safresh1    }
566fb12b70Safresh1
576fb12b70Safresh1    # Remove insecure directories from PATH
586fb12b70Safresh1    my @path;
596fb12b70Safresh1    my $sep = $Config{path_sep};
606fb12b70Safresh1    foreach my $dir (split(/\Q$sep/,$ENV{'PATH'}))
616fb12b70Safresh1    {
626fb12b70Safresh1        ##
636fb12b70Safresh1        ## Match the directory taint tests in mg.c::Perl_magic_setenv()
646fb12b70Safresh1        ##
656fb12b70Safresh1        push(@path,$dir) unless (length($dir) >= 256
666fb12b70Safresh1                                 or
676fb12b70Safresh1                                 substr($dir,0,1) ne "/"
686fb12b70Safresh1                                 or
696fb12b70Safresh1                                 (stat $dir)[2] & 002);
706fb12b70Safresh1    }
716fb12b70Safresh1    $ENV{'PATH'} = join($sep,@path);
726fb12b70Safresh1}
736fb12b70Safresh1
746fb12b70Safresh1my $symlink_exists = eval { symlink("",""); 1 };
756fb12b70Safresh1
76f2a19305Safresh1my $test_root_dir; # where we are when this test starts
77f2a19305Safresh1my $test_root_dir_tainted = cwd();
78f2a19305Safresh1if ($test_root_dir_tainted =~ /^(.*)$/) {
79f2a19305Safresh1    $test_root_dir = $1;
80f2a19305Safresh1} else {
81f2a19305Safresh1    die "Failed to untaint root dir of test";
82f2a19305Safresh1}
83f2a19305Safresh1ok($test_root_dir,"test_root_dir is set up as expected");
84f2a19305Safresh1my $test_temp_dir = tempdir("FF_taint_t_XXXXXX",CLEANUP=>1);
85f2a19305Safresh1ok($test_temp_dir,"test_temp_dir is set up as expected");
866fb12b70Safresh1
876fb12b70Safresh1my $found;
886fb12b70Safresh1find({wanted => sub { ++$found if $_ eq 'taint.t' },
896fb12b70Safresh1                untaint => 1, untaint_pattern => qr|^(.+)$|}, File::Spec->curdir);
906fb12b70Safresh1
916fb12b70Safresh1is($found, 1, 'taint.t found once');
926fb12b70Safresh1$found = 0;
936fb12b70Safresh1
946fb12b70Safresh1finddepth({wanted => sub { ++$found if $_ eq 'taint.t'; },
956fb12b70Safresh1           untaint => 1, untaint_pattern => qr|^(.+)$|}, File::Spec->curdir);
966fb12b70Safresh1
976fb12b70Safresh1is($found, 1, 'taint.t found once again');
986fb12b70Safresh1
996fb12b70Safresh1my $case = 2;
1006fb12b70Safresh1my $FastFileTests_OK = 0;
1016fb12b70Safresh1
102f2a19305Safresh1my $chdir_error = "";
103f2a19305Safresh1chdir($test_temp_dir)
104f2a19305Safresh1    or $chdir_error = "Failed to chdir to '$test_temp_dir': $!";
105f2a19305Safresh1is($chdir_error,"","chdir to temp dir '$test_temp_dir' successful")
106f2a19305Safresh1    or die $chdir_error;
107f2a19305Safresh1
1086fb12b70Safresh1sub cleanup {
109f2a19305Safresh1    # the following chdirs into $test_root_dir/$test_temp_dir but
110f2a19305Safresh1    # handles various possible edge case errors cleanly. If it returns
111f2a19305Safresh1    # false then we bail out of the cleanup.
112f2a19305Safresh1    _cleanup_start($test_root_dir, $test_temp_dir)
113f2a19305Safresh1        or return;
114f2a19305Safresh1
1156fb12b70Safresh1    my $need_updir = 0;
116f3efcd01Safresh1    if (-d dir_path('for_find_taint')) {
117f3efcd01Safresh1        $need_updir = 1 if chdir(dir_path('for_find_taint'));
1186fb12b70Safresh1    }
119f3efcd01Safresh1    if (-d dir_path('fa_taint')) {
120f3efcd01Safresh1        unlink file_path('fa_taint', 'fa_ord'),
121f3efcd01Safresh1               file_path('fa_taint', 'fsl'),
122f3efcd01Safresh1               file_path('fa_taint', 'faa', 'faa_ord'),
123f3efcd01Safresh1               file_path('fa_taint', 'fab', 'fab_ord'),
124f3efcd01Safresh1               file_path('fa_taint', 'fab', 'faba', 'faba_ord'),
125f3efcd01Safresh1               file_path('fb_taint', 'fb_ord'),
126f3efcd01Safresh1               file_path('fb_taint', 'fba', 'fba_ord');
127f3efcd01Safresh1        rmdir dir_path('fa_taint', 'faa');
128f3efcd01Safresh1        rmdir dir_path('fa_taint', 'fab', 'faba');
129f3efcd01Safresh1        rmdir dir_path('fa_taint', 'fab');
130f3efcd01Safresh1        rmdir dir_path('fa_taint');
131f3efcd01Safresh1        rmdir dir_path('fb_taint', 'fba');
132f3efcd01Safresh1        rmdir dir_path('fb_taint');
1336fb12b70Safresh1    }
1346fb12b70Safresh1    if ($need_updir) {
1356fb12b70Safresh1        my $updir = $^O eq 'VMS' ? File::Spec::VMS->updir() : File::Spec->updir;
1366fb12b70Safresh1        chdir($updir);
1376fb12b70Safresh1    }
138f3efcd01Safresh1    if (-d dir_path('for_find_taint')) {
139f3efcd01Safresh1        rmdir dir_path('for_find_taint') or print "# Can't rmdir for_find_taint: $!\n";
1406fb12b70Safresh1    }
141f2a19305Safresh1    chdir($test_root_dir) or die "Failed to chdir to '$test_root_dir': $!";
1426fb12b70Safresh1}
1436fb12b70Safresh1
1446fb12b70Safresh1END {
1456fb12b70Safresh1    cleanup();
1466fb12b70Safresh1}
1476fb12b70Safresh1
1486fb12b70Safresh1sub wanted_File_Dir {
1496fb12b70Safresh1    print "# \$File::Find::dir => '$File::Find::dir'\t\$_ => '$_'\n";
1506fb12b70Safresh1    s#\.$## if ($^O eq 'VMS' && $_ ne '.'); #
1516fb12b70Safresh1    s/(.dir)?$//i if ($^O eq 'VMS' && -d _);
1526fb12b70Safresh1    ok( $Expect_File{$_}, "found $_ for \$_, as expected" );
1536fb12b70Safresh1    if ( $FastFileTests_OK ) {
1546fb12b70Safresh1        delete $Expect_File{$_}
1556fb12b70Safresh1          unless ( $Expect_Dir{$_} && ! -d _ );
1566fb12b70Safresh1    }
1576fb12b70Safresh1    else {
1586fb12b70Safresh1        delete $Expect_File{$_}
1596fb12b70Safresh1          unless ( $Expect_Dir{$_} && ! -d $_ );
1606fb12b70Safresh1    }
1616fb12b70Safresh1}
1626fb12b70Safresh1
1636fb12b70Safresh1sub wanted_File_Dir_prune {
1646fb12b70Safresh1    &wanted_File_Dir;
1656fb12b70Safresh1    $File::Find::prune=1 if  $_ eq 'faba';
1666fb12b70Safresh1}
1676fb12b70Safresh1
1686fb12b70Safresh1sub simple_wanted {
1696fb12b70Safresh1    print "# \$File::Find::dir => '$File::Find::dir'\n";
1706fb12b70Safresh1    print "# \$_ => '$_'\n";
1716fb12b70Safresh1}
1726fb12b70Safresh1
1736fb12b70Safresh1# Use topdir() to specify a directory path that you want to pass to
1746fb12b70Safresh1# find/finddepth. Historically topdir() differed on Mac OS classic.
1756fb12b70Safresh1
1766fb12b70Safresh1*topdir = \&dir_path;
1776fb12b70Safresh1
1786fb12b70Safresh1# Use file_path_name() to specify a file path that's expected for
1796fb12b70Safresh1# $File::Find::Name (%Expect_Name). Note: When the no_chdir => 1
1806fb12b70Safresh1# option is in effect, $_ is the same as $File::Find::Name. In that
1816fb12b70Safresh1# case, also use this function to specify a file path that's expected
1826fb12b70Safresh1# for $_.
1836fb12b70Safresh1#
1846fb12b70Safresh1# Historically file_path_name differed on Mac OS classic.
1856fb12b70Safresh1
1866fb12b70Safresh1*file_path_name = \&file_path;
1876fb12b70Safresh1
188f2a19305Safresh1##### Create directories, files and symlinks used in testing #####
189f3efcd01Safresh1mkdir_ok( dir_path('for_find_taint'), 0770 );
190f3efcd01Safresh1ok( chdir( dir_path('for_find_taint')), 'successful chdir() to for_find_taint' );
1916fb12b70Safresh1
1926fb12b70Safresh1$cwd = cwd(); # save cwd
1936fb12b70Safresh1( $cwd_untainted ) = $cwd =~ m|^(.+)$|; # untaint it
1946fb12b70Safresh1
195f3efcd01Safresh1mkdir_ok( dir_path('fa_taint'), 0770 );
196f3efcd01Safresh1mkdir_ok( dir_path('fb_taint'), 0770  );
197f3efcd01Safresh1create_file_ok( file_path('fb_taint', 'fb_ord') );
198f3efcd01Safresh1mkdir_ok( dir_path('fb_taint', 'fba'), 0770  );
199f3efcd01Safresh1create_file_ok( file_path('fb_taint', 'fba', 'fba_ord') );
2006fb12b70Safresh1SKIP: {
2016fb12b70Safresh1    skip "Creating symlink", 1, unless $symlink_exists;
202256a93a4Safresh1    if (symlink('../fb_taint','fa_taint/fsl')) {
203256a93a4Safresh1        pass('Created symbolic link' );
204256a93a4Safresh1    }
205256a93a4Safresh1    else {
206256a93a4Safresh1        my $error = 0 + $!;
207256a93a4Safresh1        if ($^O eq "MSWin32" &&
208256a93a4Safresh1            ($error == &Errno::ENOSYS || $error == &Errno::EPERM)) {
209256a93a4Safresh1            $symlink_exists = 0;
210256a93a4Safresh1            skip "symbolic links not available", 1;
211256a93a4Safresh1        }
212256a93a4Safresh1        else {
213256a93a4Safresh1            fail('Created symbolic link');
214256a93a4Safresh1        }
215256a93a4Safresh1    }
2166fb12b70Safresh1}
217f3efcd01Safresh1create_file_ok( file_path('fa_taint', 'fa_ord') );
2186fb12b70Safresh1
219f3efcd01Safresh1mkdir_ok( dir_path('fa_taint', 'faa'), 0770  );
220f3efcd01Safresh1create_file_ok( file_path('fa_taint', 'faa', 'faa_ord') );
221f3efcd01Safresh1mkdir_ok( dir_path('fa_taint', 'fab'), 0770  );
222f3efcd01Safresh1create_file_ok( file_path('fa_taint', 'fab', 'fab_ord') );
223f3efcd01Safresh1mkdir_ok( dir_path('fa_taint', 'fab', 'faba'), 0770  );
224f3efcd01Safresh1create_file_ok( file_path('fa_taint', 'fab', 'faba', 'faba_ord') );
2256fb12b70Safresh1
2266fb12b70Safresh1print "# check untainting (no follow)\n";
2276fb12b70Safresh1
2286fb12b70Safresh1# untainting here should work correctly
2296fb12b70Safresh1
2306fb12b70Safresh1%Expect_File = (File::Spec->curdir => 1, file_path('fsl') =>
2316fb12b70Safresh1                1,file_path('fa_ord') => 1, file_path('fab') => 1,
2326fb12b70Safresh1                file_path('fab_ord') => 1, file_path('faba') => 1,
2336fb12b70Safresh1                file_path('faa') => 1, file_path('faa_ord') => 1);
2346fb12b70Safresh1delete $Expect_File{ file_path('fsl') } unless $symlink_exists;
2356fb12b70Safresh1%Expect_Name = ();
2366fb12b70Safresh1
237f3efcd01Safresh1%Expect_Dir = ( dir_path('fa_taint') => 1, dir_path('faa') => 1,
2386fb12b70Safresh1                dir_path('fab') => 1, dir_path('faba') => 1,
239f3efcd01Safresh1                dir_path('fb_taint') => 1, dir_path('fba') => 1);
2406fb12b70Safresh1
241f3efcd01Safresh1delete @Expect_Dir{ dir_path('fb_taint'), dir_path('fba') } unless $symlink_exists;
2426fb12b70Safresh1
2436fb12b70Safresh1File::Find::find( {wanted => \&wanted_File_Dir_prune, untaint => 1,
244f3efcd01Safresh1                   untaint_pattern => qr|^(.+)$|}, topdir('fa_taint') );
2456fb12b70Safresh1
246256a93a4Safresh1is(scalar keys %Expect_File, 0, 'Found all expected files')
247256a93a4Safresh1    or diag "Not found " . join(" ", sort keys %Expect_File);
2486fb12b70Safresh1
2496fb12b70Safresh1# don't untaint at all, should die
2506fb12b70Safresh1%Expect_File = ();
2516fb12b70Safresh1%Expect_Name = ();
2526fb12b70Safresh1%Expect_Dir  = ();
2536fb12b70Safresh1undef $@;
254f3efcd01Safresh1eval {File::Find::find( {wanted => \&simple_wanted}, topdir('fa_taint') );};
2556fb12b70Safresh1like( $@, qr|Insecure dependency|, 'Tainted directory causes death (good)' );
2566fb12b70Safresh1chdir($cwd_untainted);
2576fb12b70Safresh1
2586fb12b70Safresh1
2596fb12b70Safresh1# untaint pattern doesn't match, should die
2606fb12b70Safresh1undef $@;
2616fb12b70Safresh1
2626fb12b70Safresh1eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1,
2636fb12b70Safresh1                         untaint_pattern => qr|^(NO_MATCH)$|},
264f3efcd01Safresh1                         topdir('fa_taint') );};
2656fb12b70Safresh1
2666fb12b70Safresh1like( $@, qr|is still tainted|, 'Bad untaint pattern causes death (good)' );
2676fb12b70Safresh1chdir($cwd_untainted);
2686fb12b70Safresh1
2696fb12b70Safresh1
2706fb12b70Safresh1# untaint pattern doesn't match, should die when we chdir to cwd
2716fb12b70Safresh1print "# check untaint_skip (No follow)\n";
2726fb12b70Safresh1undef $@;
2736fb12b70Safresh1
2746fb12b70Safresh1eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1,
2756fb12b70Safresh1                         untaint_skip => 1, untaint_pattern =>
276f3efcd01Safresh1                         qr|^(NO_MATCH)$|}, topdir('fa_taint') );};
2776fb12b70Safresh1
2786fb12b70Safresh1print "# $@" if $@;
2796fb12b70Safresh1#$^D = 8;
2806fb12b70Safresh1like( $@, qr|insecure cwd|, 'Bad untaint pattern causes death in cwd (good)' );
2816fb12b70Safresh1
2826fb12b70Safresh1chdir($cwd_untainted);
2836fb12b70Safresh1
2846fb12b70Safresh1
2856fb12b70Safresh1SKIP: {
2866fb12b70Safresh1    skip "Symbolic link tests", 17, unless $symlink_exists;
2876fb12b70Safresh1    print "# --- symbolic link tests --- \n";
2886fb12b70Safresh1    $FastFileTests_OK= 1;
2896fb12b70Safresh1
2906fb12b70Safresh1    print "# check untainting (follow)\n";
2916fb12b70Safresh1
2926fb12b70Safresh1    # untainting here should work correctly
2936fb12b70Safresh1    # no_chdir is in effect, hence we use file_path_name to specify the expected paths for %Expect_File
2946fb12b70Safresh1
295f3efcd01Safresh1    %Expect_File = (file_path_name('fa_taint') => 1,
296f3efcd01Safresh1                    file_path_name('fa_taint','fa_ord') => 1,
297f3efcd01Safresh1                    file_path_name('fa_taint', 'fsl') => 1,
298f3efcd01Safresh1                    file_path_name('fa_taint', 'fsl', 'fb_ord') => 1,
299f3efcd01Safresh1                    file_path_name('fa_taint', 'fsl', 'fba') => 1,
300f3efcd01Safresh1                    file_path_name('fa_taint', 'fsl', 'fba', 'fba_ord') => 1,
301f3efcd01Safresh1                    file_path_name('fa_taint', 'fab') => 1,
302f3efcd01Safresh1                    file_path_name('fa_taint', 'fab', 'fab_ord') => 1,
303f3efcd01Safresh1                    file_path_name('fa_taint', 'fab', 'faba') => 1,
304f3efcd01Safresh1                    file_path_name('fa_taint', 'fab', 'faba', 'faba_ord') => 1,
305f3efcd01Safresh1                    file_path_name('fa_taint', 'faa') => 1,
306f3efcd01Safresh1                    file_path_name('fa_taint', 'faa', 'faa_ord') => 1);
3076fb12b70Safresh1
3086fb12b70Safresh1    %Expect_Name = ();
3096fb12b70Safresh1
310f3efcd01Safresh1    %Expect_Dir = (dir_path('fa_taint') => 1,
311f3efcd01Safresh1                   dir_path('fa_taint', 'faa') => 1,
312f3efcd01Safresh1                   dir_path('fa_taint', 'fab') => 1,
313f3efcd01Safresh1                   dir_path('fa_taint', 'fab', 'faba') => 1,
314f3efcd01Safresh1                   dir_path('fb_taint') => 1,
315f3efcd01Safresh1                   dir_path('fb_taint', 'fba') => 1);
3166fb12b70Safresh1
3176fb12b70Safresh1    File::Find::find( {wanted => \&wanted_File_Dir, follow_fast => 1,
3186fb12b70Safresh1                       no_chdir => 1, untaint => 1, untaint_pattern =>
319f3efcd01Safresh1                       qr|^(.+)$| }, topdir('fa_taint') );
3206fb12b70Safresh1
3216fb12b70Safresh1    is( scalar(keys %Expect_File), 0, 'Found all files in symlink test' );
3226fb12b70Safresh1
3236fb12b70Safresh1
3246fb12b70Safresh1    # don't untaint at all, should die
3256fb12b70Safresh1    undef $@;
3266fb12b70Safresh1
3276fb12b70Safresh1    eval {File::Find::find( {wanted => \&simple_wanted, follow => 1},
328f3efcd01Safresh1                            topdir('fa_taint') );};
3296fb12b70Safresh1
3306fb12b70Safresh1    like( $@, qr|Insecure dependency|, 'Not untainting causes death (good)' );
3316fb12b70Safresh1    chdir($cwd_untainted);
3326fb12b70Safresh1
3336fb12b70Safresh1    # untaint pattern doesn't match, should die
3346fb12b70Safresh1    undef $@;
3356fb12b70Safresh1
3366fb12b70Safresh1    eval {File::Find::find( {wanted => \&simple_wanted, follow => 1,
3376fb12b70Safresh1                             untaint => 1, untaint_pattern =>
338f3efcd01Safresh1                             qr|^(NO_MATCH)$|}, topdir('fa_taint') );};
3396fb12b70Safresh1
3406fb12b70Safresh1    like( $@, qr|is still tainted|, 'Bat untaint pattern causes death (good)' );
3416fb12b70Safresh1    chdir($cwd_untainted);
3426fb12b70Safresh1
3436fb12b70Safresh1    # untaint pattern doesn't match, should die when we chdir to cwd
3446fb12b70Safresh1    print "# check untaint_skip (Follow)\n";
3456fb12b70Safresh1    undef $@;
3466fb12b70Safresh1
3476fb12b70Safresh1    eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1,
3486fb12b70Safresh1                             untaint_skip => 1, untaint_pattern =>
349f3efcd01Safresh1                             qr|^(NO_MATCH)$|}, topdir('fa_taint') );};
3506fb12b70Safresh1    like( $@, qr|insecure cwd|, 'Cwd not untainted with bad pattern (good)' );
3516fb12b70Safresh1
3526fb12b70Safresh1    chdir($cwd_untainted);
3536fb12b70Safresh1}
354