Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.94% covered (warning)
87.94%
248 / 282
84.44% covered (warning)
84.44%
38 / 45
CRAP
0.00% covered (danger)
0.00%
0 / 1
Taintedness
87.94% covered (warning)
87.94%
248 / 282
84.44% covered (warning)
84.44%
38 / 45
158.17
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
 newSafe
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newUnknown
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newTainted
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 get
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 asCollapsed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 asKnownKeysMadeUnknown
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 getAllKeysTaint
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 add
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 with
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 remove
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 without
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 has
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
7
 keepOnly
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 withOnly
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 intersectForSink
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
7
 removeKnownKeysFrom
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
4.13
 mergeWith
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 asMergedWith
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setOffsetTaintedness
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 addKeysTaintedness
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 asMergedForAssignment
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 arrayPlus
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 asArrayPlusWith
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getTaintednessForOffsetOrWhole
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 asMaybeMovedAtOffset
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 asValueFirstLevel
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 withoutKey
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 withoutKeys
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 asKeyForForeach
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 arrayReplace
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 arrayMerge
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 isSafe
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
7
 asExecToYesTaint
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 asYesToExecTaint
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 asMovedAtRelevantOffsetsForBackprop
88.89% covered (warning)
88.89%
16 / 18
0.00% covered (danger)
0.00%
0 / 1
6.05
 flagsAsExecToYesTaint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 flagsAsYesToExecTaint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 asPreservedTaintedness
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
3.33
 decomposeForLinks
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
5
 toString
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
12
 toShortString
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 __clone
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 __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
5use ast\Node;
6
7/**
8 * Value object used to store taintedness. This should always be used to manipulate taintedness values,
9 * instead of directly using taint constants directly (except for comparisons etc.).
10 *
11 * Note that this class should be used as copy-on-write (like phan's UnionType), so in-place
12 * manipulation should never be done on phan objects.
13 */
14class Taintedness {
15    /** @var int Combination of the class constants */
16    private $flags;
17
18    /** @var self[] Taintedness for each possible array element */
19    private $dimTaint = [];
20
21    /** @var int Taintedness of the array keys */
22    private $keysTaint = SecurityCheckPlugin::NO_TAINT;
23
24    /**
25     * @var self|null Taintedness for array elements that we couldn't attribute to any key
26     */
27    private $unknownDimsTaint;
28
29    /**
30     * @param int $val One of the class constants
31     */
32    public function __construct( int $val ) {
33        $this->flags = $val;
34    }
35
36    // Common creation shortcuts
37
38    /**
39     * @return self
40     */
41    public static function newSafe(): self {
42        return new self( SecurityCheckPlugin::NO_TAINT );
43    }
44
45    /**
46     * @return self
47     */
48    public static function newUnknown(): self {
49        return new self( SecurityCheckPlugin::UNKNOWN_TAINT );
50    }
51
52    /**
53     * @return self
54     */
55    public static function newTainted(): self {
56        return new self( SecurityCheckPlugin::YES_TAINT );
57    }
58
59    /**
60     * @param Taintedness[] $values
61     * @return self
62     */
63    public static function newFromArray( array $values ): self {
64        $ret = self::newSafe();
65        foreach ( $values as $key => $value ) {
66            assert( $value instanceof self );
67            $ret->setOffsetTaintedness( $key, $value );
68        }
69        return $ret;
70    }
71
72    /**
73     * Get a numeric representation of the taint stored in this object. This includes own taint,
74     * array keys and whatnot.
75     * @note This should almost NEVER be used outside of this class! Use accessors as much as possible!
76     *
77     * @return int
78     */
79    public function get(): int {
80        $ret = $this->flags | $this->getAllKeysTaint() | $this->keysTaint;
81        return $this->unknownDimsTaint ? ( $ret | $this->unknownDimsTaint->get() ) : $ret;
82    }
83
84    /**
85     * Get a flattened version of this object, with any taint from keys etc. collapsed into flags
86     * @return $this
87     */
88    public function asCollapsed(): self {
89        return new self( $this->get() );
90    }
91
92    /**
93     * Returns a copy of this object where the taintedness of every known key has been reassigned
94     * to unknown keys.
95     * @return self
96     */
97    public function asKnownKeysMadeUnknown(): self {
98        $ret = new self( $this->flags );
99        $ret->keysTaint = $this->keysTaint;
100        $ret->unknownDimsTaint = $this->unknownDimsTaint;
101        if ( $this->dimTaint ) {
102            $ret->unknownDimsTaint ??= self::newSafe();
103            foreach ( $this->dimTaint as $keyTaint ) {
104                $ret->unknownDimsTaint->mergeWith( $keyTaint );
105            }
106        }
107        return $ret;
108    }
109
110    /**
111     * Recursively extract the taintedness from each key.
112     *
113     * @return int
114     */
115    private function getAllKeysTaint(): int {
116        $ret = SecurityCheckPlugin::NO_TAINT;
117        foreach ( $this->dimTaint as $val ) {
118            $ret |= $val->get();
119        }
120        return $ret;
121    }
122
123    // Value manipulation
124
125    /**
126     * Add the given taint to this object's flags, *without* creating a clone
127     * @see Taintedness::with() if you need a clone
128     * @see Taintedness::mergeWith() if you want to preserve the whole shape
129     *
130     * @param int $taint
131     */
132    public function add( int $taint ): void {
133        // TODO: Should this clear UNKNOWN_TAINT if its present only in one of the args?