Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.00% covered (warning)
80.00%
64 / 80
83.33% covered (warning)
83.33%
15 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
FunctionCausedByLines
80.00% covered (warning)
80.00%
64 / 80
83.33% covered (warning)
83.33%
15 / 18
73.63
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGenericLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addGenericLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setGenericLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addParamSinkLines
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addParamPreservedLines
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setParamSinkLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setParamPreservedLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setVariadicParamSinkLines
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setVariadicParamPreservedLines
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addVariadicParamSinkLines
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 addVariadicParamPreservedLines
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getParamSinkLines
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 getParamPreservedLines
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 mergeWith
84.00% covered (warning)
84.00%
21 / 25
0.00% covered (danger)
0.00%
0 / 1
14.80
 __clone
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 toString
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 __toString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php declare( strict_types=1 );
2
3namespace SecurityCheckPlugin;
4
5/**
6 * This class represents caused-by lines for a function-like:
7 * - Lines in genericLines are for taintedness that is added inside the function, regardless of parameters; these
8 *   have a Taintedness object associated, while the associated links are always empty.
9 * - Lines in (variadic)paramSinkLines are those inside a function that EXEC the arguments. These also have a
10 *   Taintedness object associated, and no links.
11 * - Lines in (variadic)paramPreservedLines are responsible for putting a parameter inside the return value. These have
12 *   a safe Taintedness associated, and usually non-empty links.
13 */
14class FunctionCausedByLines {
15    /** @var CausedByLines */
16    private $genericLines;
17    /** @var CausedByLines[] */
18    private $paramSinkLines = [];
19    /** @var CausedByLines[] */
20    private $paramPreservedLines = [];
21    /** @var int|null Index of a variadic parameter, if any */
22    private $variadicParamIndex;
23    /** @var CausedByLines|null */
24    private $variadicParamSinkLines;
25    /** @var CausedByLines|null */
26    private $variadicParamPreservedLines;
27
28    public function __construct() {
29        $this->genericLines = new CausedByLines();
30    }
31
32    /**
33     * @return CausedByLines
34     * @suppress PhanUnreferencedPublicMethod
35     */
36    public function getGenericLines(): CausedByLines {
37        return $this->genericLines;
38    }
39
40    /**
41     * @param string[] $lines
42     * @param Taintedness $taint
43     * @param MethodLinks|null $links
44     */
45    public function addGenericLines( array $lines, Taintedness $taint, MethodLinks $links = null ): void {
46        $this->genericLines->addLines( $lines, $taint, $links );
47    }
48
49    /**
50     * @param CausedByLines $lines
51     */
52    public function setGenericLines( CausedByLines $lines ): void {
53        $this->genericLines = $lines;
54    }
55
56    /**
57     * @param int $param
58     * @param string[] $lines
59     * @param Taintedness $taint
60     */
61    public function addParamSinkLines( int $param, array $lines, Taintedness $taint ): void {
62        assert( $param !== $this->variadicParamIndex );
63        if ( !isset( $this->paramSinkLines[$param] ) ) {
64            $this->paramSinkLines[$param] = new CausedByLines();
65        }
66        $this->paramSinkLines[$param]->addLines( $lines, $taint );
67    }
68
69    /**
70     * @param int $param
71     * @param string[] $lines
72     * @param Taintedness $taint
73     * @param MethodLinks|null $links
74     */
75    public function addParamPreservedLines(
76        int $param,
77        array $lines,
78        Taintedness $taint,
79        MethodLinks $links = null
80    ): void {
81        assert( $param !== $this->variadicParamIndex );
82        if ( !isset( $this->paramPreservedLines[$param] ) ) {
83            $this->paramPreservedLines[$param] = new CausedByLines();
84        }
85        $this->paramPreservedLines[$param]->addLines( $lines, $taint, $links );
86    }
87
88    /**
89     * @param int $param
90     * @param CausedByLines $lines
91     */
92    public function setParamSinkLines( int $param, CausedByLines $lines ): void {
93        $this->paramSinkLines[$param] = $lines;
94    }
95
96    /**
97     * @param int $param
98     * @param CausedByLines $lines
99     */
100    public function setParamPreservedLines( int $param, CausedByLines $lines ): void {
101        $this->paramPreservedLines[$param] = $lines;
102    }
103
104    /**
105     * @param int $param
106     * @param CausedByLines $lines
107     */
108    public function setVariadicParamSinkLines( int $param, CausedByLines $lines ): void {
109        $this->variadicParamIndex = $param;
110        $this->variadicParamSinkLines = $lines;
111    }
112
113    /**
114     * @param int $param
115     * @param CausedByLines $lines
116     */
117    public function setVariadicParamPreservedLines( int $param, CausedByLines $lines ): void {
118        $this->variadicParamIndex = $param;
119        $this->variadicParamPreservedLines = $lines;
120    }
121
122    /**
123     * @param int $param
124     * @param string[] $lines
125     * @param Taintedness $taint
126     */
127    public function addVariadicParamSinkLines(
128        int $param,
129        array $lines,
130        Taintedness $taint
131    ): void {
132        assert( !isset( $this->paramSinkLines[$param] ) && !isset( $this->paramPreservedLines[$param] ) );
133        $this->variadicParamIndex = $param;
134        if ( !$this->variadicParamSinkLines ) {
135            $this->variadicParamSinkLines = new CausedByLines();
136        }
137        $this->variadicParamSinkLines->addLines( $lines, $taint );
138    }
139
140    /**
141     * @param int $param
142     * @param string[] $lines
143     * @param Taintedness $taint
144     * @param MethodLinks|null $links
145     */
146    public function addVariadicParamPreservedLines(
147        int $param,
148        array $lines,
149        Taintedness $taint,
150        MethodLinks $links = null
151    ): void {
152        assert( !isset( $this->paramSinkLines[$param] ) && !isset( $this->paramPreservedLines[$param] ) );
153        $this->variadicParamIndex = $param;
154        if ( !$this->variadicParamPreservedLines ) {
155            $this->variadicParamPreservedLines = new CausedByLines();
156        }
157        $this->variadicParamPreservedLines->addLines( $lines, $taint, $links );
158    }
159
160    /**
161     * @param int $param
162     * @return CausedByLines
163     */
164    public function getParamSinkLines( int $param ): CausedByLines {
165        if ( isset( $this->paramSinkLines[$param] ) ) {
166            return $this->paramSinkLines[$param];
167        }
168        if (
169            $this->variadicParamIndex !== null && $param >= $this->variadicParamIndex &&
170            $this->variadicParamSinkLines
171        ) {
172            return $this->variadicParamSinkLines;
173        }
174        return new CausedByLines();
175    }
176
177    /**
178     * @param int $param
179     * @return CausedByLines
180     */
181    public function getParamPreservedLines( int $param ): CausedByLines {
182        if ( isset( $this->paramPreservedLines[$param] ) ) {
183            return $this->paramPreservedLines[$param];
184        }
185        if (
186            $this->variadicParamIndex !== null && $param >= $this->variadicParamIndex &&
187            $this->variadicParamPreservedLines
188        ) {
189            return $this->variadicParamPreservedLines;
190        }
191        return new CausedByLines();
192    }
193
194    /**
195     * @param FunctionCausedByLines $other
196     * @param FunctionTaintedness $funcTaint To check NO_OVERRIDE
197     */
198    public function mergeWith( self $other, FunctionTaintedness $funcTaint ): void {
199        if ( $funcTaint->canOverrideOverall() ) {
200            $this->genericLines->mergeWith( $other->genericLines );
201        }
202        foreach ( $other->paramSinkLines as $param => $lines ) {
203            if ( $funcTaint->canOverrideNonVariadicParam( $param ) ) {
204                if ( isset( $this->paramSinkLines[$param] ) ) {
205                    $this->paramSinkLines[$param]->mergeWith( $lines );
206                } else {
207                    $this->paramSinkLines[$param] = $lines;
208                }
209            }
210        }
211        foreach ( $other->paramPreservedLines as $param => $lines ) {
212            if ( $funcTaint->canOverrideNonVariadicParam( $param ) ) {
213                if ( isset( $this->paramPreservedLines[$param] ) ) {
214                    $this->paramPreservedLines[$param]->mergeWith( $lines );
215                } else {
216                    $this->paramPreservedLines[$param] = $lines;
217                }
218            }
219        }
220        $variadicIndex = $other->variadicParamIndex;
221        if ( $variadicIndex !== null && $funcTaint->canOverrideVariadicParam() ) {
222            $this->variadicParamIndex = $variadicIndex;
223            $sinkVariadic = $other->variadicParamSinkLines;
224            if ( $sinkVariadic ) {
225                if ( $this->variadicParamSinkLines ) {
226                    $this->variadicParamSinkLines->mergeWith( $sinkVariadic );
227                } else {
228                    $this->variadicParamSinkLines = $sinkVariadic;
229                }
230            }
231            $preserveVariadic = $other->variadicParamPreservedLines;
232            if ( $preserveVariadic ) {
233                if ( $this->variadicParamPreservedLines ) {
234                    $this->variadicParamPreservedLines->mergeWith( $preserveVariadic );
235                } else {
236                    $this->variadicParamPreservedLines = $preserveVariadic;
237                }
238            }
239        }
240    }
241
242    public function __clone() {
243        $this->genericLines = clone $this->genericLines;
244        foreach ( $this->paramSinkLines as $k => $pLines ) {
245            $this->paramSinkLines[$k] = clone $pLines;
246        }
247        if ( $this->variadicParamSinkLines ) {
248            $this->variadicParamSinkLines = clone $this->variadicParamSinkLines;
249        }
250        if ( $this->variadicParamPreservedLines ) {
251            $this->variadicParamPreservedLines = clone $this->variadicParamPreservedLines;
252        }
253    }
254
255    /**
256     * @return string
257     */
258    public function toString(): string {
259        $str = "{\nGeneric: " . $this->genericLines->toDebugString() . ",\n";
260        foreach ( $this->paramSinkLines as $par => $lines ) {
261            $str .= "$par (sink): " . $lines->toDebugString() . ",\n";
262        }
263        foreach ( $this->paramPreservedLines as $par => $lines ) {
264            $str .= "$par (preserved): " . $lines->toDebugString() . ",\n";
265        }
266        if ( $this->variadicParamSinkLines ) {
267            $str .= "...{$this->variadicParamIndex} (sink): " . $this->variadicParamSinkLines->toDebugString() . "\n";
268        }
269        if ( $this->variadicParamPreservedLines ) {
270            $str .= "...{$this->variadicParamIndex} (preserved): " .
271                $this->variadicParamPreservedLines->toDebugString() . "\n";
272        }
273        return "$str}";
274    }
275
276    /**
277     * @return string
278     */
279    public function __toString(): string {
280        return $this->toString();
281    }
282}