Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
70.59% covered (warning)
70.59%
36 / 51
53.85% covered (warning)
53.85%
14 / 26
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReturnObjectsCollectVisitor
70.59% covered (warning)
70.59%
36 / 51
53.85% covered (warning)
53.85%
14 / 26
86.88
0.00% covered (danger)
0.00%
0 / 1
 collectFromNode
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 visitProp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitNullsafeProp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 visitStaticProp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 visitVar
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitClosureVar
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handleVarNode
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
3.33
 visitEncapsList
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 visitArray
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 visitArrayElem
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 visitCast
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 visitDim
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 visitUnaryOp
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 visitBinaryOp
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 visitConditional
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
 visitCall
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitMethodCall
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitStaticCall
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitNullsafeMethodCall
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handleCall
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 visitPreDec
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 visitPreInc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 visitPostDec
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 visitPostInc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handleIncOrDec
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 handleReturnedObject
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace SecurityCheckPlugin;
4
5use ast\Node;
6use Phan\Exception\IssueException;
7use Phan\Exception\NodeException;
8use Phan\Language\Element\TypedElementInterface;
9use Phan\Language\Element\Variable;
10use Phan\PluginV3\PluginAwareBaseAnalysisVisitor;
11
12/**
13 * Given a return statements, return a list of phan objects that are returned by it.
14 *
15 * @todo This should also do what matchTaintToParam currently does
16 */
17class ReturnObjectsCollectVisitor extends PluginAwareBaseAnalysisVisitor {
18    use TaintednessBaseVisitor;
19
20    /** @var TypedElementInterface[] */
21    private $buffer = [];
22
23    /**
24     * @param Node $node
25     * @return TypedElementInterface[]
26     */
27    public function collectFromNode( Node $node ): array {
28        assert( $node->kind === \ast\AST_RETURN );
29        $this->buffer = [];
30        $this( $node->children['expr'] );
31        return $this->buffer;
32    }
33
34    /**
35     * @inheritDoc
36     */
37    public function visitProp( Node $node ): void {
38        $this->handleReturnedObject( $this->getPropFromNode( $node ) );
39    }
40
41    /**
42     * @inheritDoc
43     */
44    public function visitNullsafeProp( Node $node ): void {
45        $this->handleReturnedObject( $this->getPropFromNode( $node ) );
46    }
47
48    /**
49     * @inheritDoc
50     */
51    public function visitStaticProp( Node $node ): void {
52        $this->handleReturnedObject( $this->getPropFromNode( $node ) );
53    }
54
55    /**
56     * @inheritDoc
57     */
58    public function visitVar( Node $node ): void {
59        $this->handleVarNode( $node );
60    }
61
62    /**
63     * @inheritDoc
64     */
65    public function visitClosureVar( Node $node ): void {
66        // FIXME Is this needed?
67        $this->handleVarNode( $node );
68    }
69
70    /**
71     * @param Node $node
72     */
73    private function handleVarNode( Node $node ): void {
74        $cn = $this->getCtxN( $node );
75        if ( Variable::isHardcodedGlobalVariableWithName( $cn->getVariableName() ) ) {
76            return;
77        }
78        try {
79            $this->handleReturnedObject( $cn->getVariable() );
80        } catch ( NodeException | IssueException $e ) {
81            $this->debug( __METHOD__, "variable not in scope?? " . $this->getDebugInfo( $e ) );
82        }
83    }
84
85    /**
86     * @inheritDoc
87     */
88    public function visitEncapsList( Node $node ): void {
89        foreach ( $node->children as $child ) {
90            if ( $child instanceof Node ) {
91                $this( $child );
92            }
93        }
94    }
95
96    /**
97     * @inheritDoc
98     */
99    public function visitArray( Node $node ): void {
100        foreach ( $node->children as $child ) {
101            if ( $child instanceof Node ) {
102                $this( $child );
103            }
104        }
105    }
106
107    /**
108     * @inheritDoc
109     */
110    public function visitArrayElem( Node $node ): void {
111        if ( $node->children['key'] instanceof Node ) {
112            $this( $node->children['key'] );
113        }
114        if ( $node->children['value'] instanceof Node ) {
115            $this( $node->children['value'] );
116        }
117    }
118
119    /**
120     * @inheritDoc
121     */
122    public function visitCast( Node $node ): void {
123        // Future todo might be to ignore casts to ints, since
124        // such things should be safe. Unclear if that makes
125        // sense in all circumstances.
126        if ( $node->children['expr'] instanceof Node ) {
127            $this( $node->children['expr'] );
128        }
129    }
130
131    /**
132     * @inheritDoc
133     */
134    public function visitDim( Node $node ): void {
135        if ( $node->children['expr'] instanceof Node ) {
136            // For now just consider the outermost array.
137            // FIXME. doesn't handle tainted array keys!
138            $this( $node->children['expr'] );
139        }
140    }
141
142    /**
143     * @inheritDoc
144     */
145    public function visitUnaryOp( Node $node ): void {
146        if ( $node->children['expr'] instanceof Node ) {
147            $this( $node->children['expr'] );
148        }
149    }
150
151    /**
152     * @inheritDoc
153     */
154    public function visitBinaryOp( Node $node ): void {
155        if ( $node->children['left'] instanceof Node ) {
156            $this( $node->children['left'] );
157        }
158        if ( $node->children['right'] instanceof Node ) {
159            $this( $node->children['right'] );
160        }
161    }
162
163    /**
164     * @inheritDoc
165     */
166    public function visitConditional( Node $node ): void {
167        if ( $node->children['true'] instanceof Node ) {
168            $this( $node->children['true'] );
169        }
170        if ( $node->children['false'] instanceof Node ) {
171            $this( $node->children['false'] );
172        }
173    }
174
175    /**
176     * @inheritDoc
177     */
178    public function visitCall( Node $node ): void {
179        $this->handleCall( $node );
180    }
181
182    /**
183     * @inheritDoc
184     */
185    public function visitMethodCall( Node $node ): void {
186        $this->handleCall( $node );
187    }
188
189    /**
190     * @inheritDoc
191     */
192    public function visitStaticCall( Node $node ): void {
193        $this->handleCall( $node );
194    }
195
196    /**
197     * @inheritDoc
198     */
199    public function visitNullsafeMethodCall( Node $node ): void {
200        $this->handleCall( $node );
201    }
202
203    /**
204     * @param Node $node @phan-unused-param
205     */
206    private function handleCall( Node $node ): void {
207        // TODO If the func being called already has retObjs, we might add them.
208    }
209
210    /**
211     * @inheritDoc
212     */
213    public function visitPreDec( Node $node ): void {
214        $this->handleIncOrDec( $node );
215    }
216
217    /**
218     * @inheritDoc
219     */
220    public function visitPreInc( Node $node ): void {
221        $this->handleIncOrDec( $node );
222    }
223
224    /**
225     * @inheritDoc
226     */
227    public function visitPostDec( Node $node ): void {
228        $this->handleIncOrDec( $node );
229    }
230
231    /**
232     * @inheritDoc
233     */
234    public function visitPostInc( Node $node ): void {
235        $this->handleIncOrDec( $node );
236    }
237
238    /**
239     * @param Node $node
240     */
241    private function handleIncOrDec( Node $node ): void {
242        $children = $node->children;
243        assert( count( $children ) === 1 );
244        $this( reset( $children ) );
245    }
246
247    /**
248     * @param TypedElementInterface|null $el
249     */
250    private function handleReturnedObject( ?TypedElementInterface $el ): void {
251        if ( $el ) {
252            $this->buffer[] = $el;
253        }
254    }
255}