This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * This source file is proprietary and part of Rebilly. |
||
4 | * |
||
5 | * (c) Rebilly SRL |
||
6 | * Rebilly Ltd. |
||
7 | * Rebilly Inc. |
||
8 | * |
||
9 | * @see https://www.rebilly.com |
||
10 | */ |
||
11 | |||
12 | namespace Rebilly\OpenAPI\PhpUnit; |
||
13 | |||
14 | use JsonSchema\Entity\JsonPointer; |
||
15 | use PHPUnit\Framework\Constraint\Constraint; |
||
16 | use Psr\Http\Message\UriInterface; |
||
17 | use Rebilly\OpenAPI\JsonSchema\Validator; |
||
18 | use Rebilly\OpenAPI\UnexpectedValueException; |
||
19 | use stdClass; |
||
20 | |||
21 | /** |
||
22 | * Constraint that asserts that the URI matches the expected |
||
23 | * allowed schemes, base URI, URI paths and query-params. |
||
24 | */ |
||
25 | final class UriConstraint extends Constraint |
||
26 | { |
||
27 | private $servers; |
||
28 | |||
29 | private $path; |
||
30 | |||
31 | private $pathParameters; |
||
32 | |||
33 | private $queryParameters; |
||
34 | |||
35 | private $validator; |
||
36 | |||
37 | private $errors = []; |
||
38 | |||
39 | 11 | public function __construct( |
|
40 | array $servers, |
||
41 | string $path, |
||
42 | array $pathParameters, |
||
43 | array $queryParameters |
||
44 | ) { |
||
45 | 11 | $this->servers = array_map('strtolower', $servers); |
|
46 | 11 | $this->path = $path; |
|
47 | 11 | $this->pathParameters = array_map([$this, 'normalizeJsonSchema'], $pathParameters); |
|
48 | 11 | $this->queryParameters = array_map([$this, 'normalizeJsonSchema'], $queryParameters); |
|
49 | 11 | $this->validator = new Validator('undefined'); |
|
50 | } |
||
51 | |||
52 | 6 | public function toString(): string |
|
53 | { |
||
54 | 6 | return 'matches an specified URI parts'; |
|
55 | } |
||
56 | |||
57 | 11 | protected function matches($uri): bool |
|
58 | { |
||
59 | 11 | if (!$uri instanceof UriInterface) { |
|
60 | 1 | throw new UnexpectedValueException('The object should implements UriInterface'); |
|
61 | } |
||
62 | |||
63 | 10 | $baseUrl = null; |
|
64 | |||
65 | 10 | foreach ($this->servers as $serverUrl) { |
|
66 | 10 | if (mb_strpos((string) $uri, $serverUrl) === 0) { |
|
67 | 9 | $baseUrl = $serverUrl; |
|
68 | |||
69 | 9 | continue; |
|
70 | } |
||
71 | } |
||
72 | |||
73 | 10 | if ($baseUrl === null) { |
|
74 | 1 | $this->errors[] = [ |
|
75 | 1 | 'property' => 'baseUrl', |
|
76 | 1 | 'message' => sprintf('Unexpected URL, does not found in defined servers (%s)', implode(', ', $this->servers)), |
|
77 | ]; |
||
78 | |||
79 | 1 | return false; |
|
80 | } |
||
81 | |||
82 | 9 | $pathStart = mb_strlen($baseUrl) - mb_strpos($baseUrl, '/', mb_strpos($baseUrl, '://') + 3); |
|
83 | 9 | $path = mb_substr($uri->getPath(), $pathStart + 1); |
|
84 | 9 | $actualSegments = $this->splitString('#\/#', $path); |
|
85 | 9 | $expectedSegments = $this->splitString('#\/#', $this->path); |
|
86 | |||
87 | 9 | if (count($actualSegments) !== count($expectedSegments)) { |
|
88 | 1 | $this->errors[] = [ |
|
89 | 1 | 'property' => 'path', |
|
90 | 1 | 'message' => "Unexpected URI path, does not match the template ({$this->path})", |
|
91 | ]; |
||
92 | |||
93 | 1 | return false; |
|
94 | } |
||
95 | |||
96 | 8 | foreach ($expectedSegments as $i => $expectedSegment) { |
|
97 | 8 | $actualSegment = $actualSegments[$i]; |
|
98 | 8 | mb_strpos($expectedSegment, '{') === false |
|
99 | 8 | ? $this->assertPathSegment($expectedSegment, $actualSegment) |
|
100 | 6 | : $this->assertPathParam($expectedSegment, $actualSegment); |
|
101 | } |
||
102 | |||
103 | 8 | if (!empty($this->errors)) { |
|
104 | 2 | return false; |
|
105 | } |
||
106 | |||
107 | 6 | parse_str($uri->getQuery(), $actualQueryParams); |
|
108 | |||
109 | // TODO: Assert query params |
||
110 | 6 | View Code Duplication | foreach ($this->queryParameters as $name => $queryParamSchema) { |
0 ignored issues
–
show
|
|||
111 | 6 | if (isset($actualQueryParams[$name])) { |
|
112 | 2 | $actualQueryParam = $actualQueryParams[$name]; |
|
113 | |||
114 | // TODO: Consider to disallow non-string params in query, that make no sense |
||
115 | 2 | $actualQueryParam = $this->normalizeNumericString($actualQueryParam); |
|
116 | |||
117 | 2 | $this->errors = array_merge( |
|
118 | 2 | $this->errors, |
|
119 | 2 | $this->validator->validate($actualQueryParam, $queryParamSchema, new JsonPointer('#/query')) |
|
120 | ); |
||
121 | 4 | } elseif (isset($queryParamSchema->required) && $queryParamSchema->required) { |
|
122 | 1 | $this->errors[] = [ |
|
123 | 1 | 'property' => 'query', |
|
124 | 1 | 'message' => "Missing required query param ({$name})", |
|
125 | ]; |
||
126 | } |
||
127 | } |
||
128 | |||
129 | 6 | return empty($this->errors); |
|
130 | } |
||
131 | |||
132 | 6 | protected function failureDescription($other): string |
|
133 | { |
||
134 | 6 | return json_encode((object) $this->normalizeUri($other)) . ' ' . $this->toString(); |
|
135 | } |
||
136 | |||
137 | 6 | protected function additionalFailureDescription($other): string |
|
138 | { |
||
139 | 6 | return $this->validator->serializeErrors($this->errors); |
|
140 | } |
||
141 | |||
142 | 8 | private function assertPathSegment(string $expectedSegment, string $actualSegment): void |
|
143 | { |
||
144 | 8 | if ($actualSegment !== $expectedSegment) { |
|
145 | 1 | $this->errors[] = [ |
|
146 | 1 | 'property' => 'path', |
|
147 | 1 | 'message' => "Missing path segment ({$expectedSegment})", |
|
148 | ]; |
||
149 | } |
||
150 | } |
||
151 | |||
152 | 6 | private function assertPathParam(string $expectedSegment, string $actualSegment): void |
|
153 | { |
||
154 | 6 | $pathParamSchema = $this->pathParameters[mb_substr($expectedSegment, 1, -1)]; |
|
155 | |||
156 | // TODO: Consider to disallow non-string params in path, that make no sense |
||
157 | 6 | $actualSegment = $this->normalizeNumericString($actualSegment); |
|
158 | |||
159 | 6 | $this->errors = array_merge( |
|
160 | 6 | $this->errors, |
|
161 | 6 | $this->validator->validate($actualSegment, $pathParamSchema, new JsonPointer('#/path')) |
|
162 | ); |
||
163 | } |
||
164 | |||
165 | 6 | private static function normalizeUri(UriInterface $uri): array |
|
0 ignored issues
–
show
|
|||
166 | { |
||
167 | return [ |
||
168 | 6 | 'schema' => $uri->getScheme(), |
|
169 | 6 | 'host' => $uri->getHost(), |
|
170 | 6 | 'path' => $uri->getPath(), |
|
171 | ]; |
||
172 | } |
||
173 | |||
174 | 11 | private static function normalizeJsonSchema($schema): stdClass |
|
175 | { |
||
176 | 11 | return (object) $schema; |
|
177 | } |
||
178 | |||
179 | /** |
||
180 | * Cast numeric values, JSON validator does not do it. |
||
181 | * |
||
182 | * @param mixed $value |
||
183 | * |
||
184 | * @return mixed |
||
185 | */ |
||
186 | 6 | private static function normalizeNumericString($value) |
|
187 | { |
||
188 | 6 | if (is_numeric($value)) { |
|
189 | 4 | $value += 0; |
|
190 | } |
||
191 | |||
192 | 6 | return $value; |
|
193 | } |
||
194 | |||
195 | 9 | private static function splitString(string $pattern, string $subject): array |
|
196 | { |
||
197 | 9 | return preg_split($pattern, $subject, -1, PREG_SPLIT_NO_EMPTY); |
|
198 | } |
||
199 | } |
||
200 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.