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