Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.82% |
84 / 85 |
|
88.89% |
8 / 9 |
CRAP | |
0.00% |
0 / 1 |
ParamLinksOffsets | |
98.82% |
84 / 85 |
|
88.89% |
8 / 9 |
47 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInstance | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
asMergedWith | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
7 | |||
withoutShape | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
withOffsetPushed | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
8 | |||
asMovedToKeys | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
3.03 | |||
appliedToTaintedness | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
appliedToTaintednessForBackprop | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
4 | |||
isEmpty | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
7 | |||
__toString | n/a |
0 / 0 |
n/a |
0 / 0 |
6 |
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 | public static function getInstance( int $flags ): self { |
31 | static $singletons = []; |
32 | if ( !isset( $singletons[$flags] ) ) { |
33 | $singletons[$flags] = new self( $flags ); |
34 | } |
35 | return $singletons[$flags]; |
36 | } |
37 | |
38 | public function asMergedWith( self $other ): self { |
39 | if ( $this === $other ) { |
40 | return $this; |
41 | } |
42 | |
43 | $ret = clone $this; |
44 | |
45 | $ret->ownFlags |= $other->ownFlags; |
46 | if ( $other->unknown && !$ret->unknown ) { |
47 | $ret->unknown = $other->unknown; |
48 | } elseif ( $other->unknown ) { |
49 | $ret->unknown = $ret->unknown->asMergedWith( $other->unknown ); |
50 | } |
51 | foreach ( $other->dims as $key => $val ) { |
52 | if ( !isset( $ret->dims[$key] ) ) { |
53 | $ret->dims[$key] = $val; |
54 | } else { |
55 | $ret->dims[$key] = $ret->dims[$key]->asMergedWith( $val ); |
56 | } |
57 | } |
58 | $ret->keysFlags |= $other->keysFlags; |
59 | |
60 | return $ret; |
61 | } |
62 | |
63 | public function withoutShape( self $other ): self { |
64 | $ret = clone $this; |
65 | |
66 | $ret->ownFlags &= ~$other->ownFlags; |
67 | foreach ( $other->dims as $key => $val ) { |
68 | if ( isset( $ret->dims[$key] ) ) { |
69 | $ret->dims[$key] = $ret->dims[$key]->withoutShape( $val ); |
70 | } |
71 | } |
72 | if ( $other->unknown && $ret->unknown ) { |
73 | $ret->unknown = $ret->unknown->withoutShape( $other->unknown ); |
74 | } |
75 | $ret->keysFlags &= ~$other->keysFlags; |
76 | return $ret; |
77 | } |
78 | |
79 | /** |
80 | * Pushes $offsets to all leaves. |
81 | * @param Node|string|int|null $offset |
82 | */ |
83 | public function withOffsetPushed( $offset ): self { |
84 | $ret = clone $this; |
85 | |
86 | foreach ( $ret->dims as $key => $val ) { |
87 | $ret->dims[$key] = $val->withOffsetPushed( $offset ); |
88 | } |
89 | if ( $ret->unknown ) { |
90 | $ret->unknown = $ret->unknown->withOffsetPushed( $offset ); |
91 | } |
92 | |
93 | if ( $ret->ownFlags === SecurityCheckPlugin::NO_TAINT ) { |
94 | return $ret; |
95 | } |
96 | |
97 | $ownFlags = $ret->ownFlags; |
98 | $ret->ownFlags = SecurityCheckPlugin::NO_TAINT; |
99 | if ( is_scalar( $offset ) && !isset( $ret->dims[$offset] ) ) { |
100 | $ret->dims[$offset] = self::getInstance( $ownFlags ); |
101 | } elseif ( !is_scalar( $offset ) && !$ret->unknown ) { |
102 | $ret->unknown = self::getInstance( $ownFlags ); |
103 | } |
104 | |
105 | return $ret; |
106 | } |
107 | |
108 | /** |
109 | * @return self |
110 | */ |
111 | public function asMovedToKeys(): self { |
112 | $ret = new self( SecurityCheckPlugin::NO_TAINT ); |
113 | |
114 | foreach ( $this->dims as $k => $val ) { |
115 | $ret->dims[$k] = $val->asMovedToKeys(); |
116 | } |
117 | if ( $this->unknown ) { |
118 | $ret->unknown = $this->unknown->asMovedToKeys(); |
119 | } |
120 | |
121 | $ret->keysFlags = $this->ownFlags; |
122 | |
123 | return $ret; |
124 | } |
125 | |
126 | /** |
127 | * @param Taintedness $taintedness |
128 | * @return Taintedness |
129 | */ |
130 | public function appliedToTaintedness( Taintedness $taintedness ): Taintedness { |
131 | if ( $this->ownFlags ) { |
132 | $ret = $taintedness->withOnly( $this->ownFlags ); |
133 | } else { |
134 | $ret = Taintedness::safeSingleton(); |
135 | } |
136 | foreach ( $this->dims as $k => $val ) { |
137 | $ret = $ret->asMergedWith( |
138 | $val->appliedToTaintedness( $taintedness->getTaintednessForOffsetOrWhole( $k ) ) |
139 | ); |
140 | } |
141 | if ( $this->unknown ) { |
142 | $ret = $ret->asMergedWith( |
143 | $this->unknown->appliedToTaintedness( $taintedness->getTaintednessForOffsetOrWhole( null ) ) |
144 | ); |
145 | } |
146 | $ret = $ret->with( $taintedness->asKeyForForeach()->withOnly( $this->keysFlags )->get() ); |
147 | return $ret; |
148 | } |
149 | |
150 | public function appliedToTaintednessForBackprop( Taintedness $taintedness ): Taintedness { |
151 | if ( $this->ownFlags ) { |
152 | $ret = $taintedness->withOnly( $this->ownFlags ); |
153 | } else { |
154 | $ret = Taintedness::safeSingleton(); |
155 | } |
156 | |
157 | foreach ( $this->dims as $k => $val ) { |
158 | $ret = $ret->withAddedOffsetTaintedness( |
159 | $k, |
160 | $val->appliedToTaintednessForBackprop( $taintedness->getTaintednessForOffsetOrWhole( $k ) ) |
161 | ); |
162 | } |