Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
TaintednessLoopVisitor
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
1 / 1
 visitForeach
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace SecurityCheckPlugin;
4
5use ast\Node;
6use Phan\PluginV3\BeforeLoopBodyAnalysisVisitor;
7
8class TaintednessLoopVisitor extends BeforeLoopBodyAnalysisVisitor {
9    use TaintednessBaseVisitor;
10
11    /**
12     * Visit a foreach loop
13     *
14     * We do that in this visitor so that we can handle the loop condition prior to
15     * determine the taint of the loop variable, prior to evaluating the loop body.
16     * See https://github.com/phan/phan/issues/3936
17     *
18     * @param Node $node
19     */
20    public function visitForeach( Node $node ): void {
21        $expr = $node->children['expr'];
22        $lhsTaintednessWithError = $this->getTaintedness( $expr );
23        $lhsTaintedness = $lhsTaintednessWithError->getTaintedness();
24        $lhsLinks = $lhsTaintednessWithError->getMethodLinks();
25
26        $value = $node->children['value'];
27        if ( $value->kind === \ast\AST_REF ) {
28            // TODO, this doesn't propagate the taint to the outer scope
29            // (FWIW, phan doesn't do much better with types, https://github.com/phan/phan/issues/4017)
30            $value = $value->children['var'];
31        }
32
33        $valueTaint = $lhsTaintedness->asValueFirstLevel();
34        $valueError = $lhsTaintednessWithError->getError()->asAllValueFirstLevel();
35        $valueLinks = $lhsLinks->asValueFirstLevel();
36        // TODO Actually compute this
37        $rhsIsArray = false;
38        // NOTE: As mentioned in test 'foreach', we won't be able to retroactively attribute
39        // the right taint to the value if we discover what the key is for the current iteration
40        $valueVisitor = new TaintednessAssignVisitor(
41            $this->code_base,
42            $this->context,
43            $valueTaint,
44            $valueError,
45            $valueLinks,
46            $valueTaint,
47            $valueLinks,
48            $rhsIsArray
49        );
50        $valueVisitor( $value );
51
52        $key = $node->children['key'] ?? null;
53        if ( $key instanceof Node ) {
54            $keyTaint = $lhsTaintedness->asKeyForForeach();
55            $keyError = $lhsTaintednessWithError->getError()->asAllKeyForForeach();
56            $keyLinks = $lhsLinks->asKeyForForeach();
57            $keyVisitor = new TaintednessAssignVisitor(
58                $this->code_base,
59                $this->context,
60                $keyTaint,
61                $keyError,
62                $keyLinks,
63                $keyTaint,
64                $keyLinks,
65                $rhsIsArray
66            );
67            $keyVisitor( $key );
68        }
69    }
70}