Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
FunctionCausedByLines
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 16
2756
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 emptySingleton
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getGenericLines
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 withAddedGenericLines
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 withGenericLines
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 withAddedParamSinkLines
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 withAddedParamPreservedLines
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 withParamSinkLines
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 withParamPreservedLines
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 withVariadicParamSinkLines
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 withVariadicParamPreservedLines
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 withAddedVariadicParamSinkLines
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 withAddedVariadicParamPreservedLines
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getParamSinkLines
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 getParamPreservedLines
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 asMergedWith
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
272
 toString
n/a
0 / 0
n/a
0 / 0
5
 __toString
n/a
0 / 0
n/a
0 / 0
1
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 = CausedByLines::emptySingleton();
30    }
31
32    public static function emptySingleton(): self {
33        static $singleton;
34        if ( !$singleton ) {
35            $singleton = new self();
36        }
37        return $singleton;
38    }
39
40    /**
41     * @suppress PhanUnreferencedPublicMethod
42     */
43    public function getGenericLines(): CausedByLines {
44        return $this->genericLines;
45    }
46
47    /**
48     * @param string[] $lines
49     * @param Taintedness $taint
50     * @param ?MethodLinks $links
51     */
52    public function withAddedGenericLines( array $lines, Taintedness $taint, ?MethodLinks $links = null ): self {
53        $ret = clone $this;
54        $ret->genericLines = $this->genericLines->withAddedLines( $lines, $taint, $links );
55        return $ret;
56    }
57
58    public function withGenericLines( CausedByLines $lines ): self {
59        $ret = clone $this;
60        $ret->genericLines = $lines;
61        return $ret;
62    }
63
64    /**
65     * @param int $param
66     * @param string[] $lines
67     * @param Taintedness $taint
68     */
69    public function withAddedParamSinkLines( int $param, array $lines, Taintedness $taint ): self {
70        assert( $param !== $this->variadicParamIndex );
71        $ret = clone $this;
72        if ( !isset( $ret->paramSinkLines[$param] ) ) {
73            $ret->paramSinkLines[$param] = CausedByLines::emptySingleton();
74        }
75        $ret->paramSinkLines[$param] = $ret->paramSinkLines[$param]->withAddedLines( $lines, $taint );
76        return $ret;
77    }
78
79    /**
80     * @param int $param
81     * @param string[] $lines
82     * @param Taintedness $taint
83     * @param ?MethodLinks $links
84     */
85    public function withAddedParamPreservedLines(
86        int $param,
87        array $lines,
88        Taintedness $taint,
89        ?MethodLinks $links = null
90    ): self {
91        assert( $param !== $this->variadicParamIndex );
92        $ret = clone $this;
93        if ( !isset( $ret->paramPreservedLines[$param] ) ) {
94            $ret->paramPreservedLines[$param] = CausedByLines::emptySingleton();
95        }
96        $ret->paramPreservedLines[$param] = $ret->paramPreservedLines[$param]
97            ->withAddedLines( $lines, $taint, $links );
98        return $ret;
99    }
100
101    public function withParamSinkLines( int $param, CausedByLines $lines ): self {
102        $ret = clone $this;
103        $ret->paramSinkLines[$param] = $lines;
104        return $ret;
105    }
106
107    public function withParamPreservedLines( int $param, CausedByLines $lines ): self {
108        $ret = clone $this;
109        $ret->paramPreservedLines[$param] = $lines;
110        return $ret;
111    }
112
113    public function withVariadicParamSinkLines( int $param, CausedByLines $lines ): self {
114        $ret = clone $this;
115        $ret->variadicParamIndex = $param;
116        $ret->variadicParamSinkLines = $lines;
117        return $ret;
118    }
119
120    public function withVariadicParamPreservedLines( int $param, CausedByLines $lines ): self {
121        $ret = clone $this;
122        $ret->variadicParamIndex = $param;
123        $ret->variadicParamPreservedLines = $lines;
124        return $ret;
125    }
126
127    /**
128     * @param int $param
129     * @param string[] $lines
130     * @param Taintedness $taint
131     */
132    public function withAddedVariadicParamSinkLines(
133        int $param,
134        array $lines,
135        Taintedness $taint
136    ): self {
137        assert( !isset( $this->paramSinkLines[$param] ) && !isset( $this->paramPreservedLines[$param] ) );
138        $ret = clone $this;
139        $ret->variadicParamIndex = $param;
140        if ( !$ret->variadicParamSinkLines ) {
141            $ret->variadicParamSinkLines = CausedByLines::emptySingleton();
142        }
143        $ret->variadicParamSinkLines = $ret->variadicParamSinkLines->withAddedLines( $lines, $taint );
144        return $ret;
145    }
146
147    /**
148     * @param int $param
149     * @param string[] $lines
150     * @param Taintedness $taint
151     * @param ?MethodLinks $links
152     */
153    public function withAddedVariadicParamPreservedLines(
154        int $param,
155        array $lines,
156        Taintedness $taint,
157        ?MethodLinks $links = null
158    ): self {
159        assert( !isset( $this->paramSinkLines[$param] ) && !isset( $this->paramPreservedLines[$param] ) );
160        $ret = clone $this;
161        $ret->variadicParamIndex = $param;
162        if ( !$ret->variadicParamPreservedLines ) {
163            $ret->variadicParamPreservedLines = CausedByLines::emptySingleton();
164        }
165        $ret->variadicParamPreservedLines = $ret->variadicParamPreservedLines
166            ->withAddedLines( $lines, $taint, $links );
167        return $ret;
168    }
169
170    public function getParamSinkLines( int $param ): CausedByLines {
171        if ( isset( $this->paramSinkLines[$param] ) ) {
172            return $this->paramSinkLines[$param];
173        }
174        if (
175            $this->variadicParamIndex !== null && $param >= $this->variadicParamIndex &&
176            $this->variadicParamSinkLines
177        ) {
178            return $this->variadicParamSinkLines;
179        }
180        return CausedByLines::emptySingleton();
181    }
182
183    public function getParamPreservedLines( int $param ): CausedByLines {
184        if ( isset( $this->paramPreservedLines[$param] ) ) {
185            return $this->paramPreservedLines[$param];
186        }
187        if (
188            $this->variadicParamIndex !== null && $param >= $this->variadicParamIndex &&
189            $this->variadicParamPreservedLines
190        ) {
191            return $this->variadicParamPreservedLines;
192        }
193        return CausedByLines::emptySingleton();
194    }
195
196    /**
197     * @param FunctionCausedByLines $other
198     * @param FunctionTaintedness $funcTaint To check NO_OVERRIDE
199     */
200    public function asMergedWith( self $other, FunctionTaintedness $funcTaint ): self {
201        $ret = clone $this;
202        $canOverrideOverall = $funcTaint->canOverrideOverall();
203        if ( $canOverrideOverall ) {
204            $ret->genericLines = $ret->genericLines->asMergedWith( $other->genericLines );
205        }
206
207        foreach ( $other->paramSinkLines as $param => $lines ) {
208            if ( $funcTaint->canOverrideNonVariadicParam( $param ) ) {
209                if ( isset( $ret->paramSinkLines[$param] ) ) {
210                    $ret->paramSinkLines[$param] = $ret->paramSinkLines[$param]->asMergedWith( $lines );
211                } else {
212                    $ret->paramSinkLines[$param] = $lines;
213                }
214            }
215        }
216        if ( $canOverrideOverall ) {
217            foreach ( $other->paramPreservedLines as $param => $lines ) {
218                if ( $funcTaint->canOverrideNonVariadicParam( $param ) ) {
219                    if ( isset( $ret->paramPreservedLines[$param] ) ) {
220                        $ret->paramPreservedLines[$param] = $ret->paramPreservedLines[$param]->asMergedWith( $lines );
221                    } else {
222                        $ret->paramPreservedLines[$param] = $lines;
223                    }
224                }
225            }
226        }
227        $variadicIndex = $other->variadicParamIndex;
228        if ( $variadicIndex !== null && $funcTaint->canOverrideVariadicParam() ) {
229            $ret->variadicParamIndex = $variadicIndex;
230            $sinkVariadic = $other->variadicParamSinkLines;
231            if ( $sinkVariadic ) {
232                if ( $ret->variadicParamSinkLines ) {
233                    $ret->variadicParamSinkLines = $ret->variadicParamSinkLines->asMergedWith( $sinkVariadic );
234                } else {
235                    $ret->variadicParamSinkLines = $sinkVariadic;
236                }
237            }
238            if ( $canOverrideOverall ) {
239                $preserveVariadic = $other->variadicParamPreservedLines;
240                if ( $preserveVariadic ) {
241                    if ( $ret->variadicParamPreservedLines ) {
242                        $ret->variadicParamPreservedLines = $this->variadicParamPreservedLines
243                            ->asMergedWith( $preserveVariadic );
244                    } else {
245                        $ret->variadicParamPreservedLines = $preserveVariadic;
246                    }
247                }
248            }
249        }
250        return $ret;
251    }
252
253    /**
254     * @codeCoverageIgnore
255     */
256    public function toString(): string {
257        $str = "{\nGeneric: " . $this->genericLines->toDebugString() . ",\n";
258        foreach ( $this->paramSinkLines as $par => $lines ) {
259            $str .= "$par (sink): " . $lines->toDebugString() . ",\n";
260        }
261        foreach ( $this->paramPreservedLines as $par => $lines ) {
262            $str .= "$par (preserved): " . $lines->toDebugString() . ",\n";
263        }
264        if ( $this->variadicParamSinkLines ) {
265            $str .= "...{$this->variadicParamIndex} (sink): " . $this->variadicParamSinkLines->toDebugString() . "\n";
266        }
267        if ( $this->variadicParamPreservedLines ) {
268            $str .= "...{$this->variadicParamIndex} (preserved): " .
269                $this->variadicParamPreservedLines->toDebugString() . "\n";
270        }
271        return "$str}";
272    }
273
274    /**
275     * @codeCoverageIgnore
276     */
277    public function __toString(): string {
278        return $this->toString();
279    }
280}