Skip to content

Commit 2ab4b4d

Browse files
committed
Fall back to CSIDL_LOCAL_APPDATA under taint on Windows
When File::Spec->tmpdir gives an unwritable directory on Windows when in taint mode, this attempts to use a directory in the local appdata folder instead. See https://rt.cpan.org/Ticket/Display.html?id=60340
1 parent 2509893 commit 2ab4b4d

File tree

4 files changed

+68
-12
lines changed

4 files changed

+68
-12
lines changed

lib/File/Temp.pm

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,7 +1431,7 @@ sub tempfile {
14311431

14321432
} elsif ($options{TMPDIR}) {
14331433

1434-
$template = File::Spec->catfile(File::Spec->tmpdir, $template );
1434+
$template = File::Spec->catfile(_wrap_file_spec_tmpdir(), $template );
14351435

14361436
}
14371437

@@ -1443,7 +1443,7 @@ sub tempfile {
14431443

14441444
} else {
14451445

1446-
$template = File::Spec->catfile(File::Spec->tmpdir, TEMPXXX);
1446+
$template = File::Spec->catfile(_wrap_file_spec_tmpdir(), TEMPXXX);
14471447

14481448
}
14491449

@@ -1506,6 +1506,58 @@ sub tempfile {
15061506

15071507
}
15081508

1509+
# On Windows under taint mode, File::Spec could suggest "C:\" as a tempdir
1510+
# which might not be writable. If that is the case, we fallback to a
1511+
# user directory. See https://rt.cpan.org/Ticket/Display.html?id=60340
1512+
1513+
{
1514+
my ($alt_tmpdir, $checked);
1515+
1516+
sub _wrap_file_spec_tmpdir {
1517+
return File::Spec->tmpdir unless $^O eq "MSWin32" && ${^TAINT};
1518+
1519+
if ( $checked ) {
1520+
return $alt_tmpdir ? $alt_tmpdir : File::Spec->tmpdir;
1521+
}
1522+
1523+
# probe what File::Spec gives and find a fallback
1524+
my $xxpath = _replace_XX( "X" x 10, 0 );
1525+
1526+
# First, see if File::Spec->tmpdir is writable
1527+
my $tmpdir = File::Spec->tmpdir;
1528+
my $testpath = File::Spec->catdir( $tmpdir, $xxpath );
1529+
if (mkdir( $testpath, 0700) ) {
1530+
$checked = 1;
1531+
rmdir $testpath;
1532+
return $tmpdir;
1533+
}
1534+
1535+
# Next, see if CSIDL_LOCAL_APPDATA is writable
1536+
require Win32;
1537+
my $local_app = File::Spec->catdir(
1538+
Win32::GetFolderPath( Win32::CSIDL_LOCAL_APPDATA() ), 'Temp'
1539+
);
1540+
$testpath = File::Spec->catdir( $local_app, $xxpath );
1541+
if ( -e $local_app or mkdir( $local_app, 0700 ) ) {
1542+
if (mkdir( $testpath, 0700) ) {
1543+
$checked = 1;
1544+
rmdir $testpath;
1545+
return $alt_tmpdir = $local_app;
1546+
}
1547+
}
1548+
1549+
# Can't find something writable
1550+
croak << "HERE";
1551+
Couldn't find a writable temp directory in taint mode. Tried:
1552+
$tmpdir
1553+
$local_app
1554+
1555+
Try setting and untainting the TMPDIR environment variable.
1556+
HERE
1557+
1558+
}
1559+
}
1560+
15091561
=item B<tempdir>
15101562
15111563
This is the recommended interface for creation of temporary
@@ -1620,7 +1672,7 @@ sub tempdir {
16201672
} elsif ($options{TMPDIR}) {
16211673

16221674
# Prepend tmpdir
1623-
$template = File::Spec->catdir(File::Spec->tmpdir, $template);
1675+
$template = File::Spec->catdir(_wrap_file_spec_tmpdir(), $template);
16241676

16251677
}
16261678

