Issues (8)

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/YamlDefinitionLoader.php (1 issue)

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
2
3
namespace TheCodingMachine\Definition;
4
5
use Assembly\FactoryCallDefinition;
6
use Assembly\ObjectDefinition;
7
use Assembly\ParameterDefinition;
8
use Assembly\Reference;
9
use Interop\Container\Definition\DefinitionInterface;
10
use Interop\Container\Definition\DefinitionProviderInterface;
11
use Symfony\Component\Yaml\Exception\ParseException;
12
use Symfony\Component\Yaml\Parser;
13
use TheCodingMachine\Definition\Exception\FileNotFoundException;
14
use TheCodingMachine\Definition\Exception\InvalidArgumentException;
15
16
class YamlDefinitionLoader implements DefinitionProviderInterface
17
{
18
    /**
19
     * The name of the YAML file to be loaded.
20
     *
21
     * @var string
22
     */
23
    private $fileName;
24
25
    public function __construct($fileName)
26
    {
27
        $this->fileName = $fileName;
28
    }
29
30
    /**
31
     * Returns the definition to register in the container.
32
     *
33
     * @return DefinitionInterface[]
34
     */
35
    public function getDefinitions()
36
    {
37
        $content = $this->loadFile($this->fileName);
38
39
        // empty file
40
        if (null === $content) {
41
            return [];
42
        }
43
44
        // imports
45
        $definitions = $this->parseImports($content);
46
47
        // parameters
48
        if (isset($content['parameters'])) {
49
            if (!is_array($content['parameters'])) {
50
                throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $this->fileName));
51
            }
52
53
            foreach ($content['parameters'] as $key => $value) {
54
                $definitions[$key] = new ParameterDefinition($value);
55
                //$this->container->setParameter($key, $this->resolveServices($value));
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
56
            }
57
        }
58
59
        // services
60
        $serviceDefinitions = $this->parseDefinitions($content);
61
        $definitions = $definitions + $serviceDefinitions;
62
63
        return $definitions;
64
    }
65
66
    /**
67
     * Parses all imports.
68
     *
69
     * @param array $content
70
     * @param $content
71
     *
72
     * @return DefinitionInterface[]
73
     */
74
    private function parseImports($content)
75
    {
76
        if (!isset($content['imports'])) {
77
            return [];
78
        }
79
80
        if (!is_array($content['imports'])) {
81
            throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $this->fileName));
82
        }
83
84
        $additionalDefinitions = [];
85
86
        foreach ($content['imports'] as $import) {
87
            if (!is_array($import)) {
88
                throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $this->fileName));
89
            }
90
91
            if (isset($import['ignore_errors'])) {
92
                throw new InvalidArgumentException(sprintf('The "ignore_errors" key is not supported in YamlDefinitionLoader. This is a Symfony specific syntax. Check your YAML syntax.', $this->fileName));
93
            }
94
95
            $importFileName = $import['resource'];
96
97
            if (strpos($importFileName, '/') !== 0) {
98
                $importFileName = dirname($this->fileName).'/'.$importFileName;
99
            }
100
101
            $yamlDefinitionLoader = new self($importFileName);
102
            $newDefinitions = $yamlDefinitionLoader->getDefinitions();
103
104
            $additionalDefinitions = $newDefinitions + $additionalDefinitions;
105
        }
106
107
        return $additionalDefinitions;
108
    }
109
110
    /**
111
     * Parses definitions.
112
     *
113
     * @param array $content
114
     *
115
     * @return DefinitionInterface[]
116
     */
117
    private function parseDefinitions($content)
118
    {
119
        if (!isset($content['services'])) {
120
            return [];
121
        }
122
123
        if (!is_array($content['services'])) {
124
            throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $this->fileName));
125
        }
126
127
        $definitions = [];
128
129
        foreach ($content['services'] as $id => $service) {
130
            $definitions[$id] = $this->parseDefinition($id, $service);
131
        }
132
133
        return $definitions;
134
    }
