Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.82% covered (success)
98.82%
84 / 85
88.89% covered (warning)
88.89%
8 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ParamLinksOffsets
98.82% covered (success)
98.82%
84 / 85
88.89% covered (warning)
88.89%
8 / 9
47
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
 getInstance
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 asMergedWith
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 withoutShape
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 withOffsetPushed
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
8
 asMovedToKeys
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 appliedToTaintedness
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 appliedToTaintednessForBackprop
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
4
 isEmpty
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
7
 __toString
n/a
0 / 0
n/a
0 / 0
6
1<?php declare( strict_types=1 );
2
3namespace SecurityCheckPlugin;
4
5use ast\Node;
6
7/**
8 * Tree-like object of possible offset combinations for partial links to a parameter
9 */
10class 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        }