From 6adbbd2d8dd0e19201f22140cbd1b37cd298dd7a Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Thu, 16 Apr 2015 16:11:38 +0200 Subject: [PATCH 1/8] fix this indenting --- lib/Module/Metadata.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Module/Metadata.pm b/lib/Module/Metadata.pm index 191fc41..1295b00 100644 --- a/lib/Module/Metadata.pm +++ b/lib/Module/Metadata.pm @@ -604,8 +604,8 @@ sub _parse_fh { $need_vers = 0 if $version_package eq $package; unless ( defined $vers{$version_package} && length $vers{$version_package} ) { - $vers{$version_package} = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); - } + $vers{$version_package} = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); + } # first non-comment line in undeclared package main is VERSION } elsif ( $package eq 'main' && $version_fullname && !exists($vers{main}) ) { From 1abd4d217dd067c9ee1daad5ba519598b081e1b8 Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Fri, 17 Apr 2015 18:12:06 +0200 Subject: [PATCH 2/8] also test finding the fully-qualified version in the other order --- t/metadata.t | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/t/metadata.t b/t/metadata.t index 5985f0d..6dd523e 100644 --- a/t/metadata.t +++ b/t/metadata.t @@ -122,6 +122,11 @@ $Simple::VERSION = 1.23; package Simple; $Simple2::VERSION = '999'; $VERSION = 1.23; +--- + '1.23' => <<'---', # Differentiate fully qualified $VERSION and unqualified, other order +package Simple; +$VERSION = 1.23; +$Simple2::VERSION = '999'; --- '1.23' => <<'---', # $VERSION declared as package variable from within 'main' package $Simple::VERSION = '1.23'; From 9fecda83d80741c3968dff1750738942a589a2f4 Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Fri, 17 Apr 2015 18:10:43 +0200 Subject: [PATCH 3/8] preserve the original version string(s) before inflating them into objects (not yet exposed as an API) --- lib/Module/Metadata.pm | 49 ++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/Module/Metadata.pm b/lib/Module/Metadata.pm index 1295b00..ed0442a 100644 --- a/lib/Module/Metadata.pm +++ b/lib/Module/Metadata.pm @@ -514,7 +514,7 @@ sub _parse_fh { my ($self, $fh) = @_; my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 ); - my( @packages, %vers, %pod, @pod ); + my( @packages, %vers_raw, %vers, %pod, @pod ); my $package = 'main'; my $pod_sect = ''; my $pod_data = ''; @@ -590,12 +590,8 @@ sub _parse_fh { push( @packages, $package ) unless grep( $package eq $_, @packages ); $need_vers = defined $version ? 0 : 1; - if ( not exists $vers{$package} and defined $version ){ - # Upgrade to a version object. - my $dwim_version = eval { _dwim_version($version) }; - croak "Version '$version' from $self->{filename} does not appear to be valid:\n$line\n\nThe fatal error was: $@\n" - unless defined $dwim_version; # "0" is OK! - $vers{$package} = $dwim_version; + if ( not exists $vers_raw{$package}[0] and defined $version ){ + $vers_raw{$package} = [ $version, $line ]; } # VERSION defined with full package spec, i.e. $Module::VERSION @@ -603,21 +599,21 @@ sub _parse_fh { push( @packages, $version_package ) unless grep( $version_package eq $_, @packages ); $need_vers = 0 if $version_package eq $package; - unless ( defined $vers{$version_package} && length $vers{$version_package} ) { - $vers{$version_package} = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); + unless ( defined $vers_raw{$version_package}[0] && length $vers_raw{$version_package}[0] ) { + $vers_raw{$version_package} = [ $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ), $line ]; } # first non-comment line in undeclared package main is VERSION - } elsif ( $package eq 'main' && $version_fullname && !exists($vers{main}) ) { + } elsif ( $package eq 'main' && $version_fullname && !exists($vers_raw{main}[0]) ) { $need_vers = 0; my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); - $vers{$package} = $v; + $vers_raw{$package} = [ $v, $line ]; push( @packages, 'main' ); # first non-comment line in undeclared package defines package main - } elsif ( $package eq 'main' && !exists($vers{main}) && $line =~ /\w/ ) { + } elsif ( $package eq 'main' && !exists($vers_raw{main}[0]) && $line =~ /\w/ ) { $need_vers = 1; - $vers{main} = ''; + $vers_raw{main} = [ '', $line ]; push( @packages, 'main' ); # only keep if this is the first $VERSION seen @@ -625,8 +621,8 @@ sub _parse_fh { $need_vers = 0; my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); - unless ( defined $vers{$package} && length $vers{$package} ) { - $vers{$package} = $v; + unless ( exists $vers_raw{$package}[0] && length $vers{$package}[0] ) { + $vers_raw{$package} = [ $v, $line ]; } } @@ -636,6 +632,20 @@ sub _parse_fh { $pod{$pod_sect} = $pod_data; } + # Upgrade the found versions into version objects + foreach my $package (keys %vers_raw) { + # watch out for autovivification at the first level of the hash + delete($vers_raw{$package}), next if not exists $vers_raw{$package}[0]; + my $version = eval { _dwim_version($vers_raw{$package}[0]) }; + + croak "Version '$vers_raw{$package}[0]' from $self->{filename} does not appear to be valid:\n$vers_raw{$package}[1]\n\nThe fatal error was: $@\n" + unless defined $version; # "0" is OK! + + $vers_raw{$package} = $vers_raw{$package}[0]; + $vers{$package} = $version; + } + + $self->{versions_raw} = \%vers_raw; $self->{versions} = \%vers; $self->{packages} = \@packages; $self->{pod} = \%pod; @@ -683,14 +693,7 @@ sub _evaluate_version_line { croak "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n" if $@; - # Upgrade it into a version object - my $version = eval { _dwim_version($result) }; - - # FIXME: $eval is not the right thing to print here - croak "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n" - unless defined $version; # "0" is OK! - - return $version; + return $result; } } From db72135139d0ce2a8d93e9f8bacc7ba65771a374 Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Fri, 14 Mar 2014 10:40:45 +0100 Subject: [PATCH 4/8] move version extraction guts to its own file. NO FUNCTIONALITY CHANGES YET --- lib/Module/Metadata.pm | 74 ++++++++--------------- lib/Module/Metadata/ExtractVersion.pm | 84 +++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 lib/Module/Metadata/ExtractVersion.pm diff --git a/lib/Module/Metadata.pm b/lib/Module/Metadata.pm index ed0442a..faef743 100644 --- a/lib/Module/Metadata.pm +++ b/lib/Module/Metadata.pm @@ -10,7 +10,6 @@ package Module::Metadata; # perl modules (assuming this may be expanded in the distant # parrot future to look at other types of modules). -sub __clean_eval { eval $_[0] } use strict; use warnings; @@ -25,6 +24,9 @@ BEGIN { } or *SEEK_SET = sub { 0 } } use version 0.87; +use Module::Metadata::ExtractVersion 'eval_version'; + + BEGIN { if ($INC{'Log/Contextual.pm'}) { require "Log/Contextual/WarnLogger.pm"; # Hide from AutoPrereqs @@ -600,13 +602,23 @@ sub _parse_fh { $need_vers = 0 if $version_package eq $package; unless ( defined $vers_raw{$version_package}[0] && length $vers_raw{$version_package}[0] ) { - $vers_raw{$version_package} = [ $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ), $line ]; + $vers_raw{$version_package} = [ eval_version( + sigil => $version_sigil, + variable_name => $version_fullname, + string => $line, + filename => $self->{filename}, + ), $line ]; } # first non-comment line in undeclared package main is VERSION } elsif ( $package eq 'main' && $version_fullname && !exists($vers_raw{main}[0]) ) { $need_vers = 0; - my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); + my $v = eval_version( + sigil => $version_sigil, + variable_name => $version_fullname, + string => $line, + filename => $self->{filename}, + ); $vers_raw{$package} = [ $v, $line ]; push( @packages, 'main' ); @@ -619,9 +631,14 @@ sub _parse_fh { # only keep if this is the first $VERSION seen } elsif ( $version_fullname && $need_vers ) { $need_vers = 0; - my $v = $self->_evaluate_version_line( $version_sigil, $version_fullname, $line ); - - unless ( exists $vers_raw{$package}[0] && length $vers{$package}[0] ) { + my $v = eval_version( + sigil => $version_sigil, + variable_name => $version_fullname, + string => $line, + filename => $self->{filename}, + ); + + unless ( exists $vers_raw{$package}[0] && length $vers_raw{$package}[0] ) { $vers_raw{$package} = [ $v, $line ]; } } @@ -652,51 +669,6 @@ sub _parse_fh { $self->{pod_headings} = \@pod; } -{ -my $pn = 0; -sub _evaluate_version_line { - my $self = shift; - my( $sigil, $variable_name, $line ) = @_; - - # We compile into a local sub because 'use version' would cause - # compiletime/runtime issues with local() - $pn++; # everybody gets their own package - my $eval = qq{ my \$dummy = q# Hide from _packages_inside() - #; package Module::Metadata::_version::p${pn}; - use version; - sub { - local $sigil$variable_name; - $line; - \$$variable_name - }; - }; - - $eval = $1 if $eval =~ m{^(.+)}s; - - local $^W; - # Try to get the $VERSION - my $vsub = __clean_eval($eval); - # some modules say $VERSION $Foo::Bar::VERSION, but Foo::Bar isn't - # installed, so we need to hunt in ./lib for it - if ( $@ =~ /Can't locate/ && -d 'lib' ) { - local @INC = ('lib',@INC); - $vsub = __clean_eval($eval); - } - warn "Error evaling version line '$eval' in $self->{filename}: $@\n" - if $@; - - (ref($vsub) eq 'CODE') or - croak "failed to build version sub for $self->{filename}"; - - my $result = eval { $vsub->() }; - # FIXME: $eval is not the right thing to print here - croak "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n" - if $@; - - return $result; -} -} - # Try to DWIM when things fail the lax version test in obvious ways { my @version_prep = ( diff --git a/lib/Module/Metadata/ExtractVersion.pm b/lib/Module/Metadata/ExtractVersion.pm new file mode 100644 index 0000000..1d593ef --- /dev/null +++ b/lib/Module/Metadata/ExtractVersion.pm @@ -0,0 +1,84 @@ +package Module::Metadata::ExtractVersion; +# ABSTRACT: Safe parsing of module $VERSION lines + +sub __clean_eval { eval $_[0] } + +use strict; +use warnings; + +our $VERSION = '1.000028'; + +use base 'Exporter'; +our @EXPORT_OK = qw/eval_version/; + +use Carp qw/croak/; +use version 0.87; + +=func eval_version + +Given a (decoded) string (usually a single line) that contains a C<$VERSION> +declaration, this function will evaluate it in a L compartment in a +separate process. If the C<$VERSION> is a valid version string according to +L, it will return it as a string, otherwise, it will return undef. + +=cut + +sub eval_version +{ + my (%args) = @_; + + return _evaluate_version_line( + $args{sigil}, + $args{variable_name}, + $args{string}, + $args{filename}, + ); +} + +# transported directly from Module::Metadata + +{ +my $pn = 0; +sub _evaluate_version_line { + my( $sigil, $variable_name, $line, $filename ) = @_; + + # We compile into a local sub because 'use version' would cause + # compiletime/runtime issues with local() + $pn++; # everybody gets their own package + my $eval = qq{ my \$dummy = q# Hide from _packages_inside() + #; package Module::Metadata::_version::p${pn}; + use version; + sub { + local $sigil$variable_name; + $line; + \$$variable_name + }; + }; + + $eval = $1 if $eval =~ m{^(.+)}s; + + local $^W; + # Try to get the $VERSION + my $vsub = __clean_eval($eval); + # some modules say $VERSION $Foo::Bar::VERSION, but Foo::Bar isn't + # installed, so we need to hunt in ./lib for it + if ( $@ =~ /Can't locate/ && -d 'lib' ) { + local @INC = ('lib',@INC); + $vsub = __clean_eval($eval); + } + warn "Error evaling version line '$eval' in $filename: $@\n" + if $@; + + (ref($vsub) eq 'CODE') or + croak "failed to build version sub for $filename"; + + my $result = eval { $vsub->() }; + # FIXME: $eval is not the right thing to print here + croak "Could not get version from $filename by executing:\n$eval\n\nThe fatal error was: $@\n" + if $@; + + return $result; +} +} + +1; From b69a5a9dd2ad754b7602ab98a9792f5473948177 Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Sat, 15 Mar 2014 16:20:57 +0100 Subject: [PATCH 5/8] extract version much more safely --- Changes | 2 + lib/Module/Metadata.pm | 21 +-- lib/Module/Metadata/ExtractVersion.pm | 214 +++++++++++++++++++------- 3 files changed, 164 insertions(+), 73 deletions(-) diff --git a/Changes b/Changes index f9bc029..494bffe 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,8 @@ Revision history for Module-Metadata {{$NEXT}} + - now extracting module version a Safe compartment in a subprocess + (RT#89283) 1.000027 2015-04-11 00:21:26Z - work around issues with an unconfigured Log::Contextual (Kent Fredric) diff --git a/lib/Module/Metadata.pm b/lib/Module/Metadata.pm index faef743..1fb2e76 100644 --- a/lib/Module/Metadata.pm +++ b/lib/Module/Metadata.pm @@ -602,23 +602,13 @@ sub _parse_fh { $need_vers = 0 if $version_package eq $package; unless ( defined $vers_raw{$version_package}[0] && length $vers_raw{$version_package}[0] ) { - $vers_raw{$version_package} = [ eval_version( - sigil => $version_sigil, - variable_name => $version_fullname, - string => $line, - filename => $self->{filename}, - ), $line ]; + $vers_raw{$version_package} = [ eval_version($line), $line ]; } # first non-comment line in undeclared package main is VERSION } elsif ( $package eq 'main' && $version_fullname && !exists($vers_raw{main}[0]) ) { $need_vers = 0; - my $v = eval_version( - sigil => $version_sigil, - variable_name => $version_fullname, - string => $line, - filename => $self->{filename}, - ); + my $v = eval_version($line); $vers_raw{$package} = [ $v, $line ]; push( @packages, 'main' ); @@ -631,12 +621,7 @@ sub _parse_fh { # only keep if this is the first $VERSION seen } elsif ( $version_fullname && $need_vers ) { $need_vers = 0; - my $v = eval_version( - sigil => $version_sigil, - variable_name => $version_fullname, - string => $line, - filename => $self->{filename}, - ); + my $v = eval_version($line); unless ( exists $vers_raw{$package}[0] && length $vers_raw{$package}[0] ) { $vers_raw{$package} = [ $v, $line ]; diff --git a/lib/Module/Metadata/ExtractVersion.pm b/lib/Module/Metadata/ExtractVersion.pm index 1d593ef..49608d3 100644 --- a/lib/Module/Metadata/ExtractVersion.pm +++ b/lib/Module/Metadata/ExtractVersion.pm @@ -11,74 +11,178 @@ our $VERSION = '1.000028'; use base 'Exporter'; our @EXPORT_OK = qw/eval_version/; -use Carp qw/croak/; -use version 0.87; +use File::Spec; +use File::Temp 0.18; +use IPC::Open3 qw(open3); +use Symbol 'gensym'; + +# Win32 is slow to spawn processes +my $TIMEOUT = $^O eq 'MSWin32' ? 5 : 2; =func eval_version + my $version = eval_version( q[our $VERSION = "1.23"] ); + Given a (decoded) string (usually a single line) that contains a C<$VERSION> declaration, this function will evaluate it in a L compartment in a -separate process. If the C<$VERSION> is a valid version string according to -L, it will return it as a string, otherwise, it will return undef. +separate process. The extracted string is returned; it is B validated +against required syntax for versions at this level, so the caller should +normally do something like C<< version::is_lax($version) >> before proceeding +to use this data. =cut sub eval_version { - my (%args) = @_; - - return _evaluate_version_line( - $args{sigil}, - $args{variable_name}, - $args{string}, - $args{filename}, - ); + my ( $string, $timeout ) = @_; + $timeout = $TIMEOUT unless defined $timeout; + + # what $VERSION are we looking for? + my ( $sigil, $var ) = $string =~ /([\$*])((?:[\w\:\']*)\bVERSION)\b.*\=/; + return unless $sigil && $var; + + # munge string: remove "use version" as we do that already and the "use" + # will get stopped by the Safe compartment + $string =~ s/(?:use|require)\s+version[^;]*/1/; + + # create test file + my $temp = File::Temp->new; + print {$temp} _pl_template( $string, $sigil, $var ); + close $temp; + + my $rc; + my $result; + my $err = gensym; + my $pid = open3(my $in, my $out, $err, $^X, $temp); + my $killer; + if ($^O eq 'MSWin32') { + $killer = fork; + if (!defined $killer) { + die "Can't fork: $!"; + } + elsif ($killer == 0) { + sleep $timeout; + kill 'KILL', $pid; + exit 0; + } + } + my $got = eval { + local $SIG{ALRM} = sub { die "alarm\n" }; + alarm $timeout; + local $/; + $result = readline $out; + my $c = waitpid $pid, 0; + alarm 0; + ( $c != $pid ) || $?; + }; + if ( $@ eq "alarm\n" ) { + kill 'KILL', $pid; + waitpid $pid, 0; + $rc = $?; + } + else { + $rc = $got; + } + if ($killer) { + kill 'KILL', $killer; + waitpid $killer, 0; + } + + return if $rc || !defined $result; # error condition + +## print STDERR "# C<< $string >> --> $result" if $result =~ /^ERROR/; + return if $result =~ /^ERROR/; + + $result =~ s/[\r\n]+\z//; + + # treat '' the same as undef: no version was found + undef $result if $result eq ''; + + return $result; } -# transported directly from Module::Metadata - -{ -my $pn = 0; -sub _evaluate_version_line { - my( $sigil, $variable_name, $line, $filename ) = @_; - - # We compile into a local sub because 'use version' would cause - # compiletime/runtime issues with local() - $pn++; # everybody gets their own package - my $eval = qq{ my \$dummy = q# Hide from _packages_inside() - #; package Module::Metadata::_version::p${pn}; - use version; - sub { - local $sigil$variable_name; - $line; - \$$variable_name +sub _pl_template { + my ( $string, $sigil, $var ) = @_; + return <<"HERE" +use version; +use Safe; +use File::Spec; +open STDERR, '>', File::Spec->devnull; +open STDIN, '<', File::Spec->devnull; + +my \$comp = Safe->new; +\$comp->permit("entereval"); # for MBARBON/Module-Info-0.30.tar.gz +\$comp->share("*version::new"); +\$comp->share("*version::numify"); +\$comp->share_from('main', ['*version::', + '*Exporter::', + '*DynaLoader::']); +\$comp->share_from('version', ['&qv']); + +my \$code = <<'END'; + local $sigil$var; + \$$var = undef; + do { + $string }; - }; - - $eval = $1 if $eval =~ m{^(.+)}s; - - local $^W; - # Try to get the $VERSION - my $vsub = __clean_eval($eval); - # some modules say $VERSION $Foo::Bar::VERSION, but Foo::Bar isn't - # installed, so we need to hunt in ./lib for it - if ( $@ =~ /Can't locate/ && -d 'lib' ) { - local @INC = ('lib',@INC); - $vsub = __clean_eval($eval); - } - warn "Error evaling version line '$eval' in $filename: $@\n" - if $@; - - (ref($vsub) eq 'CODE') or - croak "failed to build version sub for $filename"; - - my $result = eval { $vsub->() }; - # FIXME: $eval is not the right thing to print here - croak "Could not get version from $filename by executing:\n$eval\n\nThe fatal error was: $@\n" - if $@; - - return $result; -} + \$$var; +END + +my \$result = \$comp->reval(\$code); +print "ERROR: \$@\n" if \$@; +exit unless defined \$result; + +eval { \$result = version->parse(\$result)->stringify }; +print \$result; + +HERE } 1; + +=head1 SYNOPSIS + + use Version::Eval qw/eval_version/; + + my $version = eval_version( $unsafe_string ); + +=head1 DESCRIPTION + +Package versions are defined by a string such as this: + + package Foo; + our $VERSION = "1.23"; + +If we want to know the version of a F<.pm> file, we can +load it and check CVERSION> for the package. But that means any +buggy or hostile code in F gets run. + +The safe thing to do is to parse out a string that looks like an assignment +to C<$VERSION> and then evaluate it. But even that could be hostile: + + package Foo; + our $VERSION = do { my $n; $n++ while 1 }; # infinite loop + +This module executes a potential version string in a separate process in +a L compartment with a timeout to avoid as much risk as possible. + +Hostile code might still attempt to consume excessive resources, but the +timeout should limit the problem. + +=head1 SEE ALSO + +=over 4 + +* L + +* L + +* L + +* L + +* L + +=back + +=cut From 8107cc4c1076f5c55f14e96dc5054b412e74143b Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Sun, 12 Apr 2015 18:40:59 -0700 Subject: [PATCH 6/8] give haarg attribution --- lib/Module/Metadata/ExtractVersion.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Module/Metadata/ExtractVersion.pm b/lib/Module/Metadata/ExtractVersion.pm index 49608d3..7ec4804 100644 --- a/lib/Module/Metadata/ExtractVersion.pm +++ b/lib/Module/Metadata/ExtractVersion.pm @@ -185,4 +185,8 @@ timeout should limit the problem. =back +=head1 AUTHOR + +This logic was written by Graham Knop, . + =cut From fe823298651075b17587373ea9dab88ca614f495 Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Sun, 12 Apr 2015 18:42:35 -0700 Subject: [PATCH 7/8] shorter pod lists --- lib/Module/Metadata/ExtractVersion.pm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Module/Metadata/ExtractVersion.pm b/lib/Module/Metadata/ExtractVersion.pm index 7ec4804..d6b67c7 100644 --- a/lib/Module/Metadata/ExtractVersion.pm +++ b/lib/Module/Metadata/ExtractVersion.pm @@ -171,20 +171,13 @@ timeout should limit the problem. =head1 SEE ALSO -=over 4 - +=for :list * L - * L - * L - * L - * L -=back - =head1 AUTHOR This logic was written by Graham Knop, . From 9083544cd897c88961de4071dcbe1af2df826404 Mon Sep 17 00:00:00 2001 From: Karen Etheridge Date: Sat, 15 Mar 2014 16:54:45 +0100 Subject: [PATCH 8/8] fix taint tests --- lib/Module/Metadata/ExtractVersion.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Module/Metadata/ExtractVersion.pm b/lib/Module/Metadata/ExtractVersion.pm index d6b67c7..d739099 100644 --- a/lib/Module/Metadata/ExtractVersion.pm +++ b/lib/Module/Metadata/ExtractVersion.pm @@ -50,10 +50,15 @@ sub eval_version print {$temp} _pl_template( $string, $sigil, $var ); close $temp; + # detaint... + undef $ENV{PATH}; + my $perl = $^X; + $perl = $1 if $perl =~ m{^(.+)}s; + my $rc; my $result; my $err = gensym; - my $pid = open3(my $in, my $out, $err, $^X, $temp); + my $pid = open3(my $in, my $out, $err, $perl, $temp); my $killer; if ($^O eq 'MSWin32') { $killer = fork;