diff --git a/Makefile.PL b/Makefile.PL index e34a44e40..8b3932e63 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -75,9 +75,11 @@ WriteMakefile(NAME => 'LaTeXML', 'URI' => 0, 'version' => 0, # Windows terminal handling (see Common::Error) + # Windows argument escaping (see Util::Pathname) ($^O eq 'MSWin32' ? ('Win32::Console' => 0, - 'Win32::Console::ANSI' => 0) + 'Win32::Console::ANSI' => 0, + 'Win32::ShellQuote' => 0) : ()), # If we have an "old" version of XML::LibXML, # we also need XPathContext. diff --git a/lib/LaTeXML/Package.pm b/lib/LaTeXML/Package.pm index 4b18d62a9..f51100d65 100644 --- a/lib/LaTeXML/Package.pm +++ b/lib/LaTeXML/Package.pm @@ -2016,8 +2016,6 @@ sub FindFile_aux { return $file . '.ltxml' if -f ($file . '.ltxml'); } # No need to search, just check if it exists. return $file if -f $file; # No need to search, just check if it exists. return; } # otherwise we're never going to find it. - elsif (pathname_is_nasty($file)) { # If it is a nasty filename, we won't touch it. - return; } # we DO NOT want to pass this to kpathse or such! # Note that the strategy is complicated by the fact that # (1) we prefer .ltxml bindings, if present @@ -2026,7 +2024,7 @@ sub FindFile_aux { # (4) depending on switches we may EXCLUDE .ltxml OR raw tex OR allow both. # (5) we may allow interpreting raw TeX/sty/whatever files individually or broadly # (6) but we may also want to override an apparently "versioned" file, preferring the ltxml - my $paths = LookupValue('SEARCHPATHS'); + my $paths = LookupValue('SEARCHPATHS') // []; my $urlbase = LookupValue('URLBASE'); my $nopaths = LookupValue('REMOTE_REQUEST'); my $ltxml_paths = $nopaths ? [] : $paths; @@ -2053,8 +2051,7 @@ sub FindFile_aux { # Otherwise, pass on to kpsewhich # Depending on flags, maybe search for ltxml in texmf or for plain tex in ours! # The main point, though, is to we make only ONE (more) call. - return if grep { pathname_is_nasty($_) } @$paths; # SECURITY! No nasty paths in cmdline - # Do we need to sanitize these environment variables? + # Do we need to sanitize these environment variables? my @candidates = (((!$options{noltxml} && !$nopaths) ? ("$file.ltxml") : ()), (!$options{notex} ? ($file) : ())); local $ENV{TEXINPUTS} = join($Config::Config{'path_sep'}, @@ -2138,10 +2135,6 @@ sub FindFile_fallback { else { return; } } -sub pathname_is_nasty { - my ($pathname) = @_; - return $pathname =~ /[^\w\-_\+\=\/\\\.~\:\s]/; } - sub maybeReportSearchPaths { if (LookupValue('SEARCHPATHS_REPORTED')) { return (); } diff --git a/lib/LaTeXML/Util/Pathname.pm b/lib/LaTeXML/Util/Pathname.pm index 829df9606..9407c65fe 100644 --- a/lib/LaTeXML/Util/Pathname.pm +++ b/lib/LaTeXML/Util/Pathname.pm @@ -46,6 +46,13 @@ our @EXPORT = qw( &pathname_find &pathname_findall &pathname_kpsewhich &pathname_cwd &pathname_chdir &pathname_mkdir &pathname_copy &pathname_installation); +my $ISWINDOWS; + +BEGIN { + $ISWINDOWS = $^O =~ /^(MSWin|NetWare|cygwin)/i; + require Win32::ShellQuote if $ISWINDOWS; +} + # NOTE: For absolute pathnames, the directory component starts with # whatever File::Spec considers to be the volume, or "/". #====================================================================== @@ -54,7 +61,6 @@ our @EXPORT = qw( &pathname_find &pathname_findall &pathname_kpsewhich ### my $SEP = '/'; # [CONSTANT] # Some indicators that this is not sufficient? (calls to libraries/externals???) # PRELIMINARY test, probably need to be even more careful -my $ISWINDOWS = $^O =~ /^(MSWin|NetWare|cygwin)/i; my $SEP = ($ISWINDOWS ? '\\' : '/'); # [CONSTANT] my $KPATHSEP = ($ISWINDOWS ? ';' : ':'); # [CONSTANT] my $LITERAL_RE = '(?:literal)(?=:)'; # [CONSTANT] @@ -391,6 +397,8 @@ our $kpse_toolchain = ""; sub pathname_kpsewhich { my (@candidates) = @_; + # ($kpsewhich,@candidates) MUST NOT be empty to guarantee that Perl runs $kpsewhich directly + # rather than through a shell or cmd.exe return unless $kpsewhich && @candidates; build_kpse_cache() unless $kpse_cache; foreach my $file (@candidates) { @@ -400,7 +408,10 @@ sub pathname_kpsewhich { # For multiple calls, this is slower in general. But MiKTeX, eg., doesn't use texmf ls-R files! if ($kpse_toolchain) { push(@candidates, $kpse_toolchain); } - if ($kpsewhich && open(my $resfh, '-|', $kpsewhich, @candidates)) { + if ($kpsewhich && open(my $resfh, '-|', + # on Windows, ($kpsewhich, @candidates) is joined into a single string, which most binaries + # parse with CommandLineToArgvW, so the arguments must be escaped accordingly + $ISWINDOWS ? Win32::ShellQuote::quote_system_list($kpsewhich, @candidates) : ($kpsewhich, @candidates))) { my $result = <$resfh>; # we only need the first line { local $/; <$resfh>; } # discard the rest of the output close($resfh); # ignore exit status (only one of @candidates exists, usually)