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

Could someone elucidate on why when I'm taint checking a scalar:

($filename) = $filename =~ m{([\w./]+)}smx or croak "Bah!";
Then passing that to an objects method:
$object->method( $filename );

why $filename gets tainted in side of the method? I'm under the impression that passing a taint checked scalar should leave $filename untainted while it's in the objects method.

In particular. I'm using Net::SFTP::Foreign to
$sftp->get( $remote, $local );

Where $local has been checked as above. When running with -T I get:

Insecure dependency in chmod while running with -T switch at ../Net/SFTP/Foreign.pm line 373.

-T is a must have.

Replies are listed 'Best First'.
Re: Passing taint checked scalars
by Joost (Canon) on Feb 03, 2006 at 23:30 UTC
Re: Passing taint checked scalars
by mnology (Acolyte) on Feb 04, 2006 at 00:34 UTC
    Actually this seems to be a problem with Net::SFTP::Foreign's get method not checking $mode. Not $local. When $sftp->get open's the file for writing, it should complain about taintedness on then FH open. Not during the chmod.

    The culprit appears to be $mode being passed to chmod unchecked. The method which get's $mode uses an Open2 call to get remote file stats. stat* doesn't appear to be taint checked after it's returned from the system.

Re: Passing taint checked scalars
by Anonymous Monk on Feb 04, 2006 at 05:47 UTC
    I could be way off base (usually am), but I thought the only way to untaint a variable was to use regex 'special' variables $1,$2, etc... Thus the following is what comes to mind:
    $filename =~ m{([\w./]+)}smx ? $filename = $1 : croak "Bah!";
    Ignorance is not bliss, educate me please.
      Let's walk through the OP's code snippet then:

      $filename =~ m{([\w./]+)}smx is a pattern match with capturing parentheses in list context, evaluating to a list of the captured strings on success, otherwise an empty list. ($filename) = ... is the list assignment that gives our pattern match its context (note the parentheses) and stores the result of the match back in $filename. If the match fails, an empty list is assigned and that way $filename becomes undefined.

      Now this whole assignment is the left hand side of a low-precedence or operation. The right hand side only gets evaluated if the left hand side evaluates to boolean false. A list assignment evaluates to true if and only if the list of things ready to be assigned is not empty, which is the case here if the pattern match succeeded.

      The last part, croak "Bah!" will thus only be called if the pattern match failed. On success, $filename is untainted and stripped of all extra characters outside the match. The first element of the matching list is exactly what also could have been accessed as $1. There you are.

      By the way, although this was some way to perform a taint check, the regular expression used here is not very effective to avoid danger. A user-supplied filename like ../../../etc/passwd, for example, would pass the check with nobody the wiser.

        Assuming I'm wish to enforce canonical paths, I believe:

        ($filename) = $filename =~ m{\A (/ ([\w./]+) )}smx and $filename !~ m{\.{2}\/}smx or die 'Bad filename';

        Covers me. Correct? Is there a more elegant way to do this?