Issues (245)

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/Config.php (10 issues)

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
/**
4
 * \AppserverIo\Doppelgaenger\Config
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Bernhard Wick <[email protected]>
15
 * @copyright 2015 TechDivision GmbH - <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/doppelgaenger
18
 * @link      http://www.appserver.io/
19
 */
20
21
namespace AppserverIo\Doppelgaenger;
22
23
use AppserverIo\Doppelgaenger\Interfaces\ConfigInterface;
24
use AppserverIo\Doppelgaenger\Exceptions\ConfigException;
25
use AppserverIo\Doppelgaenger\Utils\Formatting;
26
use AppserverIo\Doppelgaenger\Utils\InstanceContainer;
27
use AppserverIo\Doppelgaenger\Dictionaries\ReservedKeywords;
28
29
/**
30
 * This class implements the access point for our global (oh no!) configuration
31
 *
32
 * @author    Bernhard Wick <[email protected]>
33
 * @copyright 2015 TechDivision GmbH - <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/appserver-io/doppelgaenger
36
 * @link      http://www.appserver.io/
37
 */
38
class Config implements ConfigInterface
39
{
40
    /**
41
     * @const string DEFAULT_CONFIG Name of the default configuration file
42
     */
43
    const DEFAULT_CONFIG = 'config.default.json';
44
45
    /**
46
     * The delimiter for values names as they are used externally
47
     *
48
     * @const string VALUE_NAME_DELIMITER
49
     */
50
    const VALUE_NAME_DELIMITER = '/';
51
52
    /**
53
     * @var string $context The context for this instance e.g. app based configurations
54
     */
55
    protected $context;
56
57
    /**
58
     * @var array $config Configuration array
59
     */
60
    protected $config = array();
61
62
    /**
63
     * Default constructor
64
     */
65
    public function __construct()
66
    {
67
        $this->load(__DIR__ . DIRECTORY_SEPARATOR . self::DEFAULT_CONFIG);
68
    }
69
70
    /**
71
     * Will flatten a multi-level associative array into a one-level one
72
     *
73
     * @param array  $array       The array to flatten
74
     * @param string $parentKey   The key of the parent array, used within recursion
75
     * @param bool   $initialCall Is this the initial call or recursion?
76
     *
77
     * @return array
78
     */
79
    protected function flattenArray(array $array, $parentKey = '', $initialCall = true)
80
    {
81
        $result = array();
82
        foreach ($array as $key => $value) {
83
            // If it is an array not containing integer keys (so no nested config element) we have to get recursive
84
            if (is_array($value) && @!is_int(array_keys($value)[0])) {
85
                $value = $this->flattenArray($value, $key, false);
86
            }
87
88
            // Save the result with a newly combined key
89
            $result[trim($parentKey . self::VALUE_NAME_DELIMITER . $key, self::VALUE_NAME_DELIMITER)] = $value;
90
        }
91
92
        // If we are within the initial call we would like to do a final flattening and sorting process
93
        if ($initialCall === true) {
94
            // No iterate all the entries and array shift them if they are arrays
95
            foreach ($result as $key => $value) {
96
                if (is_array($value)) {
97
                    unset($result[$key]);
98
                    $result = array_merge($result, $value);
99
                }
100
            }
101
102
            // Sort the whole thing as we might have mixed it up little
103
            ksort($result);
104
        }
105
        return $result;
106
    }
107
108
    /**
109
     * Sets a value to specific content
110
     *
111
     * @param string $valueName The value to set
112
     * @param mixed  $value     The actual content for the value
113
     *
114
     * @return void
115
     */
116
    public function setValue($valueName, $value)
117
    {
118
        // Set the value
119
        $this->config[$valueName] = $value;
120
    }
121
122
    /**
123
     * Might be called after the all config data has been loaded.
124
     * Will store all config object instances which might be needed later in the instance container util.
125
     *
126
     * @return void
127
     */
128
    public function storeInstances()
129
    {
130
        $instanceContainer = new InstanceContainer();
131
132
        // One thing we need is the logger instance (if any)
133
        if ($this->hasValue('enforcement/logger')) {
134
            $logger = $this->extractLoggerInstance($this->config);
135
136
            if ($logger !== false) {
137
                $instanceContainer[ReservedKeywords::LOGGER_CONTAINER_ENTRY] = $logger;
138
            }
139
        }
140
    }
141
142
    /**
143
     * Extends a value by specific content. If the value is an array it will be merged, otherwise it will be
144
     * string-concatinated to the end of the current value
145
     *
146
     * @param string $valueName The value to extend
147
     * @param string $value     The actual content for the value we want to add to the original
148
     *
149
     * @return void
150
     */
151
    public function extendValue($valueName, $value)
152
    {
153
        // Get the original value
154
        $originalValue = $this->getValue($valueName);
155
156
        // If we got an array
157
        if (is_array($value)) {
158
            if (is_array($originalValue)) {
159
                $newValue = array_merge($originalValue, $value);
160
0 ignored issues
show
Blank line found at end of control structure
Loading history...
161
            } else {
162
                $newValue = array_merge(array($originalValue), $value);
163
            }
164
0 ignored issues
show
Blank line found at end of control structure
Loading history...
165
        } else {
166
            $newValue = $originalValue . $value;
167
        }
168
169
        // Finally set the new value
170
        $this->setValue($valueName, $newValue);
171
    }
172
173
    /**
174
     * Will extract an logger instance from the configuration array.
175
     * Returns false on error.
176
     *
177
     * @param array $configArray The config to extract the logger instance from
178
     *
179
     * @return object|boolean
180
     */
181
    protected function extractLoggerInstance(array $configArray)
182
    {
183
        if (isset($configArray['enforcement/logger'])) {
184
            // Get the logger
185
            $logger = $configArray['enforcement/logger'];
186
            if (is_string($logger)) {
187
                $logger = new $logger;
188
            }
189
190
            // Return the logger
191
            return $logger;
192
0 ignored issues
show
Blank line found at end of control structure
Loading history...
193
        } else {
194
            return false;
195
        }
196
    }
197
198
    /**
199
     * Unsets a specific config value
200
     *
201
     * @param string $value The value to unset
202
     *
203
     * @return void
204
     */
205
    public function unsetValue($value)
206
    {
207
        if (isset($this->config[$value])) {
208
            // unset the value
209
            unset($this->config[$value]);
210
        }
211
    }
212
213
    /**
214
     * Returns the content of a specific config value
215
     *
216
     * @param string $value The value to get the content for
217
     *
218
     * @throws \AppserverIo\Doppelgaenger\Exceptions\ConfigException
219
     *
220
     * @return mixed
221
     */
222
    public function getValue($value)
223
    {
224
        // check if server var is set
225
        if (isset($this->config[$value])) {
226
            // return server vars value
227
            return $this->config[$value];
228
        }
229
        // throw exception
230
        throw new ConfigException(sprintf("Config value %s does not exist.", $value));
231
    }
232
233
    /**
234
     * Checks if value exists for given value
235
     *
236
     * @param string $value The value to check
237
     *
238
     * @return boolean Weather it has value (true) or not (false)
239
     */
240
    public function hasValue($value)
241
    {
242
        // check if server var is set
243
        if (!isset($this->config[$value])) {
244
            return false;
245
        }
246
247
        return true;
248
    }
249
250
    /**
251
     * Will load a certain configuration file into this instance. Might throw an exception if the file is not valid
252
     *
253
     * @param string $file The path of the configuration file we should load
254
     *
255
     * @return \AppserverIo\Doppelgaenger\Config
256
     *
257
     * @throws \AppserverIo\Doppelgaenger\Exceptions\ConfigException
258
     */
259
    public function load($file)
260
    {
261
        // Do we load a valid config?
262
        $configCandidate = $this->validate($file);
263
        if ($configCandidate === false) {
264
            throw new ConfigException(sprintf('Attempt to load invalid configuration file %s', $file));
265
        }
266
267
        $this->config = array_replace_recursive($this->config, $configCandidate);
268
269
        return $this;
270
    }
271
272
    /**
273
     * Will validate a potential configuration file. Returns false if file is no valid Doppelgaenger configuration, true otherwise
274
     *
275
     * @param string $file Path of the potential configuration file
276
     *
277
     * @return boolean
278
     * @throws \AppserverIo\Doppelgaenger\Exceptions\ConfigException
279
     */
280
    public function isValidConfigFile($file)
281
    {
282
        return is_array($this->validate($file));
283
    }
284
285
    /**
286
     * Will normalize directories mentioned within a configuration aspect.
287
     * If there is an error false will be returned. If not we will return the given configuration array containing only
288
     * normalized paths.
289
     *
290
     * @param string $configAspect The aspect to check for non-normal dirs
291
     * @param array  $configArray  The array to check within
292
     *
293
     * @return array|bool
294
     */
295
    protected function normalizeConfigDirs($configAspect, array $configArray)
296
    {
297
        // Are there dirs within this config aspect?
298
        if (isset($configArray[$configAspect . self::VALUE_NAME_DELIMITER . 'dirs'])) {
299
            // Get ourselves a format utility
300
            $formattingUtil = new Formatting();
301
302
            // Iterate over all dir entries and normalize the paths
303
            foreach ($configArray[$configAspect . self::VALUE_NAME_DELIMITER . 'dirs'] as $key => $projectDir) {
304
                // Do the normalization
305
                $tmp = $formattingUtil->sanitizeSeparators($formattingUtil->normalizePath($projectDir));
306
307
                if (is_readable($tmp)) {
308
                    $configArray[$configAspect . self::VALUE_NAME_DELIMITER . 'dirs'][$key] = $tmp;
309
0 ignored issues
show
Blank line found at end of control structure
Loading history...
310
                } elseif (preg_match('/\[|\]|\*|\+|\.|\(|\)|\?|\^/', $tmp)) {
311
                    // Kill the original path entry so the iterators wont give us a bad time
312
                    unset($configArray[$configAspect . self::VALUE_NAME_DELIMITER . 'dirs'][$key]);
313
314
                    // We will open up the paths with glob
315
                    foreach (glob($tmp, GLOB_ERR) as $regexlessPath) {
316
                        // collect the cleaned path
317
                        $configArray[$configAspect . self::VALUE_NAME_DELIMITER . 'dirs'][] = $regexlessPath;
318
                    }
319
0 ignored issues
show
Blank line found at end of control structure
Loading history...
320
                } else {
321
                    // Somethings wrong with the path, that should not be
322
                    return false;
323
                }
324
            }
325
0 ignored issues
show
Blank line found at end of control structure
Loading history...
326
        }
327
328
        // Everything seems fine, lets return the changes config array
329
        return $configArray;
330
    }
331
332
    /**
333
     * Will return the whole configuration or, if $aspect is given, certain parts of it
334
     *
335
     * @param string $aspect The aspect of the configuration we are interested in e.g. 'autoloader'
336
     *
337
     * @return array
338
     */
339
    public function getConfig($aspect = null)
340
    {
341
        if (!is_null($aspect)) {
342
            // Filter the aspect our of the config
343
            $tmp = array();
344
            foreach ($this->config as $key => $value) {
345
                // Do we have an entry belonging to the certain aspect? If so filter it and cut the aspect key part
346
                if (strpos($key, $aspect . self::VALUE_NAME_DELIMITER) === 0) {
347
                    $tmp[str_replace($aspect . self::VALUE_NAME_DELIMITER, '', $key)] = $value;
348
                }
349
            }
350
351
            return $tmp;
352
0 ignored issues
show
Blank line found at end of control structure
Loading history...
353
        } else {
354
            // Just return the whole config
355
356
            return $this->config;
357
        }
358
    }
359
360
    /**
361
     * Will validate a potential configuration file. Returns false if file is no valid Doppelgaenger configuration.
362
     * Will return the validated configuration on success
363
     *
364
     * @param string $file Path of the potential configuration file
365
     *
366
     * @return array|boolean
367
     * @throws \AppserverIo\Doppelgaenger\Exceptions\ConfigException
368
     */
369
    protected function validate($file)
370
    {
371
        $configCandidate = json_decode(file_get_contents($file), true);
372
373
        // Did we even get an array?
374
        if (!is_array($configCandidate)) {
375
            throw new ConfigException(sprintf('Could not parse configuration file %s.', $file));
376
0 ignored issues
show
Blank line found at end of control structure
Loading history...
377
        } else {
378
            $configCandidate = $this->flattenArray($configCandidate);
379
        }
380
381
        // We need some formatting utilities
382
        $formattingUtil = new Formatting();
383
384
        // We will normalize the paths we got and check if they are valid
385
        if (isset($configCandidate['cache' . self::VALUE_NAME_DELIMITER . 'dir'])) {
386
            $tmp = $formattingUtil->normalizePath($configCandidate['cache' . self::VALUE_NAME_DELIMITER . 'dir']);
387
388
            if (is_writable($tmp)) {
389
                $configCandidate['cache' . self::VALUE_NAME_DELIMITER . 'dir'] = $tmp;
390
0 ignored issues
show
Blank line found at end of control structure
Loading history...
391
            } else {
392
                throw new ConfigException(sprintf('The configured cache directory %s is not writable.', $tmp));
393
            }
394
        }
395
396
        // Same for enforcement dirs
397
        $configCandidate = $this->normalizeConfigDirs('enforcement', $configCandidate);
398
399
        // Do we still have an array here?
400
        if (!is_array($configCandidate)) {
401
            return false;
402
        }
403
404
        // Do the same for the autoloader dirs
405
        $configCandidate = $this->normalizeConfigDirs('autoloader', $configCandidate);
406
407
        // Lets check if there is a valid processing in place
408
        if ($configCandidate === false || !$this->validateProcessing($configCandidate)) {
409
            return false;
410
        }
411
412
        // Return what we got
413
        return $configCandidate;
414
    }
415
416
    /**
417
     * Will return true if the processing part of the config candidate array is valid. Will return false if not
418
     *
419
     * @param array $configCandidate The config candidate we want to validate in terms of processing
420
     *
421
     * @return boolean
422
     *
423
     * @todo move everything other than logger check to JSON scheme validation
0 ignored issues
show
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
424
     */
425
    protected function validateProcessing(array $configCandidate)
426
    {
427
        // Merge it with the current config as a standalone config might not be valid at all
428
        $configCandidate = array_replace_recursive($this->config, $configCandidate);
429
430
        $validEntries = array_flip(array('none', 'exception', 'logging'));
431
432
        // Do we have an entry at all?
433
        if (!isset($configCandidate['enforcement/processing'])) {
434
            return false;
435
        }
436
437
        // Did we even get something useful?
438
        if (!isset($validEntries[$configCandidate['enforcement/processing']])) {
439
            return false;
440
        }
441
442
        // If we got the option "logger" we have to check if there is a logger. If not, we fail, if yes
443
        // we have to check if we got something PSR-3 compatible
444
        if ($configCandidate['enforcement/processing'] === 'logging') {
445
            return $this->extractLoggerInstance($configCandidate) instanceof \Psr\Log\LoggerInterface;
446
        }
447
448
        // Still here? Sounds good
449
        return true;
450
    }
451
}
452