Issues (23)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Encoder/Encoder.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php declare(strict_types=1);
2
3
namespace Neomerx\JsonApi\Encoder;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Neomerx\JsonApi\Contracts\Encoder\EncoderInterface;
22
use Neomerx\JsonApi\Contracts\Factories\FactoryInterface;
23
use Neomerx\JsonApi\Contracts\Parser\DocumentDataInterface;
24
use Neomerx\JsonApi\Contracts\Parser\IdentifierInterface;
25
use Neomerx\JsonApi\Contracts\Parser\ParserInterface;
26
use Neomerx\JsonApi\Contracts\Parser\ResourceInterface;
27
use Neomerx\JsonApi\Contracts\Representation\BaseWriterInterface;
28
use Neomerx\JsonApi\Contracts\Representation\DocumentWriterInterface;
29
use Neomerx\JsonApi\Contracts\Representation\ErrorWriterInterface;
30
use Neomerx\JsonApi\Contracts\Schema\ErrorInterface;
31
use Neomerx\JsonApi\Contracts\Schema\SchemaContainerInterface;
32
use Neomerx\JsonApi\Exceptions\InvalidArgumentException;
33
use Neomerx\JsonApi\Factories\Factory;
34
35
/**
36
 * @package Neomerx\JsonApi
37
 *
38
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
39
 */
