http://www.perlmonks.org?node_id=11113990

AppleFritter has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed monks,

I'm sure this has been asked (and answered before), but I can't seem to find said question. I'd like to call from within Perl an external program, passing it some arguments, and capture its output. Usually I'd reach for backticks or the qx// operator, but the arguments that need to be passed come from user-supplied data, and while the program being called itself should be safe to invoke, there's the issue of the shell and its shenanigans.

To give a bit more context, I'm working with a TeX installation and need to call kpsewhich (a wrapper around the kpathsea library, which will help you locate various files that TeX will make use of). So I'd want to get the output of, say, kpsewhich cmr10.tfm; but the name of the file I'm looking up comes from a user-supplied file I have no control over, and I'd rather not feed kpsewhich cmr10.tfm ; evil_things_go_here to the shell. (You get the idea.)

As far as I'm aware system and exec have "safe" invocations that will avoid the shell (even on braindead OSes, like Windows). Does qx//? Or for that matter, is there another (different, possibly better) way to locate TeX's files? A Perl wrapper for the kpathsea library, perhaps? (This manpage hints that such a thing exists, but it's not on CPAN AFAICT.)

Thanks.

Replies are listed 'Best First'.
Re: Safely capturing the output of an external program
by dasgar (Priest) on Mar 09, 2020 at 04:00 UTC

    For running an external program, I usually grab Capture::Tiny. The only times I have issues with Capture::Tiny is when using threads and that's due to Capture::Tiny not being thread safe.

      Right, so this can be used to wrap a call to system to capture the output, and system can be entirely de-shelled, as it were. That might work. Thank you!
Re: Safely capturing the output of an external program
by haukex (Archbishop) on Mar 09, 2020 at 07:44 UTC
    the arguments that need to be passed come from user-supplied data, and while the program being called itself should be safe to invoke, there's the issue of the shell and its shenanigans ... system and exec have "safe" invocations ... Does qx//?

    No, not in the core*. I wrote about exactly this issue here: In this case, I might recommend IPC::System::Simple's capturex as a safer drop-in replacement for qx//, or IPC::Run3 if you want to capture STDERR as well.

    * Update: Ok, well, not quite right, there are piped opens with more than one argument, but those require one to be careful to get right, and didn't work on Windows for quite some time. The modules just make this much easier.

      Not being in the core is fine. And this, from IPC::System::Simple's documentation,

      Better still, you can even use capturex() to run the equivalent of backticks, but without the shell:

      is exactly what I wanted to read. Thanks!

        I'd like to offer another alternative, my IPC::System::Options which exports readpipe() and lets you use the standard backtick operator but with the option of not having to use shell.
Re: Safely capturing the output of an external program
by LanX (Saint) on Mar 09, 2020 at 00:00 UTC
    update

    Nevermind I misread your question as already having the path. Sorry.


    update

    Maybe have a look at IPC::Open3 and IPC::Run

    The latter is explicitly talking about avoiding the shell and both offer passing arguments explicitly.

    Untested!


    Hi

    I'm not aware of safe placeholder invocations, and the variety of possible CLI arguments is huge.

    But you could consider to examine and untaint your file argument.

    -e $file should tell you if it exists (hence not work with evil injections) and examining the path should tell you if it's inside an allowed location.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      Untainting might work though; if the filename matches, say, q/^[A-Za-z0-9]+\.tfm$/, it's probably safe to pass it through any shell. But I've never liked that approach, and "probably" is a dangerous word.
        > Untainting might work though;

        In this case I'd additionally surround arguments with 'singlequotes' .

        Your untainting demo is explicitly forbidding quotes, in other cases escape them.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Safely capturing the output of an external program
by stevieb (Canon) on Mar 09, 2020 at 00:16 UTC

    What are the command line commands you're sending?

    For searching for files if that's all you're doing, I like File::Find::Rule.

      The command would be something like kpsewhich cmr10.tfm. I'd prefer to use kpsewhich, the kpathsea library it wraps, or whatever other standard methods may exist in TeX for finding files, so as to ensure my script'll find the same files that TeX does; but if all else fails I'll try File::Find::Rule, thanks.