135
136
    /**
137
     * Parses a definition.
138
     *
139
     * @param string $id
140
     * @param array  $service
141
     *
142
     * @return DefinitionInterface
143
     *
144
     * @throws InvalidArgumentException When tags are invalid
145
     */
146
    private function parseDefinition($id, $service)
147
    {
148
        if (is_string($service) && 0 === strpos($service, '@')) {
149
            return new Reference(substr($service, 1));
150
        }
151
152 View Code Duplication
        if (!is_array($service)) {
153
            throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $this->fileName));
154
        }
155
156
        if (isset($service['alias'])) {
157
            if (isset($service['public'])) {
158
                throw new InvalidArgumentException('The "public" key is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
159
            }
160
161
            return new Reference($service['alias']);
162
        }
163
164
        if (isset($service['parent'])) {
165
            throw new InvalidArgumentException('Definition decorators via the "parent" key are not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
166
        }
167
168
        $definition = null;
169
170
        if (isset($service['class'])) {
171
            $definition = new ObjectDefinition($service['class']);
172
173
            if (isset($service['arguments'])) {
174
                $arguments = $this->resolveServices($service['arguments']);
175
                foreach ($arguments as $argument) {
176
                    $definition->addConstructorArgument($argument);
177
                }
178
            }
179
180
            if (isset($service['properties'])) {
181
                $properties = $this->resolveServices($service['properties']);
182
                foreach ($properties as $name => $property) {
183
                    $definition->addPropertyAssignment($name, $property);
184
                }
185
            }
186
187
            if (isset($service['calls'])) {
188 View Code Duplication
                if (!is_array($service['calls'])) {
189
                    throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $this->fileName));
190
                }
191
192
                foreach ($service['calls'] as $call) {
193
                    if (isset($call['method'])) {
194
                        $method = $call['method'];
195
                        $args = isset($call['arguments']) ? $this->resolveServices($call['arguments']) : array();
196
                    } else {
197
                        $method = $call[0];
198
                        $args = isset($call[1]) ? $this->resolveServices($call[1]) : array();
199
                    }
200
201
                    array_unshift($args, $method);
202
                    call_user_func_array([$definition, 'addMethodCall'], $args);
203
                }
204
            }
205
        }
206
207
        if (isset($service['factory'])) {
208
            if (is_string($service['factory'])) {
209
                if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) {
210
                    $parts = explode(':', $service['factory']);
211
                    $definition = new FactoryCallDefinition($this->resolveServices('@'.$parts[0]), $parts[1]);
212
                } elseif (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') !== false) {
213
                    $parts = explode('::', $service['factory']);
214
                    $definition = new FactoryCallDefinition($parts[0], $parts[1]);
215
                } else {
216
                    throw new InvalidArgumentException('A "factory" must be in the format "service_name:method_name" or "class_name::method_name".Got "'.$service['factory'].'"');
217
                }
218
            } else {
219
                $definition = new FactoryCallDefinition($this->resolveServices($service['factory'][0]), $service['factory'][1]);
220
            }
221
222
            if (isset($service['arguments'])) {
223
                $arguments = $this->resolveServices($service['arguments']);
224
                call_user_func_array([$definition, 'setArguments'], $arguments);
225
            }
226
        }
