Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
PreTaintednessVisitor
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
6 / 6
16
100.00% covered (success)
100.00%
1 / 1
 visitFuncDecl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitClosure
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitArrowFunc
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitMethod
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
11
 visitAssignOp
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 visitPropElem
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace SecurityCheckPlugin;
4
5use ast\Node;
6use Phan\Language\Element\Parameter;
7use Phan\PluginV3\PluginAwarePreAnalysisVisitor;
8
9/**
10 * Class for visiting any nodes we want to handle in pre-order.
11 *
12 * Unlike TaintednessVisitor, this is solely used to set taint
13 * on variable objects, and not to determine the taint of the
14 * current node, so this class does not return anything.
15 *
16 * Copyright (C) 2017  Brian Wolff <bawolff@gmail.com>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 */
32class PreTaintednessVisitor extends PluginAwarePreAnalysisVisitor {
33    use TaintednessBaseVisitor;
34
35    /**
36     * @see visitMethod
37     * @param Node $node
38     */
39    public function visitFuncDecl( Node $node ): void {
40        $this->visitMethod( $node );
41    }
42
43    /**
44     * @see visitMethod
45     * @param Node $node
46     */
47    public function visitClosure( Node $node ): void {
48        $this->visitMethod( $node );
49    }
50
51    /**
52     * @param Node $node
53     */
54    public function visitArrowFunc( Node $node ): void {
55        $this->visitMethod( $node );
56    }
57
58    /**
59     * Set the taintedness of parameters to method/function.
60     *
61     * Parameters that are ints (etc) are clearly safe so
62     * this marks them as such. For other parameters, it
63     * creates a map between the function object and the
64     * parameter object so if anyone later calls the method
65     * with a dangerous argument we can determine if we need
66     * to output a warning.
67     *
68     * Also handles FuncDecl and Closure
69     * @param Node $node
70     */
71    public function visitMethod( Node $node ): void {
72        // var_dump( __METHOD__ ); Debug::printNode( $node );
73        $method = $this->context->getFunctionLikeInScope( $this->code_base );
74        // Initialize retObjs to avoid recursing on methods that don't return anything.
75        self::initRetObjs( $method );
76        $promotedProps = [];
77        if ( $node->kind === \ast\AST_METHOD && $node->children['name'] === '__construct' ) {
78            foreach ( $method->getParameterList() as $i => $param ) {
79                if ( $param->getFlags() & Parameter::PARAM_MODIFIER_FLAGS ) {
80                    $promotedProps[$i] = $this->getPropInCurrentScopeByName( $param->getName() );
81                }
82            }
83        }
84
85        $params = $node->children['params']->children;
86        foreach ( $params as $i => $param ) {
87            $paramName = $param->children['name'];
88            $scope = $this->context->getScope();
89            if ( !$scope->hasVariableWithName( $paramName ) ) {
90                // @codeCoverageIgnoreStart
91                $this->debug( __METHOD__, "Missing variable for param \$" . $paramName );
92                continue;
93                // @codeCoverageIgnoreEnd
94            }
95            $varObj = $scope->getVariableByName( $paramName );
96
97            $paramTypeTaint = $this->getTaintByType( $varObj->getUnionType() );
98            // Initially, the variable starts off with no taint.
99            $startTaint = Taintedness::safeSingleton();
100            // No point in adding a caused-by line here.
101            self::setTaintednessRaw( $varObj, $startTaint );
102
103            if ( !$paramTypeTaint->isSafe() ) {
104                // If the param is not an integer or something, link it to the func
105                $this->linkParamAndFunc( $varObj, $method, $i );
106            }
107            if ( isset( $promotedProps[$i] ) ) {
108                $this->ensureTaintednessIsSet( $promotedProps[$i] );
109                $paramLinks = self::getMethodLinks( $varObj );
110                if ( $paramLinks ) {
111                    $this->mergeTaintDependencies( $promotedProps[$i], $paramLinks, false );
112                }
113                $this->addTaintError( $promotedProps[$i], $startTaint, $paramLinks );
114            }
115        }
116
117        if ( !self::getFuncTaint( $method ) ) {
118            $this->getSetKnownTaintOfFunctionWithoutAnalysis( $method );
119        }
120    }
121
122    /**
123     * Determine whether this operation is safe, based on the operand types. This needs to be done
124     * in preorder because phan infers types from operators, e.g. from `$a += $b` phan will infer
125     * that they're both numbers. We need to use the types of the operands *before* inferring
126     * types from the operator.
127     *
128     * @param Node $node
129     */
130    public function visitAssignOp( Node $node ): void {
131        $lhs = $node->children['var'];
132        $rhs = $node->children['expr'];
133        // @phan-suppress-next-line PhanUndeclaredProperty
134        $node->assignTaintMask = $this->getBinOpTaintMask( $node, $lhs, $rhs );
135    }
136
137    /**
138     * When a class property is declared
139     * @param Node $node
140     */
141    public function visitPropElem( Node $node ): void {
142        $prop = $this->getPropInCurrentScopeByName( $node->children['name'] );
143        $this->ensureTaintednessIsSet( $prop );
144    }
145}