40
class Encoder implements EncoderInterface
41
{
42
    use EncoderPropertiesTrait;
43
44
    /**
45
     * Default value.
46
     */
47
    const DEFAULT_URL_PREFIX = '';
48
49
    /**
50
     * Default value.
51
     */
52
    const DEFAULT_INCLUDE_PATHS = [];
53
54
    /**
55
     * Default value.
56
     */
57
    const DEFAULT_FIELD_SET_FILTERS = [];
58
59
    /**
60
     * Default encode options.
61
     *
62
     * @link http://php.net/manual/en/function.json-encode.php
63
     */
64
    const DEFAULT_JSON_ENCODE_OPTIONS = 0;
65
66
    /**
67
     * Default encode depth.
68
     *
69
     * @link http://php.net/manual/en/function.json-encode.php
70
     */
71
    const DEFAULT_JSON_ENCODE_DEPTH = 512;
72
73
    /**
74
     * @param FactoryInterface         $factory
75
     * @param SchemaContainerInterface $container
76
     */
77 87
    public function __construct(
78
        FactoryInterface $factory,
79
        SchemaContainerInterface $container
80
    ) {
81 87
        $this->setFactory($factory)->setContainer($container)->reset();
82 87
    }
83
84
    /**
85
     * Create encoder instance.
86
     *
87
     * @param array $schemas Schema providers.
88
     *
89
     * @return EncoderInterface
90
     */
91 87
    public static function instance(array $schemas = []): EncoderInterface
92
    {
93 87
        $factory   = static::createFactory();
94 87
        $container = $factory->createSchemaContainer($schemas);
0 ignored issues
show
$schemas is of type array, but the function expects a object<Neomerx\JsonApi\C...cts\Factories\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
95 87
        $encoder   = $factory->createEncoder($container);
96
97 87
        return $encoder;
98
    }
99
100
    /**
101
     * @inheritdoc
102
     */
103 70
    public function encodeData($data): string
104
    {
105
        // encode to json
106 70
        $array  = $this->encodeDataToArray($data);
107 67
        $result = $this->encodeToJson($array);
108
109 67
        return $result;
110
    }
111
112
    /**
113
     * @inheritdoc
114
     */
115 5
    public function encodeIdentifiers($data): string
116
    {
117
        // encode to json
118 5
        $array  = $this->encodeIdentifiersToArray($data);
119 5
        $result = $this->encodeToJson($array);
120
121 5
        return $result;
122
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127 3
    public function encodeError(ErrorInterface $error): string
128
    {
129
        // encode to json
130 3
        $array  = $this->encodeErrorToArray($error);
131 3
        $result = $this->encodeToJson($array);
132
133 3
        return $result;
134
    }
135
136
    /**
137
     * @inheritdoc
138
     */
139 3
    public function encodeErrors(iterable $errors): string
140
    {
141
        // encode to json
142 3
        $array  = $this->encodeErrorsToArray($errors);
143 3
        $result = $this->encodeToJson($array);
144
145 3
        return $result;
146
    }
147
148
    /**
149
     * @inheritdoc
150
     */
151 1
    public function encodeMeta($meta): string
152
    {
153
        // encode to json
154 1
        $array  = $this->encodeMetaToArray($meta);
155 1
        $result = $this->encodeToJson($array);
156
157 1
        return $result;
158
    }
159
160
    /**
161
     * @return FactoryInterface
162
     */
163 77
    protected static function createFactory(): FactoryInterface
164
    {
165 77
        return new Factory();
166
    }
167
168
    /**
169
     * @param object|iterable|null $data Data to encode.
170
     *
171
     * @return array
172
     *
173
     * @SuppressWarnings(PHPMD.ElseExpression)
174
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
175
     */
176 71
    protected function encodeDataToArray($data): array
177
    {
178 71
        if (\is_array($data) === false && \is_object($data) === false && $data !== null) {
179 1
            throw new InvalidArgumentException();
180
        }
181
182 70
        $context = $this->getFactory()->createParserContext($this->getFieldSets(), $this->getIncludePaths());
183 70
        $parser  = $this->getFactory()->createParser($this->getSchemaContainer(), $context);
184 70
        $writer  = $this->createDocumentWriter();
185 70
        $filter  = $this->getFactory()->createFieldSetFilter($this->getFieldSets());
186
187
        // write header
188 70
        $this->writeHeader($writer);
189
190
        // write body
191 70
        foreach ($parser->parse($data, $this->getIncludePaths()) as $item) {
192 69
            if ($item instanceof ResourceInterface) {
193 61
                if ($item->getPosition()->getLevel() > ParserInterface::ROOT_LEVEL) {
194 24
                    if ($filter->shouldOutputRelationship($item->getPosition()) === true) {
195 24
                        $writer->addResourceToIncluded($item, $filter);
196
                    }
197
                } else {
198 61
                    $writer->addResourceToData($item, $filter);
199
                }
200 69
            } elseif ($item instanceof IdentifierInterface) {
201 1
                \assert($item->getPosition()->getLevel() <= ParserInterface::ROOT_LEVEL);
202 1
                $writer->addIdentifierToData($item);
203 View Code Duplication
            } else {
204 69
                \assert($item instanceof DocumentDataInterface);
205 69
                \assert($item->getPosition()->getLevel() === 0);
206 69
                if ($item->isCollection() === true) {
207 21
                    $writer->setDataAsArray();
208 50
                } elseif ($item->isNull() === true) {
209 69
                    $writer->setNullToData();
210
                }
211
            }
212
        }
213
214
        // write footer
215 68
        $this->writeFooter($writer);
216
217 68
        $array = $writer->getDocument();
218
219 68
        return $array;
220
    }
221
222
    /**
223
     * @param object|iterable|null $data Data to encode.
224
     *
225
     * @return array
226
     *
227
     * @SuppressWarnings(PHPMD.ElseExpression)
228
     */
229 6
    protected function encodeIdentifiersToArray($data): array
230
    {
231 6
        $context = $this->getFactory()->createParserContext($this->getFieldSets(), $this->getIncludePaths());
232 6
        $parser  = $this->getFactory()->createParser($this->getSchemaContainer(), $context);
233 6
        $writer  = $this->createDocumentWriter();
234 6
        $filter  = $this->getFactory()->createFieldSetFilter($this->getFieldSets());
235
236
        // write header
237 6
        $this->writeHeader($writer);
238
239
        // write body
240 6
        $includePaths   = $this->getIncludePaths();
241 6
        $expectIncluded = empty($includePaths) === false;
242
243
        // https://github.com/neomerx/json-api/issues/218
244
        //
245
        // if we expect included resources we have to include top level resources in `included` as well
246
        // Spec:
247
        //
248
        // GET /articles/1/relationships/comments?include=comments.author HTTP/1.1
249
        // Accept: application/vnd.api+json
250
        //
251
        // In this case, the primary data would be a collection of resource identifier objects that
252
        // represent linkage to comments for an article, while the full comments and comment authors
253
        // would be returned as included data.
254
255 6
        foreach ($parser->parse($data, $includePaths) as $item) {
256 6
            if ($item instanceof ResourceInterface) {
257 4
                if ($item->getPosition()->getLevel() > ParserInterface::ROOT_LEVEL) {
258 1
                    \assert($expectIncluded === true);
259 1
                    if ($filter->shouldOutputRelationship($item->getPosition()) === true) {
260 1
                        $writer->addResourceToIncluded($item, $filter);
261
                    }
262
                } else {
263 4
                    $writer->addIdentifierToData($item);
264 4
                    if ($expectIncluded === true) {
265 4
                        $writer->addResourceToIncluded($item, $filter);
266
                    }
267
                }
268 6
            } elseif ($item instanceof IdentifierInterface) {
269 1
                \assert($item->getPosition()->getLevel() <= ParserInterface::ROOT_LEVEL);
270 1
                $writer->addIdentifierToData($item);
271 View Code Duplication
            } else {
272 6
                \assert($item instanceof DocumentDataInterface);
273 6
                \assert($item->getPosition()->getLevel() === 0);
274 6
                if ($item->isCollection() === true) {
275 2
                    $writer->setDataAsArray();
276 5
                } elseif ($item->isNull() === true) {
277 6
                    $writer->setNullToData();
278
                }
279
            }
280
        }
281
282
        // write footer
283 6
        $this->writeFooter($writer);
284
285 6
        $array = $writer->getDocument();
286
287 6
        return $array;
288
    }
289
290
    /**
291
     * @param ErrorInterface $error
292
     *
293
     * @return array
294
     */
295 4
    protected function encodeErrorToArray(ErrorInterface $error): array
296
    {
297 4
        $writer = $this->createErrorWriter();
298
299
        // write header
300 4
        $this->writeHeader($writer);
301
302
        // write body
303 4
        $writer->addError($error);
304
305
        // write footer
306 4
        $this->writeFooter($writer);
307
308 4
        $array = $writer->getDocument();
309
310 4
        return $array;
311
    }
312
313
    /**
314
     * @param iterable $errors
315
     *
316
     * @return array
317
     */
318 4
    protected function encodeErrorsToArray(iterable $errors): array
319
    {
320 4
        $writer = $this->createErrorWriter();
321
322
        // write header
323 4
        $this->writeHeader($writer);
324
325
        // write body
326 4
        foreach ($errors as $error) {
327 3
            \assert($error instanceof ErrorInterface);
328 3
            $writer->addError($error);
329
        }
330
331
        // write footer
332 4
        $this->writeFooter($writer);
333
334
        // encode to json
335 4
        $array = $writer->getDocument();
336
337 4
        return $array;
338
    }
339
340
    /**
341
     * @param $meta
342
     *
343
     * @return array
344
     */
345 2
    protected function encodeMetaToArray($meta): array
346
    {
347 2
        $this->withMeta($meta);
348
349 2
        $writer = $this->getFactory()->createDocumentWriter();
350
351 2
        $writer->setUrlPrefix($this->getUrlPrefix());
352
353
        // write header
354 2
        $this->writeHeader($writer);
355
356
        // write footer
357 2
        $this->writeFooter($writer);
358
359
        // encode to json
360 2
        $array = $writer->getDocument();
361
362 2
        return $array;
363
    }
364
365
    /**
366
     * @param BaseWriterInterface $writer
367
     *
368
     * @return void
369
     */
370 85
    protected function writeHeader(BaseWriterInterface $writer): void
371
    {
372 85
        if ($this->hasMeta() === true) {
373 6
            $writer->setMeta($this->getMeta());
374
        }
375
376 85
        if ($this->hasJsonApiVersion() === true) {
377 2
            $writer->setJsonApiVersion($this->getJsonApiVersion());
378
        }
379
380 85
        if ($this->hasJsonApiMeta() === true) {
381 2
            $writer->setJsonApiMeta($this->getJsonApiMeta());
382
        }
383
384 85
        if ($this->hasLinks() === true) {
385 5
            $writer->setLinks($this->getLinks());
386
        }
387
388 85
        if ($this->hasProfile() === true) {
389 1
            $writer->setProfile($this->getProfile());
390
        }
391 85
    }
392
393
    /**
394
     * @param BaseWriterInterface $writer
395
     *
396
     * @return void
397
     */
398 83
    protected function writeFooter(BaseWriterInterface $writer): void
399
    {
400 83
        \assert($writer !== null);
401 83
    }
402
403
    /**
404
     * Encode array to JSON.
405
     *
406
     * @param array $document
407
     *
408
     * @return string
409
     */
410 79
    protected function encodeToJson(array $document): string
411
    {
412 79
        return \json_encode($document, $this->getEncodeOptions(), $this->getEncodeDepth());
413
    }
414
415
    /**
416
     * @return DocumentWriterInterface
417
     */
418 76
    private function createDocumentWriter(): DocumentWriterInterface
419
    {
420 76
        $writer = $this->getFactory()->createDocumentWriter();
421 76
        $writer->setUrlPrefix($this->getUrlPrefix());
422
423 76
        return $writer;
424
    }
425
426
    /**
427
     * @return ErrorWriterInterface
428
     */
429 7
    private function createErrorWriter(): ErrorWriterInterface
430
    {
431 7
        $writer = $this->getFactory()->createErrorWriter();
432 7
        $writer->setUrlPrefix($this->getUrlPrefix());
433
434 7
        return $writer;
435
    }
436
}
437