Issues (1165)

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/Kint.php (4 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
class Kint
4
{
5
    /**
6
     * @var mixed Kint mode
7
     *
8
     * false: Disabled
9
     * true: Enabled, default mode selection
10
     * string: Manual mode selection
11
     */
12
    public static $enabled_mode = true;
13
14
    /**
15
     * Default mode.
16
     *
17
     * @var string
18
     */
19
    public static $mode_default = self::MODE_RICH;
20
21
    /**
22
     * Default mode in CLI with cli_detection on.
23
     *
24
     * @var string
25
     */
26
    public static $mode_default_cli = self::MODE_CLI;
27
28
    /**
29
     * @var bool Return output instead of echoing
30
     */
31
    public static $return;
32
33
    /**
34
     * @var string format of the link to the source file in trace entries.
35
     *
36
     * Use %f for file path, %l for line number.
37
     *
38
     * [!] EXAMPLE (works with for phpStorm and RemoteCall Plugin):
39
     *
40
     * Kint::$file_link_format = 'http://localhost:8091/?message=%f:%l';
41
     */
42
    public static $file_link_format = '';
43
44
    /**
45
     * @var bool whether to display where kint was called from
46
     */
47
    public static $display_called_from = true;
48
49
    /**
50
     * @var array base directories of your application that will be displayed instead of the full path.
51
     *
52
     * Keys are paths, values are replacement strings
53
     *
54
     * [!] EXAMPLE (for Kohana framework):
55
     *
56
     * Kint::$app_root_dirs = array(
57
     *      APPPATH => 'APPPATH',
58
     *      SYSPATH => 'SYSPATH',
59
     *      MODPATH => 'MODPATH',
60
     *      DOCROOT => 'DOCROOT',
61
     * );
62
     *
63
     * [!] EXAMPLE #2 (for a semi-universal approach)
64
     *
65
     * Kint::$app_root_dirs = array(
66
     *      realpath( __DIR__ . '/../../..' ) => 'ROOT', // go up as many levels as needed
67
     * );
68
     */
69
    public static $app_root_dirs = array();
70
71
    /**
72
     * @var int max array/object levels to go deep, if zero no limits are applied
73
     */
74
    public static $max_depth = 6;
75
76
    /**
77
     * @var bool expand all trees by default for rich view
78
     */
79
    public static $expanded = false;
80
81
    /**
82
     * @var bool enable detection when Kint is command line.
83
     *
84
     * Formats output with whitespace only; does not HTML-escape it
85
     */
86
    public static $cli_detection = true;
87
88
    /**
89
     * @var array Kint aliases. Add debug functions in Kint wrappers here to fix modifiers and backtraces
90
     */
91
    public static $aliases = array(
92
        array('Kint', 'dump'),
93
        array('Kint', 'trace'),
94
        array('Kint', 'dumpArray'),
95
    );
96
97
    /**
98
     * @var array Kint_Renderer descendants. Add to array to extend.
99
     */
100
    public static $renderers = array(
101
        self::MODE_RICH => 'Kint_Renderer_Rich',
102
        self::MODE_PLAIN => 'Kint_Renderer_Plain',
103
        self::MODE_TEXT => 'Kint_Renderer_Text',
104
        self::MODE_CLI => 'Kint_Renderer_Cli',
105
    );
106
107
    const MODE_RICH = 'r';
108
    const MODE_TEXT = 't';
109
    const MODE_CLI = 'c';
110
    const MODE_PLAIN = 'p';
111
112
    public static $plugins = array(
113
        'Kint_Parser_Base64',
114
        'Kint_Parser_Blacklist',
115
        'Kint_Parser_ClassMethods',
116
        'Kint_Parser_ClassStatics',
117
        'Kint_Parser_Closure',
118
        'Kint_Parser_Color',
119
        'Kint_Parser_DateTime',
120
        'Kint_Parser_FsPath',
121
        'Kint_Parser_Iterator',
122
        'Kint_Parser_Json',
123
        'Kint_Parser_Microtime',
124
        'Kint_Parser_SimpleXMLElement',
125
        'Kint_Parser_SplFileInfo',
126
        'Kint_Parser_SplObjectStorage',
127
        'Kint_Parser_Stream',
128
        'Kint_Parser_Table',
129
        'Kint_Parser_Throwable',
130
        'Kint_Parser_Timestamp',
131
        'Kint_Parser_ToString',
132
        'Kint_Parser_Trace',
133
        'Kint_Parser_Xml',
134
    );
135
136
    private static $plugin_pool = array();
137
    private static $dump_array = false;
138
    private static $names = array();
139
140
    /**
141
     * Stashes or sets all settings at once.
142
     *
143
     * @param array|null $settings Array of all settings to be set or null to set none
144
     *
145
     * @return array Current settings
146
     */
147
    public static function settings(array $settings = null)
148
    {
149
        static $keys = array(
150
            'aliases',
151
            'app_root_dirs',
152
            'cli_detection',
153
            'display_called_from',
154
            'enabled_mode',
155
            'expanded',
156
            'file_link_format',
157
            'max_depth',
158
            'mode_default',
159
            'mode_default_cli',
160
            'renderers',
161
            'return',
162
            'plugins',
163
        );
164
165
        $out = array();
166
167
        foreach ($keys as $key) {
168
            $out[$key] = self::$$key;
169
        }
170
171
        if ($settings !== null) {
172
            $in = array_intersect_key($settings, $out);
173
            foreach ($in as $key => $val) {
174
                self::$$key = $val;
175
            }
176
        }
177
178
        return $out;
179
    }
180
181
    /**
182
     * Prints a debug backtrace, same as Kint::dump(1).
183
     *
184
     * @param array $trace [OPTIONAL] you can pass your own trace, otherwise, `debug_backtrace` will be called
185
     *
186
     * @return mixed
187
     */
188
    public static function trace($trace = null)
189
    {
190
        if ($trace === null) {
191
            if (KINT_PHP525) {
192
                $trace = debug_backtrace(true);
193
            } else {
194
                $trace = debug_backtrace();
195
            }
196
        } else {
197
            return self::dump($trace);
198
        }
199
200
        Kint_Parser_Trace::normalizeAliases(self::$aliases);
201
202
        $trimmed_trace = array();
203
204
        foreach ($trace as $frame) {
205
            if (Kint_Parser_Trace::frameIsListed($frame, self::$aliases)) {
206
                $trimmed_trace = array();
207
            }
208
209
            $trimmed_trace[] = $frame;
210
        }
211
212
        return self::dumpArray(
213
            array($trimmed_trace),
214
            array(Kint_Object::blank('Kint::trace()', 'debug_backtrace()'))
215
        );
216
    }
217
218
    /**
219
     * Dumps an array as separate values, and uses $names to seed the parser.
220
     *
221
     * @param array                   $data  Data to be dumped
222
     * @param array[Kint_Object]|null $names Array of Kint_Object to seed the parser with
0 ignored issues
show
The doc-type array[Kint_Object]|null could not be parsed: Expected "]" at position 2, but found "Kint_Object". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
223
     */
224
    public static function dumpArray(array $data, array $names = null)
225
    {
226
        self::$names = $names;
227
        self::$dump_array = true;
228
229
        $out = self::dump($data);
230
231
        self::$names = null;
232
        self::$dump_array = false;
233
234
        return $out;
235
    }
236
237
    /**
238
     * Dump information about variables, accepts any number of parameters, supports modifiers:.
239
     *
240
     *  clean up any output before kint and place the dump at the top of page:
241
     *   - Kint::dump()
242
     *  *****
243
     *  expand all nodes on display:
244
     *   ! Kint::dump()
245
     *  *****
246
     *  dump variables disregarding their depth:
247
     *   + Kint::dump()
248
     *  *****
249
     *  return output instead of displaying it:
250
     *
251
     *   @ Kint::dump()
252
     *  *****
253
     *  force output as plain text
254
     *   ~ Kint::dump()
255
     *
256
     * Modifiers are supported by all dump wrapper functions, including Kint::trace(). Space is optional.
257
     *
258
     * @param mixed $data
259
     *
260
     * @return int|string
261
     */
262
    public static function dump($data = null)
263
    {
264
        if (!self::$enabled_mode) {
265
            return 0;
266
        }
267
268
        $stash = self::settings();
269
        $num_args = func_num_args();
270
271
        list($params, $modifiers, $callee, $caller, $minitrace) = self::getCalleeInfo(
272
            defined('DEBUG_BACKTRACE_IGNORE_ARGS')
273
                ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)
274
                : debug_backtrace(),
275
            $num_args
276
        );
277
278
        // set mode for current run
279 View Code Duplication
        if (self::$enabled_mode === true) {
280
            self::$enabled_mode = self::$mode_default;
281
            if (PHP_SAPI === 'cli' && self::$cli_detection === true) {
282
                self::$enabled_mode = self::$mode_default_cli;
283
            }
284
        }
285
286
        if (in_array('~', $modifiers)) {
287
            self::$enabled_mode = self::MODE_TEXT;
288
        }
289
290
        if (!array_key_exists(self::$enabled_mode, self::$renderers)) {
291
            $renderer = self::$renderers[self::MODE_PLAIN];
292
        } else {
293
            $renderer = self::$renderers[self::$enabled_mode];
294
        }
295
296
        // process modifiers: @, +, ! and -
297
        if (in_array('-', $modifiers)) {
298
            while (ob_get_level()) {
299
                ob_end_clean();
300
            }
301
        }
302
        if (in_array('!', $modifiers)) {
303
            self::$expanded = true;
304
        }
305
        if (in_array('+', $modifiers)) {
306
            self::$max_depth = false;
0 ignored issues
show
Documentation Bug introduced by
The property $max_depth was declared of type integer, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
307
        }
308
        if (in_array('@', $modifiers)) {
309
            self::$return = true;
310
        }
311
312
        $renderer = new $renderer(array(
313
            'num_args' => $num_args,
314
            'params' => $params,
315
            'modifiers' => $modifiers,
316
            'callee' => $callee,
317
            'caller' => $caller,
318
            'minitrace' => $minitrace,
319
            'settings' => self::settings(),
320
            'stash' => $stash,
321
        ));
322
323
        $plugins = array();
324
325
        foreach (self::$plugins as $plugin) {
326
            if ($plugin instanceof Kint_Parser_Plugin) {
327
                $plugins[] = $plugin;
328
            } elseif (is_string($plugin) && is_subclass_of($plugin, 'Kint_Parser_Plugin')) {
329
                if (!isset(self::$plugin_pool[$plugin])) {
330
                    $p = new $plugin();
331
                    self::$plugin_pool[$plugin] = $p;
332
                }
333
                $plugins[] = self::$plugin_pool[$plugin];
334
            }
335
        }
336
337
        $plugins = $renderer->parserPlugins($plugins);
338
339
        $output = $renderer->preRender();
340
341
        $parser = new Kint_Parser(self::$max_depth, empty($caller['class']) ? null : $caller['class']);
0 ignored issues
show
It seems like self::$max_depth can also be of type integer; however, Kint_Parser::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
342
343
        foreach ($plugins as $plugin) {
344
            $parser->addPlugin($plugin);
345
        }
346
347
        // Kint::dump(1) shorthand
348
        if (!self::$dump_array && (!isset($params[0]['name']) || $params[0]['name'] == '1') && $num_args === 1 && $data === 1) {
349
            if (KINT_PHP525) {
350
                $data = debug_backtrace(true);
351
            } else {
352
                $data = debug_backtrace();
353
            }
354
355
            $trace = array();
356
357
            // No need to normalize as we've already called it through getCalleeInfo at this point
358 View Code Duplication
            foreach ($data as $index => $frame) {
359
                if (Kint_Parser_Trace::frameIsListed($frame, self::$aliases)) {
360
                    $trace = array();
361
                }
362
363
                $trace[] = $frame;
364
            }
365
366
            $lastframe = reset($trace);
367
            $tracename = $lastframe['function'].'(1)';
368
            if (isset($lastframe['class'], $lastframe['type'])) {
369
                $tracename = $lastframe['class'].$lastframe['type'].$tracename;
370
            }
371
            $tracebase = Kint_Object::blank($tracename, 'debug_backtrace()');
372
373
            if (empty($trace)) {
374
                $output .= $renderer->render($tracebase->transplant(new Kint_Object_Trace()));
375
            } else {
376
                $output .= $renderer->render($parser->parse($trace, $tracebase));
377
            }
378
        } else {
379
            $data = func_get_args();
380
            if ($data === array()) {
381
                $output .= $renderer->render(new Kint_Object_Nothing());
382
            }
383
384
            if (self::$dump_array) {
385
                $data = $data[0];
386
            }
387
388
            static $blacklist = array('null', 'true', 'false', 'array(...)', 'array()', '"..."', 'b"..."', '[...]', '[]', '(...)', '()');
389
390
            foreach ($data as $i => $argument) {
391
                if (isset(self::$names[$i])) {
392
                    $output .= $renderer->render(
393
                        $parser->parse($argument, self::$names[$i])
394
                    );
395
                    continue;
396
                }
397
398
                if (!isset($params[$i]['name']) || is_numeric($params[$i]['name']) || in_array(str_replace("'", '"', strtolower($params[$i]['name'])), $blacklist, true)) {
399
                    $name = null;
400
                } else {
401
                    $name = $params[$i]['name'];
402
                }
403
404
                if (isset($params[$i]['path'])) {
405
                    $access_path = $params[$i]['path'];
406
407
                    if (!empty($params[$i]['expression'])) {
408
                        $access_path = '('.$access_path.')';
409
                    }
410
                } else {
411
                    $access_path = '$'.$i;
412
                }
413
414
                $output .= $renderer->render(
415
                    $parser->parse($argument, Kint_Object::blank($name, $access_path))
416
                );
417
            }
418
        }
419
420
        $output .= $renderer->postRender();
421
422
        if (self::$return) {
423
            self::settings($stash);
424
425
            return $output;
426
        }
427
428
        self::settings($stash);
429
        echo $output;
430
431
        return 0;
432
    }
433
434
    /**
435
     * generic path display callback, can be configured in app_root_dirs; purpose is
436
     * to show relevant path info and hide as much of the path as possible.
437
     *
438
     * @param string $file
439
     *
440
     * @return string
441
     */
442
    public static function shortenPath($file)
443
    {
444
        $file = array_values(array_filter(explode('/', str_replace('\\', '/', $file)), 'strlen'));
445
446
        $longest_match = 0;
447
        $match = '/';
448
449
        foreach (self::$app_root_dirs as $path => $alias) {
450
            if (empty($path)) {
451
                continue;
452
            }
453
454
            $path = array_values(array_filter(explode('/', str_replace('\\', '/', $path)), 'strlen'));
455
456
            if (array_slice($file, 0, count($path)) === $path && count($path) > $longest_match) {
457
                $longest_match = count($path);
458
                $match = $alias;
459
            }
460
        }
461
462
        if ($longest_match) {
463
            $file = array_merge(array($match), array_slice($file, $longest_match));
464
465
            return implode('/', $file);
466
        } else {
467
            // fallback to find common path with Kint dir
468
            $kint = array_values(array_filter(explode('/', str_replace('\\', '/', KINT_DIR)), 'strlen'));
469
470
            foreach ($file as $i => $part) {
471
                if (!isset($kint[$i]) || $kint[$i] !== $part) {
472
                    return ($i ? '.../' : '/').implode('/', array_slice($file, $i));
473
                }
474
            }
475
476
            return '/'.implode('/', $file);
477
        }
478
    }
479
480
    public static function getIdeLink($file, $line)
481
    {
482
        return str_replace(array('%f', '%l'), array($file, $line), self::$file_link_format);
483
    }
484
485
    /**
486
     * returns parameter names that the function was passed, as well as any predefined symbols before function
487
     * call (modifiers).
488
     *
489
     * @param array $trace
490
     *
491
     * @return array($params, $modifiers, $callee, $caller, $miniTrace)
0 ignored issues
show
The doc-type array($params, could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
492
     */
493
    private static function getCalleeInfo($trace, $num_params)
494
    {
495
        Kint_Parser_Trace::normalizeAliases(self::$aliases);
496
        $miniTrace = array();
497
498 View Code Duplication
        foreach ($trace as $index => $frame) {
499
            if (Kint_Parser_Trace::frameIsListed($frame, self::$aliases)) {
500
                $miniTrace = array();
501
            }
502
503
            if (!Kint_Parser_Trace::frameIsListed($frame, array('spl_autoload_call'))) {
504
                $miniTrace[] = $frame;
505
            }
506
        }
507
508
        $callee = reset($miniTrace);
509
        $caller = next($miniTrace);
510
        if (!$callee) {
511
            $callee = null;
512
        }
513
        if (!$caller) {
514
            $caller = null;
515
        }
516
517
        unset($miniTrace[0]);
518
519
        foreach ($miniTrace as $index => &$frame) {
520
            if (!isset($frame['file'], $frame['line'])) {
521
                unset($miniTrace[$index]);
522
            } else {
523
                unset($frame['object'], $frame['args']);
524
            }
525
        }
526
527
        $miniTrace = array_values($miniTrace);
528
529
        if (!isset($callee['file'], $callee['line']) || !is_readable($callee['file'])) {
530
            return array(null, array(), $callee, $caller, $miniTrace);
531
        }
532
533
        // open the file and read it up to the position where the function call expression ended
534
        if (empty($callee['class'])) {
535
            $callfunc = $callee['function'];
536
        } else {
537
            $callfunc = array($callee['class'], $callee['function']);
538
        }
539
540
        $calls = Kint_SourceParser::getFunctionCalls(
541
            file_get_contents($callee['file']),
542
            $callee['line'],
543
            $callfunc
544
        );
545
546
        $return = array(null, array(), $callee, $caller, $miniTrace);
547
548
        foreach ($calls as $call) {
549
            $is_unpack = false;
550
551
            // Handle argument unpacking as a last resort
552
            if (KINT_PHP56) {
553
                foreach ($call['parameters'] as $i => &$param) {
554
                    if (strpos($param['name'], '...') === 0) {
555
                        if ($i === count($call['parameters']) - 1) {
556
                            for ($j = 1; $j + $i < $num_params; ++$j) {
557
                                $call['parameters'][] = array(
558
                                    'name' => 'array_values('.substr($param['name'], 3).')['.$j.']',
559
                                    'path' => 'array_values('.substr($param['path'], 3).')['.$j.']',
560
                                    'expression' => false,
561
                                );
562
                            }
563
564
                            $param['name'] = 'reset('.substr($param['name'], 3).')';
565
                            $param['path'] = 'reset('.substr($param['path'], 3).')';
566
                            $param['expression'] = false;
567
                        } else {
568
                            $call['parameters'] = array_slice($call['parameters'], 0, $i);
569
                        }
570
571
                        $is_unpack = true;
572
                        break;
573
                    }
574
                }
575
            }
576
577
            if ($is_unpack || count($call['parameters']) === $num_params) {
578
                if ($return[0] === null) {
579
                    $return = array($call['parameters'], $call['modifiers'], $callee, $caller, $miniTrace);
580
                } else {
581
                    // If we have multiple calls on the same line with the same amount of arguments,
582
                    // we can't be sure which it is so just return null and let them figure it out
583
                    return array(null, array(), $callee, $caller, $miniTrace);
584
                }
585
            }
586
        }
587
588
        return $return;
589
    }
590
591
    public static function composerGetExtras($key = 'kint')
592
    {
593
        $extras = array();
594
595
        if (!KINT_PHP53) {
596
            return $extras;
597
        }
598
599
        $folder = KINT_DIR.'/vendor';
600
601
        for ($i = 0; $i < 4; ++$i) {
602
            $installed = $folder.'/composer/installed.json';
603
604
            if (file_exists($installed) && is_readable($installed)) {
605
                $packages = json_decode(file_get_contents($installed), true);
606
607
                foreach ($packages as $package) {
608 View Code Duplication
                    if (isset($package['extra'][$key]) && is_array($package['extra'][$key])) {
609
                        $extras = array_replace($extras, $package['extra'][$key]);
610
                    }
611
                }
612
613
                $folder = dirname($folder);
614
615
                if (file_exists($folder.'/composer.json') && is_readable($folder.'/composer.json')) {
616
                    $composer = json_decode(file_get_contents($folder.'/composer.json'), true);
617
618 View Code Duplication
                    if (isset($composer['extra'][$key]) && is_array($composer['extra'][$key])) {
619
                        $extras = array_replace($extras, $composer['extra'][$key]);
620
                    }
621
                }
622
623
                break;
624
            } else {
625
                $folder = dirname($folder);
626
            }
627
        }
628
629
        return $extras;
630
    }
631
632
    public static function composerGetDisableHelperFunctions()
633
    {
634
        $extras = self::composerGetExtras();
635
636
        return !empty($extras['disable-helper-functions']);
637
    }
638
}
639