Issues (26)

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

Labels
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
2
namespace Consolidation\OutputFormatters;
3
4
use Consolidation\OutputFormatters\Exception\IncompatibleDataException;
5
use Consolidation\OutputFormatters\Exception\InvalidFormatException;
6
use Consolidation\OutputFormatters\Exception\UnknownFormatException;
7
use Consolidation\OutputFormatters\Formatters\FormatterAwareInterface;
8
use Consolidation\OutputFormatters\Formatters\FormatterInterface;
9
use Consolidation\OutputFormatters\Formatters\MetadataFormatterInterface;
10
use Consolidation\OutputFormatters\Formatters\RenderDataInterface;
11
use Consolidation\OutputFormatters\Options\FormatterOptions;
12
use Consolidation\OutputFormatters\Options\OverrideOptionsInterface;
13
use Consolidation\OutputFormatters\StructuredData\MetadataInterface;
14
use Consolidation\OutputFormatters\StructuredData\RestructureInterface;
15
use Consolidation\OutputFormatters\Transformations\DomToArraySimplifier;
16 28
use Consolidation\OutputFormatters\Transformations\OverrideRestructureInterface;
17
use Consolidation\OutputFormatters\Transformations\SimplifyToArrayInterface;
18 28
use Consolidation\OutputFormatters\Validate\ValidationInterface;
19 28
use Symfony\Component\Console\Input\InputOption;
20 28
use Symfony\Component\Console\Output\OutputInterface;
21 28
use Consolidation\OutputFormatters\StructuredData\OriginalDataInterface;
22 28
use Consolidation\OutputFormatters\StructuredData\ListDataFromKeys;
23 28
use Consolidation\OutputFormatters\StructuredData\ConversionInterface;
24 28
use Consolidation\OutputFormatters\Formatters\HumanReadableFormat;
25 28
26 28
/**
27 28
 * Manage a collection of formatters; return one on request.
28
 */
