Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.27% covered (success)
91.27%
115 / 126
53.85% covered (warning)
53.85%
7 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
CausedByLines
91.27% covered (success)
91.27%
115 / 126
53.85% covered (warning)
53.85%
7 / 13
71.08
0.00% covered (danger)
0.00%
0 / 1
 addLines
86.67% covered (warning)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
12.34
 asPreservingTaintednessAndLinks
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 asIntersectedWithTaintedness
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 asFilteredForFuncAndParam
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 getLinesForGenericReturn
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 withTaintAddedToMethodArgLinks
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 mergeWith
97.44% covered (success)
97.44%
38 / 39
0.00% covered (danger)
0.00%
0 / 1
17
 asMergedWith
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getArraySubsetIdx
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
7.02
 toStringForIssue
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 getRelevantLinesForTaintedness
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 toLinesArray
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toDebugString
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php declare( strict_types=1 );
2
3namespace SecurityCheckPlugin;
4
5use Phan\Language\Element\FunctionInterface;
6
7/**
8 * Value object used to store caused-by lines.
9 *
10 * @note `clone` will not deep-clone instances of this class. That'd be more correct in theory, but it's
11 * tremendously expensive for this class (+35s and +600MB for MW core).
12 *
13 * @todo Keep per-offset caused-by lines
14 */
15class CausedByLines {
16    private const MAX_LINES_PER_ISSUE = 80;
17    // XXX Hack: Enforce a hard limit, or things may explode
18    private const LINES_HARD_LIMIT = 100;
19
20    /**
21     * Note: the links are nullable for performance.
22     * @var array<array<Taintedness|string|MethodLinks|null>>
23     * @phan-var list<array{0:Taintedness,1:string,2:?MethodLinks}>
24     */
25    private $lines = [];
26
27    /**
28     * @param string[] $lines
29     * @param Taintedness $taintedness
30     * @param MethodLinks|null $links
31     * @note Taintedness and links are cloned as needed
32     */
33    public function addLines( array $lines, Taintedness $taintedness, MethodLinks $links = null ): void {
34        if ( !$this->lines ) {
35            foreach ( $lines as $line ) {
36                $this->lines[] = [ clone $taintedness, $line, $links ? clone $links : null ];
37            }
38            return;
39        }
40
41        foreach ( $lines as $line ) {
42            if ( count( $this->lines ) >= self::LINES_HARD_LIMIT ) {
43                break;
44            }
45            $idx = array_search( $line, array_column( $this->lines, 1 ), true );
46            if ( $idx !== false ) {
47                $this->lines[ $idx ][0]->mergeWith( $taintedness );
48                if ( $links && !$this->lines[$idx][2] ) {
49                    $this->lines[$idx][2] = clone $links;
50                } elseif ( $links && $links !== $this->lines[$idx][2] ) {
51                    $this->lines[$idx][2]->mergeWith( $links );
52                }
53            } else {
54                $this->lines[] = [ clone $taintedness, $line, $links ? clone $links : null ];
55            }
56        }
57    }
58
59    /**
60     * Move any possibly preserved taintedness stored in the method links to the actual taintedness of this line,
61     * and use $links as the new links being preserved.
62     *
63     * @param Taintedness $taintedness
64     * @param MethodLinks $links
65     * @return self
66     */
67    public function asPreservingTaintednessAndLinks( Taintedness $taintedness, MethodLinks $links ): self {
68        $ret = new self;
69        if ( !$this->lines ) {
70            return $ret;
71        }
72        $curTaint = $taintedness->get();
73        foreach ( $this->lines as [ $_, $eLine, $eLinks ] ) {
74            $preservedFlags = $eLinks && ( $curTaint !== SecurityCheckPlugin::NO_TAINT )
75                ? $eLinks->filterPreservedFlags( $curTaint )
76                : SecurityCheckPlugin::NO_TAINT;
77            $ret->lines[] = [ new Taintedness( $preservedFlags ), $eLine, clone $links ];
78        }
79        return $ret;
80    }
81
82    /**
83     * @param Taintedness $taintedness
84     * @return self
85     */
86    public function asIntersectedWithTaintedness( Taintedness $taintedness ): self {
87        $ret = new self;