@@ -1634,7 +1686,7 @@ sub tempdir {
16341686

16351687
} else {
16361688

1637-
$template = File::Spec->catdir(File::Spec->tmpdir, TEMPXXX);
1689+
$template = File::Spec->catdir(_wrap_file_spec_tmpdir(), TEMPXXX);
16381690

16391691
}
16401692

@@ -1901,8 +1953,9 @@ Current API available since 0.05.
19011953
sub tmpnam {
19021954

19031955
# Retrieve the temporary directory name
1904-
my $tmpdir = File::Spec->tmpdir;
1956+
my $tmpdir = _wrap_file_spec_tmpdir();
19051957

1958+
# XXX I don't know under what circumstances this occurs, -- xdg 2016-04-02
19061959
croak "Error temporary directory is not writable"
19071960
if $tmpdir eq '';
19081961

@@ -2490,7 +2543,10 @@ destruction), then you will get a warning from File::Path::rmtree().
24902543
=head2 Taint mode
24912544
24922545
If you need to run code under taint mode, updating to the latest
2493-
L<File::Spec> is highly recommended.
2546+
L<File::Spec> is highly recommended. On Windows, if the directory
2547+
given by L<File::Spec::tmpdir> isn't writable, File::Temp will attempt
2548+
to fallback to the user's local application data directory or croak
2549+
with an error.
24942550
24952551
=head2 BINMODE
24962552

t/mktemp.t

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ok(1);
1616
# MKSTEMP - test
1717

1818
# Create file in temp directory
19-
my $template = File::Spec->catfile(File::Spec->tmpdir, 'wowserXXXX');
19+
my $template = File::Spec->catfile(File::Temp::_wrap_file_spec_tmpdir(), 'wowserXXXX');
2020

2121
(my $fh, $template) = mkstemp($template);
2222

@@ -87,7 +87,7 @@ if ($status) {
8787
# MKDTEMP
8888
# Temp directory
8989

90-
$template = File::Spec->catdir(File::Spec->tmpdir, 'tmpdirXXXXXX');
90+
$template = File::Spec->catdir(File::Temp::_wrap_file_spec_tmpdir(), 'tmpdirXXXXXX');
9191

9292
my $tmpdir = mkdtemp($template);
9393

@@ -101,7 +101,7 @@ rmtree($tmpdir);
101101
# MKTEMP
102102
# Just a filename, not opened
103103

104-
$template = File::Spec->catfile(File::Spec->tmpdir, 'mytestXXXXXX');
104+
$template = File::Spec->catfile(File::Temp::_wrap_file_spec_tmpdir(), 'mytestXXXXXX');
105105

106106
my $tmpfile = mktemp($template);
107107

t/security.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ sub test_security {
7777
# Create the tempfile
7878
my $template = "tmpXXXXX";
7979
my ($fh1, $fname1) = eval { tempfile ( $template,
80-
DIR => File::Spec->tmpdir,
80+
DIR => File::Temp::_wrap_file_spec_tmpdir(),
8181
UNLINK => 1,
8282
);
8383
};
@@ -89,7 +89,7 @@ sub test_security {
8989
push(@files, $fname1); # store for end block
9090
} elsif (File::Temp->safe_level() != File::Temp::STANDARD) {
9191
chomp($@);
92-
my $msg = File::Spec->tmpdir() . " possibly insecure: $@";
92+
my $msg = File::Temp::_wrap_file_spec_tmpdir() . " possibly insecure: $@";
9393
skip $msg, 2; # one here and one in END
9494
} else {
9595
ok(0);

t/tempfile.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ push( @still_there, File::Spec->rel2abs($tempfile) ); # check at END
154154
# on NFS
155155
# Try to do what we can.
156156
# Tempfile croaks on error so we need an eval
157-
$fh = eval { tempfile( 'ftmpXXXXX', DIR => File::Spec->tmpdir ) };
157+
$fh = eval { tempfile( 'ftmpXXXXX', DIR => File::Temp::_wrap_file_spec_tmpdir() ) };
158158

159159
if ($fh) {
160160

0 commit comments

Comments
 (0)