29
class FormatterManager
30
{
31 28
    /** var FormatterInterface[] */
32 28
    protected $formatters = [];
33
    /** var SimplifyToArrayInterface[] */
34
    protected $arraySimplifiers = [];
35
36
    public function __construct()
37
    {
38
    }
39
40
    public function addDefaultFormatters()
41
    {
42
        $defaultFormatters = [
43 28
            'null' => '\Consolidation\OutputFormatters\Formatters\NoOutputFormatter',
44
            'string' => '\Consolidation\OutputFormatters\Formatters\StringFormatter',
45 28
            'yaml' => '\Consolidation\OutputFormatters\Formatters\YamlFormatter',
46 27
            'xml' => '\Consolidation\OutputFormatters\Formatters\XmlFormatter',
47 24
            'json' => '\Consolidation\OutputFormatters\Formatters\JsonFormatter',
48 24
            'print-r' => '\Consolidation\OutputFormatters\Formatters\PrintRFormatter',
49
            'php' => '\Consolidation\OutputFormatters\Formatters\SerializeFormatter',
50 27
            'var_export' => '\Consolidation\OutputFormatters\Formatters\VarExportFormatter',
51
            'list' => '\Consolidation\OutputFormatters\Formatters\ListFormatter',
52
            'csv' => '\Consolidation\OutputFormatters\Formatters\CsvFormatter',
53
            'tsv' => '\Consolidation\OutputFormatters\Formatters\TsvFormatter',
54 27
            'table' => '\Consolidation\OutputFormatters\Formatters\TableFormatter',
55 27
            'sections' => '\Consolidation\OutputFormatters\Formatters\SectionsFormatter',
56 4
        ];
57
        if (class_exists('Symfony\Component\VarDumper\Dumper\CliDumper')) {
58
             $defaultFormatters['var_dump'] = '\Consolidation\OutputFormatters\Formatters\VarDumpFormatter';
59
        }
60 27
        foreach ($defaultFormatters as $id => $formatterClassname) {
61
            $formatter = new $formatterClassname;
62
            $this->addFormatter($id, $formatter);
63 27
        }
64
        $this->addFormatter('', $this->formatters['string']);
65
    }
66
67 24
    public function addDefaultSimplifiers()
68
    {
69 24
        // Add our default array simplifier (DOMDocument to array)
70
        $this->addSimplifier(new DomToArraySimplifier());
71
    }
72
73
    /**
74
     * Add a formatter
75
     *
76
     * @param string $key the identifier of the formatter to add
77
     * @param string $formatter the class name of the formatter to add
78
     * @return FormatterManager
79 28
     */
80
    public function addFormatter($key, FormatterInterface $formatter)
81 28
    {
82 1
        $this->formatters[$key] = $formatter;
83
        return $this;
84
    }
85 27
86 27
    /**
87 9
     * Add a simplifier
88 9
     *
89 27
     * @param SimplifyToArrayInterface $simplifier the array simplifier to add
90
     * @return FormatterManager
91
     */
92 28
    public function addSimplifier(SimplifyToArrayInterface $simplifier)
93
    {
94 28
        $this->arraySimplifiers[] = $simplifier;
95
        return $this;
96
    }
97
98
    /**
99
     * Return a set of InputOption based on the annotations of a command.
100
     * @param FormatterOptions $options
101
     * @return InputOption[]
102
     */
103
    public function automaticOptions(FormatterOptions $options, $dataType)
104
    {
105
        $automaticOptions = [];
106
107 24
        // At the moment, we only support automatic options for --format
108
        // and --fields, so exit if the command returns no data.
109 24
        if (!isset($dataType)) {
110 14
            return [];
111
        }
112 14
113
        $validFormats = $this->validFormats($dataType);
114
        if (empty($validFormats)) {
115
            return [];
116
        }
117
118
        $availableFields = $options->get(FormatterOptions::FIELD_LABELS);
119
        $hasDefaultStringField = $options->get(FormatterOptions::DEFAULT_STRING_FIELD);
120
        $defaultFormat = $hasDefaultStringField ? 'string' : ($availableFields ? 'table' : 'yaml');
121
122 27
        if (count($validFormats) > 1) {
123
            // Make an input option for --format
124
            $description = 'Format the result data. Available formats: ' . implode(',', $validFormats);
125
            $automaticOptions[FormatterOptions::FORMAT] = new InputOption(FormatterOptions::FORMAT, '', InputOption::VALUE_REQUIRED, $description, $defaultFormat);
126 27
        }
127 16
128
        $dataTypeClass = ($dataType instanceof \ReflectionClass) ? $dataType : new \ReflectionClass($dataType);
129
130
        if ($availableFields) {
131
            $defaultFields = $options->get(FormatterOptions::DEFAULT_FIELDS, [], '');
132 15
            $description = 'Available fields: ' . implode(', ', $this->availableFieldsList($availableFields));
133 4
            $automaticOptions[FormatterOptions::FIELDS] = new InputOption(FormatterOptions::FIELDS, '', InputOption::VALUE_REQUIRED, $description, $defaultFields);
134
        } elseif ($dataTypeClass->implementsInterface('Consolidation\OutputFormatters\StructuredData\RestructureInterface')) {
135
            $automaticOptions[FormatterOptions::FIELDS] = new InputOption(FormatterOptions::FIELDS, '', InputOption::VALUE_REQUIRED, 'Limit output to only the listed elements. Name top-level elements by key, e.g. "--fields=name,date", or use dot notation to select a nested element, e.g. "--fields=a.b.c as example".', []);
136 11
        }
137
138
        if (isset($automaticOptions[FormatterOptions::FIELDS])) {
139
            $automaticOptions[FormatterOptions::FIELD] = new InputOption(FormatterOptions::FIELD, '', InputOption::VALUE_REQUIRED, "Select just one field, and force format to 'string'.", '');
140
        }
141
142
        return $automaticOptions;
143
    }
144
145
    /**
146
     * Given a list of available fields, return a list of field descriptions.
147 27
     * @return string[]
148
     */
149 27
    protected function availableFieldsList($availableFields)
150 7
    {
151
        return array_map(
152 20
            function ($key) use ($availableFields) {
153
                return $availableFields[$key] . " ($key)";
154
            },
155
            array_keys($availableFields)
156
        );
157
    }
158
159
    /**
160
     * Return the identifiers for all valid data types that have been registered.
161
     *
162
     * @param mixed $dataType \ReflectionObject or other description of the produced data type
163
     * @return array
164
     */
165
    public function validFormats($dataType)
166
    {
167
        $validFormats = [];
168 27
        foreach ($this->formatters as $formatId => $formatterName) {
169
            $formatter = $this->getFormatter($formatId);
170 27
            if (!empty($formatId) && $this->isValidFormat($formatter, $dataType)) {
171 5
                $validFormats[] = $formatId;
172
            }
173 26
        }
174
        sort($validFormats);
175
        return $validFormats;
176
    }
177
178
    public function isValidFormat(FormatterInterface $formatter, $dataType)
179
    {
180
        if (is_array($dataType)) {
181
            $dataType = new \ReflectionClass('\ArrayObject');
182
        }
183
        if (!is_object($dataType) && !class_exists($dataType)) {
184
            return false;
185
        }
186
        if (!$dataType instanceof \ReflectionClass) {
187
            $dataType = new \ReflectionClass($dataType);
188
        }
189
        return $this->isValidDataType($formatter, $dataType);
190
    }
191
192
    public function isValidDataType(FormatterInterface $formatter, \ReflectionClass $dataType)
193
    {
194
        if ($this->canSimplifyToArray($dataType)) {
195
            if ($this->isValidFormat($formatter, [])) {
196
                return true;
197
            }
198
        }
199
        // If the formatter does not implement ValidationInterface, then
200
        // it is presumed that the formatter only accepts arrays.
201
        if (!$formatter instanceof ValidationInterface) {
202
            return $dataType->isSubclassOf('ArrayObject') || ($dataType->getName() == 'ArrayObject');
0 ignored issues
show
Consider using $dataType->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
203
        }
204
        return $formatter->isValidDataType($dataType);
205
    }
206
207
    /**
208
     * Format and write output
209
     *
210
     * @param OutputInterface $output Output stream to write to
211
     * @param string $format Data format to output in
212
     * @param mixed $structuredOutput Data to output
213
     * @param FormatterOptions $options Formatting options
214
     */
215
    public function write(OutputInterface $output, $format, $structuredOutput, FormatterOptions $options)
216
    {
217
        // Convert the data to another format (e.g. converting from RowsOfFields to
218
        // UnstructuredListData when the fields indicate an unstructured transformation
219
        // is requested).
220
        $structuredOutput = $this->convertData($structuredOutput, $options);
221
222
        // TODO: If the $format is the default format (not selected by the user), and
223
        // if `convertData` switched us to unstructured data, then select a new default
224
        // format (e.g. yaml) if the selected format cannot render the converted data.
225
        $formatter = $this->getFormatter((string)$format);
226
227
        // If the data format is not applicable for the selected formatter, throw an error.
228
        if (!is_string($structuredOutput) && !$this->isValidFormat($formatter, $structuredOutput)) {
229
            $validFormats = $this->validFormats($structuredOutput);
230
            throw new InvalidFormatException((string)$format, $structuredOutput, $validFormats);
231
        }
232
        if ($structuredOutput instanceof FormatterAwareInterface) {
233
            $structuredOutput->setFormatter($formatter);
234
        }
235
        // Give the formatter a chance to override the options
236
        $options = $this->overrideOptions($formatter, $structuredOutput, $options);
237
        $restructuredOutput = $this->validateAndRestructure($formatter, $structuredOutput, $options);
238
        if ($formatter instanceof MetadataFormatterInterface) {
239
            $formatter->writeMetadata($output, $structuredOutput, $options);
240
        }
241
        $formatter->write($output, $restructuredOutput, $options);
242
    }
243
244
    protected function validateAndRestructure(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
245
    {
246
        // Give the formatter a chance to do something with the
247
        // raw data before it is restructured.
248
        $overrideRestructure = $this->overrideRestructure($formatter, $structuredOutput, $options);
249
        if ($overrideRestructure) {
250
            return $overrideRestructure;
251
        }
252
253
        // Restructure the output data (e.g. select fields to display, etc.).
254
        $restructuredOutput = $this->restructureData($structuredOutput, $options);
255
256
        // Make sure that the provided data is in the correct format for the selected formatter.
257
        $restructuredOutput = $this->validateData($formatter, $restructuredOutput, $options);
258
259
        // Give the original data a chance to re-render the structured
260
        // output after it has been restructured and validated.
261
        $restructuredOutput = $this->renderData($formatter, $structuredOutput, $restructuredOutput, $options);
262
263
        return $restructuredOutput;
264
    }
265
266
    /**
267
     * Fetch the requested formatter.
268
     *
269
     * @param string $format Identifier for requested formatter
270
     * @return FormatterInterface
271
     */
272
    public function getFormatter($format)
273
    {
274
        // The client must inject at least one formatter before asking for
275
        // any formatters; if not, we will provide all of the usual defaults
276
        // as a convenience.
277
        if (empty($this->formatters)) {
278
            $this->addDefaultFormatters();
279
            $this->addDefaultSimplifiers();
280
        }
281
        if (!$this->hasFormatter($format)) {
282
            throw new UnknownFormatException($format);
283
        }
284
        $formatter = $this->formatters[$format];
285
        return $formatter;
286
    }
287
288
    /**
289
     * Test to see if the stipulated format exists
290
     */
291
    public function hasFormatter($format)
292
    {
293
        return array_key_exists($format, $this->formatters);
294
    }
295
296
    /**
297
     * Render the data as necessary (e.g. to select or reorder fields).
298
     *
299
     * @param FormatterInterface $formatter
300
     * @param mixed $originalData
301
     * @param mixed $restructuredData
302
     * @param FormatterOptions $options Formatting options
303
     * @return mixed
304
     */
305
    public function renderData(FormatterInterface $formatter, $originalData, $restructuredData, FormatterOptions $options)
306
    {
307
        if ($formatter instanceof RenderDataInterface) {
308
            return $formatter->renderData($originalData, $restructuredData, $options);
309
        }
310
        return $restructuredData;
311
    }
312
313
    /**
314
     * Determine if the provided data is compatible with the formatter being used.
315
     *
316
     * @param FormatterInterface $formatter Formatter being used
317
     * @param mixed $structuredOutput Data to validate
318
     * @return mixed
319
     */
320
    public function validateData(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
321
    {
322
        // If the formatter implements ValidationInterface, then let it
323
        // test the data and throw or return an error
324
        if ($formatter instanceof ValidationInterface) {
325
            return $formatter->validate($structuredOutput);
326
        }
327
        // If the formatter does not implement ValidationInterface, then
328
        // it will never be passed an ArrayObject; we will always give
329
        // it a simple array.
330
        $structuredOutput = $this->simplifyToArray($structuredOutput, $options);
331
        // If we could not simplify to an array, then throw an exception.
332
        // We will never give a formatter anything other than an array
333
        // unless it validates that it can accept the data type.
334
        if (!is_array($structuredOutput)) {
335
            throw new IncompatibleDataException(
336
                $formatter,
337
                $structuredOutput,
338
                []
339
            );
340
        }
341
        return $structuredOutput;
342
    }
343
344
    protected function simplifyToArray($structuredOutput, FormatterOptions $options)
345
    {
346
        // We can do nothing unless the provided data is an object.
347
        if (!is_object($structuredOutput)) {
348
            return $structuredOutput;
349
        }
350
        // Check to see if any of the simplifiers can convert the given data
351
        // set to an array.
352
        $outputDataType = new \ReflectionClass($structuredOutput);
353
        foreach ($this->arraySimplifiers as $simplifier) {
354
            if ($simplifier->canSimplify($outputDataType)) {
355
                $structuredOutput = $simplifier->simplifyToArray($structuredOutput, $options);
356
            }
357
        }
358
        // Convert data structure back into its original form, if necessary.
359
        if ($structuredOutput instanceof OriginalDataInterface) {
360
            return $structuredOutput->getOriginalData();
361
        }
362
        // Convert \ArrayObjects to a simple array.
363
        if ($structuredOutput instanceof \ArrayObject) {
364
            return $structuredOutput->getArrayCopy();
365
        }
366
        return $structuredOutput;
367
    }
368
369
    protected function canSimplifyToArray(\ReflectionClass $structuredOutput)
370
    {
371
        foreach ($this->arraySimplifiers as $simplifier) {
372
            if ($simplifier->canSimplify($structuredOutput)) {
373
                return true;
374
            }
375
        }
376
        return false;
377
    }
378
379
    /**
380
     * Convert from one format to another if necessary prior to restructuring.
381
     */
382
    public function convertData($structuredOutput, FormatterOptions $options)
383
    {
384
        if ($structuredOutput instanceof ConversionInterface) {
385
            return $structuredOutput->convert($options);
386
        }
387
        return $structuredOutput;
388
    }
389
390
    /**
391
     * Restructure the data as necessary (e.g. to select or reorder fields).
392
     *
393
     * @param mixed $structuredOutput
394
     * @param FormatterOptions $options
395
     * @return mixed
396
     */
397
    public function restructureData($structuredOutput, FormatterOptions $options)
398
    {
399
        if ($structuredOutput instanceof RestructureInterface) {
400
            return $structuredOutput->restructure($options);
401
        }
402
        return $structuredOutput;
403
    }
404
405
    /**
406
     * Allow the formatter access to the raw structured data prior
407
     * to restructuring.  For example, the 'list' formatter may wish
408
     * to display the row keys when provided table output.  If this
409
     * function returns a result that does not evaluate to 'false',
410
     * then that result will be used as-is, and restructuring and
411
     * validation will not occur.
412
     *
413
     * @param mixed $structuredOutput
414
     * @param FormatterOptions $options
415
     * @return mixed
416
     */
417
    public function overrideRestructure(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
418
    {
419
        if ($formatter instanceof OverrideRestructureInterface) {
420
            return $formatter->overrideRestructure($structuredOutput, $options);
421
        }
422
    }
423
424
    /**
425
     * Allow the formatter to mess with the configuration options before any
426
     * transformations et. al. get underway.
427
     * @param FormatterInterface $formatter
428
     * @param mixed $structuredOutput
429
     * @param FormatterOptions $options
430
     * @return FormatterOptions
431
     */
432
    public function overrideOptions(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
433
    {
434
        // Set the "Human Readable" option if the formatter has the HumanReadable marker interface
435
        if ($formatter instanceof HumanReadableFormat) {
436
            $options->setHumanReadable();
437
        }
438
        // The formatter may also make dynamic adjustment to the options.
439
        if ($formatter instanceof OverrideOptionsInterface) {
440
            return $formatter->overrideOptions($structuredOutput, $options);
441
        }
442
        return $options;
443
    }
444
}
445