Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
79.37% |
50 / 63 |
|
85.71% |
12 / 14 |
CRAP | |
0.00% |
0 / 1 |
ParamLinksOffsets | |
79.37% |
50 / 63 |
|
85.71% |
12 / 14 |
49.03 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
newAll | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
newEmpty | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFlags | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
mergeWith | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
pushOffset | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
7.05 | |||
asMovedToKeys | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getFlagsRecursively | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
__clone | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
getDims | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUnknown | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getKeysFlags | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
appliedToTaintedness | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
__toString | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
42 |
1 | <?php declare( strict_types=1 ); |
2 | |
3 | namespace SecurityCheckPlugin; |
4 | |
5 | use ast\Node; |
6 | |
7 | /** |
8 | * Tree-like object of possible offset combinations for partial links to a parameter |
9 | */ |
10 | class ParamLinksOffsets { |
11 | /** @var int What taint flags are preserved for this offset */ |
12 | private $ownFlags; |
13 | |
14 | /** @var self[] */ |
15 | private $dims = []; |
16 | |
17 | /** @var self|null */ |
18 | private $unknown; |
19 | |
20 | /** @var int */ |
21 | private $keysFlags = SecurityCheckPlugin::NO_TAINT; |
22 | |
23 | /** |
24 | * @param int $flags |
25 | */ |
26 | public function __construct( int $flags ) { |
27 | $this->ownFlags = $flags; |
28 | } |
29 | |
30 | /** |
31 | * @return self |
32 | */ |
33 | public static function newAll(): self { |
34 | return new self( SecurityCheckPlugin::ALL_TAINT ); |
35 | } |
36 | |
37 | /** |
38 | * @return self |
39 | */ |
40 | public static function newEmpty(): self { |
41 | return new self( SecurityCheckPlugin::NO_TAINT ); |
42 | } |
43 | |
44 | /** |
45 | * @note This method should be avoided where possible |
46 | * @return int |
47 | */ |
48 | public function getFlags(): int { |
49 | return $this->ownFlags; |
50 | } |
51 | |
52 | /** |
53 | * @param self $other |
54 | */ |
55 | public function mergeWith( self $other ): void { |
56 | $this->ownFlags |= $other->ownFlags; |
57 | if ( $other->unknown && !$this->unknown ) { |
58 | $this->unknown = $other->unknown; |
59 | } elseif ( $other->unknown ) { |
60 | $this->unknown->mergeWith( $other->unknown ); |
61 | } |
62 | foreach ( $other->dims as $key => $val ) { |
63 | if ( !isset( $this->dims[$key] ) ) { |
64 | $this->dims[$key] = clone $val; |
65 | } else { |
66 | $this->dims[$key]->mergeWith( $val ); |
67 | } |
68 | } |
69 | $this->keysFlags |= $other->keysFlags; |
70 | } |
71 | |
72 | /** |
73 | * Pushes $offsets to all leaves. |
74 | * @param Node|string|int|null $offset |
75 | */ |
76 | public function pushOffset( $offset ): void { |
77 | foreach ( $this->dims as $val ) { |
78 | $val->pushOffset( $offset ); |
79 | } |
80 | if ( $this->unknown ) { |
81 | $this->unknown->pushOffset( $offset ); |
82 | } |
83 | $ownFlags = $this->ownFlags; |
84 | $this->ownFlags = SecurityCheckPlugin::NO_TAINT; |
85 | if ( is_scalar( $offset ) && !isset( $this->dims[$offset] ) ) { |
86 | $this->dims[$offset] = new self( $ownFlags ); |
87 | } elseif ( !is_scalar( $offset ) && !$this->unknown ) { |
88 | $this->unknown = new self( $ownFlags ); |
89 | } |
90 | } |
91 | |
92 | /** |
93 | * @return self |
94 | */ |
95 | public function asMovedToKeys(): self { |
96 | $ret = new self( SecurityCheckPlugin::NO_TAINT ); |
97 | $ret->keysFlags = $this->ownFlags; |
98 | return $ret; |
99 | } |
100 | |
101 | /** |
102 | * @note This should only be used by SingleMethodLinks::getAllPreservedFlags |
103 | * @return int |
104 | */ |
105 | public function getFlagsRecursively(): int { |
106 | $ret = $this->ownFlags; |
107 | foreach ( $this->dims as $dimOffsets ) { |
108 | $ret |= $dimOffsets->getFlagsRecursively(); |
109 | } |
110 | if ( $this->unknown ) { |
111 | $ret |= $this->unknown->getFlagsRecursively(); |
112 | } |
113 | $ret |= $this->keysFlags; |
114 | return $ret; |
115 | } |
116 | |
117 | public function __clone() { |
118 | foreach ( $this->dims as $k => $v ) { |
119 | $this->dims[$k] = clone $v; |
120 | } |
121 | if ( $this->unknown ) { |
122 | $this->unknown = clone $this->unknown; |
123 | } |
124 | } |
125 | |
126 | /** |
127 | * Should only be used in Taintedness::asMovedAtRelevantOffsets |
128 | * @return ParamLinksOffsets[] |
129 | */ |
130 | public function getDims(): array { |
131 | return $this->dims; |
132 | } |
133 | |
134 | /** |
135 | * Should only be used in Taintedness::asMovedAtRelevantOffsetsForBackprop |
136 | * @return ParamLinksOffsets|null |
137 | */ |
138 | public function getUnknown(): ?ParamLinksOffsets { |
139 | return $this->unknown; |
140 | } |
141 | |
142 | /** |
143 | * Should only be used in Taintedness::asMovedAtRelevantOffsetsForBackprop |
144 | * @return int |
145 | */ |
146 | public function getKeysFlags(): int { |
147 | return $this->keysFlags; |
148 | } |
149 | |
150 | /** |
151 | * @param Taintedness $taintedness |
152 | * @return Taintedness |
153 | */ |
154 | public function appliedToTaintedness( Taintedness $taintedness ): Taintedness { |
155 | if ( $this->ownFlags ) { |
156 | $ret = $taintedness->withOnly( $this->ownFlags ); |
157 | } else { |
158 | $ret = new Taintedness( SecurityCheckPlugin::NO_TAINT ); |
159 | } |
160 | foreach ( $this->dims as $k => $val ) { |
161 | $ret->mergeWith( $val->appliedToTaintedness( $taintedness->getTaintednessForOffsetOrWhole( $k ) ) ); |
162 | } |
163 | if ( $this->unknown ) { |
164 | $ret->mergeWith( |
165 | $this->unknown->appliedToTaintedness( $taintedness->getTaintednessForOffsetOrWhole( null ) ) |
166 | ); |
167 | } |
168 | // XXX Should we do something to the keys here? |
169 | return $ret; |
170 | } |
171 | |
172 | /** |
173 | * @return string |
174 | */ |
175 | public function __toString(): string { |
176 | $ret = '(own): ' . SecurityCheckPlugin::taintToString( $this->ownFlags ); |
177 | |
178 | if ( $this->keysFlags ) { |
179 | $ret .= ', keys: ' . SecurityCheckPlugin::taintToString( $this->keysFlags ); |
180 | } |
181 | |
182 | if ( $this->dims || $this->unknown ) { |
183 | $ret .= ', dims: ['; |
184 | $dimBits = []; |
185 | foreach ( $this->dims as $k => $val ) { |
186 | $dimBits[] = "$k => " . $val->__toString(); |
187 | } |
188 | if ( $this->unknown ) { |
189 | $dimBits[] = '(unknown): ' . $this->unknown->__toString(); |
190 | } |
191 | $ret .= implode( ', ', $dimBits ) . ']'; |
192 | } |
193 | return $ret; |
194 | } |
195 | } |