Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
23 / 23
CRAP
100.00% covered (success)
100.00%
1 / 1
TaintednessAccessorsTrait
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
23 / 23
35
100.00% covered (success)
100.00%
1 / 1
 getTaintednessRaw
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTaintednessRawClone
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 setTaintednessRaw
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getCausedByRaw
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCausedByRawCloneOrEmpty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getFuncCausedByRawCloneOrEmpty
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setCausedByRaw
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setFuncCausedByRaw
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMethodLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMethodLinksCloneOrEmpty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 setMethodLinks
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getMethodLinksRef
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getVarLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 ensureVarLinksForArgExist
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getTaintednessRef
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 setTaintednessRef
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clearRefData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clearTaintError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFuncTaint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doSetFuncTaint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRetObjs
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 addRetObjs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 initRetObjs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php declare( strict_types=1 );
2
3namespace SecurityCheckPlugin;
4
5use Phan\Language\Element\FunctionInterface;
6use Phan\Language\Element\PassByReferenceVariable;
7use Phan\Language\Element\TypedElementInterface;
8
9/**
10 * Accessors to read and write taintedness props stored inside phan objects. This trait exists to avoid duplicating
11 * dynamic property names, to have better type inference, to enable phan checks for undeclared props on the other
12 * files, to keep track of props usage etc.
13 * @phan-file-suppress PhanUndeclaredProperty
14 */
15trait TaintednessAccessorsTrait {
16    /**
17     * @param TypedElementInterface $element
18     * @return Taintedness|null
19     */
20    protected static function getTaintednessRaw( TypedElementInterface $element ): ?Taintedness {
21        return $element->taintedness ?? null;
22    }
23
24    /**
25     * @param TypedElementInterface $element
26     * @return Taintedness|null
27     * @suppress PhanUnreferencedProtectedMethod False positive
28     */
29    protected static function getTaintednessRawClone( TypedElementInterface $element ): ?Taintedness {
30        // Performance: use isset(), not property_exists()
31        return isset( $element->taintedness ) ? clone $element->taintedness : null;
32    }
33
34    /**
35     * @param TypedElementInterface $element
36     * @param Taintedness $taintedness
37     */
38    protected static function setTaintednessRaw( TypedElementInterface $element, Taintedness $taintedness ): void {
39        $element->taintedness = $taintedness;
40        if ( $element instanceof PassByReferenceVariable ) {
41            self::setTaintednessRef( $element->getElement(), $taintedness );
42        }
43    }
44
45    /**
46     * @param TypedElementInterface $element
47     * @return CausedByLines|null
48     */
49    protected static function getCausedByRaw( TypedElementInterface $element ): ?CausedByLines {
50        return $element->taintedOriginalError ?? null;
51    }
52
53    /**
54     * @param TypedElementInterface $element
55     * @return CausedByLines
56     */
57    protected static function getCausedByRawCloneOrEmpty( TypedElementInterface $element ): CausedByLines {
58        return isset( $element->taintedOriginalError ) ? clone $element->taintedOriginalError : new CausedByLines();
59    }
60
61    /**
62     * @param FunctionInterface $func
63     * @return FunctionCausedByLines
64     */
65    protected static function getFuncCausedByRawCloneOrEmpty( FunctionInterface $func ): FunctionCausedByLines {
66        return isset( $func->funcTaintedOriginalError )
67            ? clone $func->funcTaintedOriginalError
68            : new FunctionCausedByLines();
69    }
70
71    /**
72     * @param TypedElementInterface $element
73     * @param CausedByLines $lines
74     */
75    protected static function setCausedByRaw( TypedElementInterface $element, CausedByLines $lines ): void {
76        $element->taintedOriginalError = $lines;
77        if ( $element instanceof PassByReferenceVariable ) {
78            $curCausedBy = self::getCausedByRaw( $element->getElement() );
79            $newCausedBy = $curCausedBy ? $curCausedBy->asMergedWith( $lines ) : $lines;
80            self::setCausedByRaw( $element->getElement(), $newCausedBy );
81        }
82    }
83
84    /**
85     * @param FunctionInterface $func
86     * @param FunctionCausedByLines $lines
87     */
88    protected static function setFuncCausedByRaw( FunctionInterface $func, FunctionCausedByLines $lines ): void {
89        $func->funcTaintedOriginalError = $lines;
90    }
91
92    /**
93     * @note This doesn't return a clone
94     *
95     * @param TypedElementInterface $element
96     * @return MethodLinks|null
97     */
98    protected static function getMethodLinks( TypedElementInterface $element ): ?MethodLinks {
99        return $element->taintedMethodLinks ?? null;
100    }
101
102    /**
103     * @param TypedElementInterface $element
104     * @return MethodLinks
105     */
106    protected static function getMethodLinksCloneOrEmpty( TypedElementInterface $element ): MethodLinks {
107        // Performance: use isset(), not property_exists()
108        return isset( $element->taintedMethodLinks ) ? clone $element->taintedMethodLinks : new MethodLinks();
109    }
110
111    /**
112     * @param TypedElementInterface $element
113     * @param MethodLinks $links
114     */
115    protected static function setMethodLinks( TypedElementInterface $element, MethodLinks $links ): void {
116        $element->taintedMethodLinks = $links;
117        if ( $element instanceof PassByReferenceVariable ) {
118            $element->getElement()->taintedMethodLinksRef = $links;
119        }
120    }
121
122    /**
123     * @param TypedElementInterface $element
124     * @return MethodLinks|null
125     */
126    protected static function getMethodLinksRef( TypedElementInterface $element ): ?MethodLinks {
127        return $element->taintedMethodLinksRef ?? null;
128    }
129
130    /**
131     * @param FunctionInterface $func
132     * @param int $index
133     * @return VarLinksSet|null
134     */
135    protected static function getVarLinks( FunctionInterface $func, int $index ): ?VarLinksSet {
136        return $func->taintedVarLinks[$index] ?? null;
137    }
138
139    /**
140     * @param TypedElementInterface $element
141     * @param int $arg
142     */
143    protected static function ensureVarLinksForArgExist( TypedElementInterface $element, int $arg ): void {
144        $element->taintedVarLinks ??= [];
145        $element->taintedVarLinks[$arg] ??= new VarLinksSet;
146    }
147
148    /**
149     * @param TypedElementInterface $element
150     * @return Taintedness|null
151     */
152    protected static function getTaintednessRef( TypedElementInterface $element ): ?Taintedness {
153        // Performance: use isset(), not property_exists()
154        return isset( $element->taintednessRef ) ? clone $element->taintednessRef : null;
155    }
156
157    /**
158     * @param TypedElementInterface $element
159     * @param Taintedness $taintedness
160     */
161    protected static function setTaintednessRef( TypedElementInterface $element, Taintedness $taintedness ): void {
162        $element->taintednessRef = $taintedness;
163    }
164
165    /**
166     * @param TypedElementInterface $element
167     */
168    protected static function clearRefData( TypedElementInterface $element ): void {
169        unset( $element->taintednessRef, $element->taintedMethodLinksRef );
170    }
171
172    /**
173     * Clears any previous error on the given element.
174     *
175     * @param TypedElementInterface $elem
176     */
177    protected static function clearTaintError( TypedElementInterface $elem ): void {
178        unset( $elem->taintedOriginalError );
179    }
180
181    /**
182     * Get $func's taint, or null if not set. NOTE: This doesn't create a clone.
183     *
184     * @param FunctionInterface $func
185     * @return FunctionTaintedness|null
186     */
187    protected static function getFuncTaint( FunctionInterface $func ): ?FunctionTaintedness {
188        return $func->funcTaint ?? null;
189    }
190
191    /**
192     * @param FunctionInterface $func
193     * @param FunctionTaintedness $funcTaint
194     */
195    protected static function doSetFuncTaint( FunctionInterface $func, FunctionTaintedness $funcTaint ): void {
196        $func->funcTaint = $funcTaint;
197    }
198
199    /**
200     * @param FunctionInterface $func
201     * @return TypedElementInterface[]|null
202     */
203    protected static function getRetObjs( FunctionInterface $func ): ?array {
204        $funcNode = $func->getNode();
205        if ( !$funcNode ) {
206            // If it has no node, it won't have any returned object, so don't return null, to avoid
207            // potential recursive analysis attempts.
208            return [];
209        }
210        return $funcNode->retObjs ?? null;
211    }
212
213    /**
214     * @note These are saved in the function node so that they can be shared by all implementations, without
215     * having to check the defining FQSEN of a method and canonicalize $func for lookup.
216     * @param FunctionInterface $func
217     * @param TypedElementInterface[] $retObjs
218     * @suppress PhanUnreferencedProtectedMethod Used in TaintednessVisitor
219     */
220    protected static function addRetObjs( FunctionInterface $func, array $retObjs ): void {
221        $funcNode = $func->getNode();
222        if ( $funcNode ) {
223            $funcNode->retObjs = array_merge( $funcNode->retObjs ?? [], $retObjs );
224        }
225    }
226
227    /**
228     * @param FunctionInterface $func
229     */
230    protected static function initRetObjs( FunctionInterface $func ): void {
231        $funcNode = $func->getNode();
232        if ( $funcNode ) {
233            $funcNode->retObjs ??= [];
234        }
235    }
236
237}