227
228
        if (isset($service['shared'])) {
229
            throw new InvalidArgumentException('The "shared" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
230
        }
231
232
        if (isset($service['synthetic'])) {
233
            throw new InvalidArgumentException('The "synthetic" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
234
        }
235
236
        if (isset($service['lazy'])) {
237
            throw new InvalidArgumentException('The "lazy" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
238
        }
239
240
        if (isset($service['public'])) {
241
            // TODO: add support for private services => mapping to nested definitions
242
            throw new InvalidArgumentException('The "public" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
243
        }
244
245
        if (isset($service['abstract'])) {
246
            throw new InvalidArgumentException('The "abstract" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
247
        }
248
249
        if (array_key_exists('deprecated', $service)) {
250
            throw new InvalidArgumentException('The "deprecated" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
251
        }
252
253
        if (isset($service['file'])) {
254
            throw new InvalidArgumentException('The "file" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
255
        }
256
257
        if (isset($service['configurator'])) {
258
            throw new InvalidArgumentException('The "configurator" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
259
        }
260
261
        if (isset($service['tags'])) {
262
            throw new InvalidArgumentException('The "tags" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
263
        }
264
265
        if (isset($service['decorates'])) {
266
            throw new InvalidArgumentException('The "decorates" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
267
        }
268
269
        if (isset($service['autowire'])) {
270
            throw new InvalidArgumentException('The "autowire" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
271
        }
272
273
        if (isset($service['autowiring_types'])) {
274
            throw new InvalidArgumentException('The "autowiring_types" key in instance definitions is not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
275
        }
276
277
        if ($definition === null) {
278
            throw new InvalidArgumentException(sprintf('Invalid service declaration for "%s" (in %s). You should specify at least a "class", "alias" or "factory" key.', $id, $this->fileName));
279
        }
280
281
        return $definition;
282
    }
283
284
    /**
285
     * Loads a YAML file.
286
     *
287
     * @param string $file
288
     *
289
     * @return array The file content
290
     *
291
     * @throws InvalidArgumentException when the given file is not a local file or when it does not exist
292
     */
293
    protected function loadFile($file)
294
    {
295
        if (!stream_is_local($file)) {
296
            throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file));
297
        }
298
299
        if (!is_readable($file)) {
300
            throw new FileNotFoundException(sprintf('The file "%s" does not exist or is not readable.', $file));
301
        }
302
303
        $yamlParser = new Parser();
304
305
        try {
306
            $configuration = $yamlParser->parse(file_get_contents($file));
307
        } catch (ParseException $e) {
308
            throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e);
309
        }
310
311
        return $this->validate($configuration, $file);
312
    }
313
314
    /**
315
     * Validates a YAML file.
316
     *
317
     * @param mixed  $content
318
     * @param string $file
319
     *
320
     * @return array
321
     *
322
     * @throws InvalidArgumentException When service file is not valid
323
     */
324
    private function validate($content, $file)
325
    {
326
        if (null === $content) {
327
            return $content;
328
        }
329
330
        if (!is_array($content)) {
331
            throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
332
        }
333
334
        foreach ($content as $namespace => $data) {
335
            if (in_array($namespace, array('imports', 'parameters', 'services'))) {
336
                continue;
337
            }
338
339
            throw new InvalidArgumentException(sprintf(
340
                'Cannot load the configuration for file "%s". Unexpected "%s" key. Expecting one of "imports", "parameters", "services".',
341
                $file,
342
                $namespace
343
            ));
344
        }
345
346
        return $content;
347
    }
348
349
    /**
350
     * Resolves services.
351
     *
352
     * @param string|array $value
353
     *
354
     * @return array|string|Reference
355
     */
356
    private function resolveServices($value)
357
    {
358
        if (is_array($value)) {
359
            return array_map(array($this, 'resolveServices'), $value);
360
        } elseif (is_string($value) &&  0 === strpos($value, '@=')) {
361
            throw new InvalidArgumentException('Expressions (starting by "@=") are not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
362
        } elseif (is_string($value) &&  0 === strpos($value, '@')) {
363
            if (0 === strpos($value, '@@')) {
364
                return substr($value, 1);
365
            } elseif (0 === strpos($value, '@?')) {
366
                throw new InvalidArgumentException('Optional services (starting by "@?") are not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
367
            } else {
368
                $value = substr($value, 1);
369
                if ('=' === substr($value, -1)) {
370
                    throw new InvalidArgumentException('Non-strict services (ending with "=") are not supported by YamlDefinitionLoader. This is a Symfony specific feature.');
371
                } else {
372
                }
373
374
                return new Reference($value);
375
            }
376
        } else {
377
            return $value;
378
        }
379
    }
380
}
381