GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (7)

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/Camspiers/LoggerBridge/LoggerBridge.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 Camspiers\LoggerBridge;
4
5
use Camspiers\LoggerBridge\BacktraceReporter\BacktraceReporter;
6
use Camspiers\LoggerBridge\BacktraceReporter\BasicBacktraceReporter;
7
use Camspiers\LoggerBridge\EnvReporter\DirectorEnvReporter;
8
use Camspiers\LoggerBridge\EnvReporter\EnvReporter;
9
use Camspiers\LoggerBridge\ErrorReporter\DebugErrorReporter;
10
use Camspiers\LoggerBridge\ErrorReporter\ErrorReporter;
11
use Psr\Log\LoggerInterface;
12
13
/**
14
 * Enables global SilverStripe logging with a PSR-3 logger like Monolog.
15
 *
16
 * The logger is attached by using a Request Processor filter. This behaviour is required
17
 * so the logger is attached after the environment only and except rules in yml are applied.
18
 *
19
 * @author Cam Spiers <[email protected]>
20
 */
21
class LoggerBridge implements \RequestFilter
22
{
23
    /**
24
     * @var \Psr\Log\LoggerInterface
25
     */
26
    protected $logger;
27
28
    /**
29
     * @var \Camspiers\LoggerBridge\ErrorReporter\ErrorReporter
30
     */
31
    protected $errorReporter;
32
33
    /**
34
     * @var \Camspiers\LoggerBridge\EnvReporter\EnvReporter
35
     */
36
    protected $envReporter;
37
38
    /**
39
     * @var \Camspiers\LoggerBridge\BacktraceReporter\BacktraceReporter
40
     */
41
    protected $backtraceReporter;
42
43
    /**
44
     * @var bool|null
45
     */
46
    protected $registered;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $showErrors = true;
52
53
    /**
54
     * @var int
55
     */
56
    protected $reserveMemory = 5242880; // 5M
57
58
    /**
59
     * @var int|null
60
     */
61
    protected $reportLevel;
62
63
    /**
64
     * @var null|callable
65
     */
66
    protected $errorHandler;
67
68
    /**
69
     * @var null|callable
70
     */
71
    protected $exceptionHandler;
72
73
    /**
74
     * @var bool
75
     */
76
    protected $reportBacktrace = false;
77
78
    /**
79
     * If an ErrorException should be included in the log context when handling errors
80
     * @var bool
81
     */
82
    protected $exceptionInContext = true;
83
84
    /**
85
     * Defines the way error types are logged
86
     * @var
87
     */
88
    protected $errorLogGroups = array(
89
        'error'   => array(
90
            E_ERROR,
91
            E_CORE_ERROR,
92
            E_USER_ERROR,
93
            E_PARSE,
94
            E_COMPILE_ERROR,
95
            E_RECOVERABLE_ERROR
96
        ),
97
        'warning' => array(
98
            E_WARNING,
99
            E_CORE_WARNING,
100
            E_USER_WARNING,
101
            E_NOTICE,
102
            E_USER_NOTICE,
103
            E_DEPRECATED,
104
            E_USER_DEPRECATED,
105
            E_STRICT
106
        )
107
    );
108
    
109
    /**
110
     * Defines what errors should terminate
111
     */
112
    protected $terminatingErrors = array(
113
        E_ERROR,
114
        E_CORE_ERROR,
115
        E_USER_ERROR,
116
        E_PARSE,
117
        E_COMPILE_ERROR,
118
        E_RECOVERABLE_ERROR
119
    );
120
121
    /**
122
     * @param \Psr\Log\LoggerInterface $logger
123
     * @param bool                     $showErrors    If false stops the display of SilverStripe errors
124
     * @param null                     $reserveMemory The amount of memory to reserve for out of memory errors
125
     * @param null|int                 $reportLevel   Allow the specification of a reporting level
126
     */
127 23
    public function __construct(
128
        LoggerInterface $logger,
129
        $showErrors = true,
130
        $reserveMemory = null,
131
        $reportLevel = null
132
    ) {
133 23
        $this->logger = $logger;
134 23
        $this->showErrors = (bool) $showErrors;
135 23
        if ($reserveMemory !== null) {
136
            $this->setReserveMemory($reserveMemory);
137
        }
138
        // If a specific reportLevel isn't set use error_reporting
139
        // It can be useful to set a reportLevel when you want to override SilverStripe live settings
140 23
        $this->reportLevel = $reportLevel !== null ? $reportLevel : error_reporting();
141 23
    }
142
143
    /**
144
     * @param \Psr\Log\LoggerInterface $logger
145
     */
146
    public function setLogger($logger)
147
    {
148
        $this->logger = $logger;
149
    }
150
151
    /**
152
     * @return \Psr\Log\LoggerInterface
153
     */
154
    public function getLogger()
155
    {
156
        return $this->logger;
157
    }
158
159
    /**
160
     * @param \Camspiers\LoggerBridge\ErrorReporter\ErrorReporter $errorReporter
161
     */
162 15
    public function setErrorReporter(ErrorReporter $errorReporter)
163
    {
164 15
        $this->errorReporter = $errorReporter;
165 15
    }
166
167
    /**
168
     * @return \Camspiers\LoggerBridge\ErrorReporter\ErrorReporter
169
     */
170 7
    public function getErrorReporter()
171
    {
172 7
        $this->errorReporter = $this->errorReporter ? : new DebugErrorReporter($this->getEnvReporter());
173
174 7
        return $this->errorReporter;
175
    }
176
177
    /**
178
     * @param \Camspiers\LoggerBridge\EnvReporter\EnvReporter $envReporter
179
     */
180 20
    public function setEnvReporter(EnvReporter $envReporter)
181
    {
182 20
        $this->envReporter = $envReporter;
183 20
    }
184
185
    /**
186
     * @return \Camspiers\LoggerBridge\EnvReporter\EnvReporter
187
     */
188 6
    public function getEnvReporter()
189
    {
190 6
        $this->envReporter = $this->envReporter ? : new DirectorEnvReporter();
191
192 6
        return $this->envReporter;
193
    }
194
195
    /**
196
     * @param \Camspiers\LoggerBridge\BacktraceReporter\BacktraceReporter $backtraceReporter
197
     */
198
    public function setBacktraceReporter(BacktraceReporter $backtraceReporter)
199
    {
200
        $this->backtraceReporter = $backtraceReporter;
201
    }
202
203
    /**
204
     * @return \Camspiers\LoggerBridge\BacktraceReporter\BacktraceReporter
205
     */
206
    public function getBacktraceReporter()
207
    {
208
        $this->backtraceReporter = $this->backtraceReporter ? : new BasicBacktraceReporter();
209
210
        return $this->backtraceReporter;
211
    }
212
213
    /**
214
     * @return boolean
215
     */
216 1
    public function isRegistered()
217
    {
218 1
        return $this->registered;
219
    }
220
221
    /**
222
     * @param array $errorLogGroups
223
     */
224
    public function setErrorLogGroups($errorLogGroups)
225
    {
226
        if (is_array($errorLogGroups)) {
227
            $this->errorLogGroups = $errorLogGroups;
228
        }
229
    }
230
231
    /**
232
     * @return mixed
233
     */
234
    public function getErrorLogGroups()
235
    {
236
        return $this->errorLogGroups;
237
    }
238
239
    /**
240
     * @param boolean $showErrors
241
     */
242
    public function setShowErrors($showErrors)
243
    {
244
        $this->showErrors = (bool) $showErrors;
245
    }
246
247
    /**
248
     * @return boolean
249
     */
250
    public function isShowErrors()
251
    {
252
        return $this->showErrors;
253
    }
254
255
    /**
256
     * @param int|null $reportLevel
257
     */
258
    public function setReportLevel($reportLevel)
259
    {
260
        $this->reportLevel = $reportLevel;
261
    }
262
263
    /**
264
     * @return int|null
265
     */
266
    public function getReportLevel()
267
    {
268
        return $this->reportLevel;
269
    }
270
271
    /**
272
     * @param bool $reportBacktrace
273
     */
274
    public function setReportBacktrace($reportBacktrace)
275
    {
276
        $this->reportBacktrace = $reportBacktrace;
277
    }
278
279
    /**
280
     * @param bool $exceptionInContext
281
     */
282
    public function setExceptionInContext($exceptionInContext)
283
    {
284
        $this->exceptionInContext = $exceptionInContext;
285
    }
286
287
    /**
288
     * @param string|int $reserveMemory
289
     */
290 1
    public function setReserveMemory($reserveMemory)
291
    {
292 1
        if (is_string($reserveMemory)) {
293 1
            $this->reserveMemory = $this->translateMemoryLimit($reserveMemory);
294 1
        } elseif (is_int($reserveMemory)) {
295 1
            $this->reserveMemory = $reserveMemory;
296 1
        }
297 1
    }
298
299
    /**
300
     * @return int|null
301
     */
302 2
    public function getReserveMemory()
303
    {
304 2
        return $this->reserveMemory;
305
    }
306
307
    /**
308
     * This hook function is executed from RequestProcessor before the request starts
309
     * @param  \SS_HTTPRequest $request
310
     * @param  \Session        $session
311
     * @param  \DataModel      $model
312
     * @return bool
313
     * @SuppressWarnings("unused")
314
     */
315 1
    public function preRequest(
316
        \SS_HTTPRequest $request,
317
        \Session $session,
318
        \DataModel $model
319
    ) {
320 1
        $this->registerGlobalHandlers();
321
322 1
        return true;
323
    }
324
325
    /**
326
     * This hook function is executed from RequestProcessor after the request ends
327
     * @param  \SS_HTTPRequest  $request
328
     * @param  \SS_HTTPResponse $response
329
     * @param  \DataModel       $model
330
     * @return bool
331
     * @SuppressWarnings("unused")
332
     */
333 1
    public function postRequest(
334
        \SS_HTTPRequest $request,
335
        \SS_HTTPResponse $response,
336
        \DataModel $model
337
    ) {
338 1
        $this->deregisterGlobalHandlers();
339
340 1
        return true;
341
    }
342
343
    /**
344
     * Registers global error handlers
345
     */
346 5
    public function registerGlobalHandlers() {
347 5
        if (!$this->registered) {
348
            // Store the previous error handler if there was any
349 5
            $this->registerErrorHandler();
350
            // Store the previous exception handler if there was any
351 5
            $this->registerExceptionHandler();
352
            // If the shutdown function hasn't been registered register it
353 5
            if ($this->registered === null) {
354 5
                $this->registerFatalErrorHandler();
355
356
                // If suhosin is relevant then decrease the memory_limit by the reserveMemory amount
357
                // otherwise we should be able to increase the memory by our reserveMemory amount without worry
358 5
                if ($this->isSuhosinRelevant()) {
359 1
                    $this->ensureSuhosinMemory();
360 1
                }
361 5
            }
362 5
            $this->registered = true;
363 5
        }
364 5
    }
365
366
    /**
367
     * Removes handlers we have added, and restores others if possible
368
     */
369 1
    public function deregisterGlobalHandlers()
370
    {
371 1
        if ($this->registered) {
372
            // Restore the previous error handler if available
373 1
            set_error_handler(
374
                is_callable($this->errorHandler) ? $this->errorHandler : function () { }
375 1
            );
376
            // Restore the previous exception handler if available
377 1
            set_exception_handler(
378
                is_callable($this->exceptionHandler) ? $this->exceptionHandler : function () { }
379 1
            );
380 1
            $this->registered = false;
381 1
        }
382 1
    }
383
384
    /**
385
     * Registers the error handler
386
     */
387 3
    protected function registerErrorHandler()
388
    {
389 3
        $this->errorHandler = set_error_handler(array($this, 'errorHandler'), $this->reportLevel);
390 3
    }
391
392
    /**
393
     * Registers the exception handler
394
     */
395 3
    protected function registerExceptionHandler()
396
    {
397 3
        $this->exceptionHandler = set_exception_handler(array($this, 'exceptionHandler'));
398 3
    }
399
400
    /**
401
     * Registers the fatal error handler
402
     */
403 3
    protected function registerFatalErrorHandler()
404
    {
405 3
        register_shutdown_function(array($this, 'fatalHandler'));
406 3
    }
407
408
    /**
409
     * Handles general errors, user, warn and notice
410
     * @param $errno
411
     * @param $errstr
412
     * @param $errfile
413
     * @param $errline
414
     * @return bool|string|void
415
     */
416 5
    public function errorHandler($errno, $errstr, $errfile, $errline)
417
    {
418
        // Honour error suppression through @
419 5
        if (($errorReporting = error_reporting()) === 0) {
420 1
            return true;
421
        }
422
        
423 4
        $logType = null;
424
425 4
        foreach ($this->errorLogGroups as $candidateLogType => $errorTypes) {
426 4
            if (in_array($errno, $errorTypes)) {
427 4
                $logType = $candidateLogType;
428 4
                break;
429
            }
430 4
        }
431
        
432 4
        if (is_null($logType)) {
433
            throw new \Exception(sprintf(
434
                "No log type found for errno '%s'",
435
                $errno
436
            ));
437
        }
438
439 4
        $exception = $this->createException($errstr, $errno, $errfile, $errline);
440
        
441
        // Log all errors regardless of type
442
        $context = array(
443 4
            'file' => $errfile,
444
            'line' => $errline
445 4
        );
446
    
447 4
        if ($this->reportBacktrace) {
448
            $context['backtrace'] = $this->getBacktraceReporter()->getBacktrace();
449
        }
450
451 4
        if ($this->exceptionInContext) {
452 4
            $context['exception'] = $exception;
453 4
        }
454
    
455 4
        $this->logger->$logType($errstr, $context);
456
    
457
        // Check the error_reporting level in comparison with the $errno (honouring the environment)
458
        // And check that $showErrors is on or the site is live
459 4
        if (($errno & $errorReporting) === $errno &&
460 4
            ($this->showErrors || $this->getEnvReporter()->isLive())) {
461 2
            $this->getErrorReporter()->reportError($exception);
462 2
        }
463
            
464 4
        if (in_array($errno, $this->terminatingErrors)) {
465 2
            $this->terminate();
466 2
        }
467
        
468
        // ignore the usually handling of this type of error
469 4
        return true;
470
    }
471
472
    /**
473
     * Handles uncaught exceptions
474
     * @param  \Exception  $exception
475
     * @return string|void
476
     */
477 2
    public function exceptionHandler(\Exception $exception)
478
    {
479
        $context = array(
480 2
            'file'    => $exception->getFile(),
481 2
            'line'    => $exception->getLine()
482 2
        );
483
484 2
        if ($this->reportBacktrace) {
485
            $context['backtrace'] = $this->getBacktraceReporter()->getBacktrace($exception);
486
        }
487
488 2
        if ($this->exceptionInContext) {
489 2
            $context['exception'] = $exception;
490 2
        }
491
492 2
        $this->logger->error(
493 2
            $message = 'Uncaught ' . get_class($exception) . ': ' . $exception->getMessage(),
494
            $context
495 2
        );
496
497
        // Exceptions must be reported in general because they stop the regular display of the page
498 2
        if ($this->showErrors || $this->getEnvReporter()->isLive()) {
499 1
            $this->getErrorReporter()->reportError($exception);
500 1
        }
501 2
    }
502
503
    /**
504
     * Handles fatal errors
505
     * If we are registered, and there is a fatal error then log and try to gracefully handle error output
506
     * In cases where memory is exhausted increase the memory_limit to allow for logging
507
     */
508 6
    public function fatalHandler()
509
    {
510 6
        $error = $this->getLastError();
511
        
512 6
        if ($this->isRegistered() && $this->isFatalError($error)) {
513 4
            if (defined('FRAMEWORK_PATH')) {
514 4
                chdir(FRAMEWORK_PATH);
515 4
            }
516
517 4
            if ($this->isMemoryExhaustedError($error)) {
518
                // We can safely change the memory limit be the reserve amount because if suhosin is relevant
519
                // the memory will have been decreased prior to exhaustion
520 1
                $this->changeMemoryLimit($this->reserveMemory);
521 1
            }
522
523 4
            $exception = $this->createException(
524 4
                $error['message'],
525 4
                $error['type'],
526 4
                $error['file'],
527 4
                $error['line']
528 4
            );
529
530
            $context = array(
531 4
                'file' => $error['file'],
532 4
                'line' => $error['line']
533 4
            );
534
535 4
            if ($this->reportBacktrace) {
536
                $context['backtrace'] = $this->getBacktraceReporter()->getBacktrace();
537
            }
538
539 4
            if ($this->exceptionInContext) {
540 4
                $context['exception'] = $exception;
541 4
            }
542
543 4
            $this->logger->critical($error['message'], $context);
544
545
            // Fatal errors should be reported when live as they stop the display of regular output
546 4
            if ($this->showErrors || $this->getEnvReporter()->isLive()) {
547 3
                $this->getErrorReporter()->reportError($exception);
548 3
            }
549 4
        }
550 6
    }
551
552
    /**
553
     * @param $message
554
     * @param $code
555
     * @param $filename
556
     * @param $lineno
557
     * @return \ErrorException
558
     */
559 8
    protected function createException($message, $code, $filename, $lineno)
560
    {
561 8
        return new \ErrorException(
562 8
            $message,
563 8
            $code,
564 8
            0,
565 8
            $filename,
566
            $lineno
567 8
        );
568
    }
569
570
    /**
571
     * Returns whether or not the last error was fatal
572
     * @param $error
573
     * @return bool
574
     */
575 5
    protected function isFatalError($error)
576
    {
577 5
        return $error && in_array(
578 4
            $error['type'],
579
            array(
580 4
                E_ERROR,
581 4
                E_PARSE,
582 4
                E_CORE_ERROR,
583
                E_COMPILE_ERROR
584 4
            )
585 5
        );
586
    }
587
588
    /**
589
     * @return array
590
     */
591 1
    protected function getLastError()
592
    {
593 1
        return error_get_last();
594
    }
595
596
    /**
597
     * Formats objects and array for logging
598
     * @param $arr
599
     * @return mixed
600
     */
601
    protected function format($arr)
602
    {
603
        return print_r($arr, true);
604
    }
605
606
    /**
607
     * Returns whether or not the passed in error is a memory exhausted error
608
     * @param $error array
609
     * @return bool
610
     */
611 4
    protected function isMemoryExhaustedError($error)
612
    {
613
        return
614 4
            isset($error['message'])
615 4
            && stripos($error['message'], 'memory') !== false
616 4
            && stripos($error['message'], 'exhausted') !== false;
617
    }
618
619
    /**
620
     * Change memory_limit by specified amount
621
     * @param $amount
622
     */
623
    protected function changeMemoryLimit($amount)
624
    {
625
        ini_set(
626
            'memory_limit',
627
            $this->getMemoryLimit() + $amount
628
        );
629
    }
630
631
    /**
632
     * Translate the memory limit string to a int in bytes.
633
     * @param $memoryLimit
634
     * @return int
635
     */
636 1
    protected function translateMemoryLimit($memoryLimit)
637
    {
638 1
        $unit = strtolower(substr($memoryLimit, -1, 1));
639 1
        $memoryLimit = (int) $memoryLimit;
640
        switch ($unit) {
641 1
            case 'g':
642 1
                $memoryLimit *= 1024;
643
            // intentional
644 1
            case 'm':
645 1
                $memoryLimit *= 1024;
646
            // intentional
647 1
            case 'k':
648 1
                $memoryLimit *= 1024;
649
            // intentional
650 1
        }
651
652 1
        return $memoryLimit;
653
    }
654
655
    /**
656
     * @return int
657
     */
658
    protected function getMemoryLimit()
659
    {
660
        return $this->translateMemoryLimit(ini_get('memory_limit'));
661
    }
662
663
    /**
664
     * @return int
665
     */
666
    protected function getSuhosinMemoryLimit()
667
    {
668
        return $this->translateMemoryLimit(ini_get('suhosin.memory_limit'));
669
    }
670
671
    /**
672
     * Checks if suhosin is enabled and the memory_limit is closer to suhosin.memory_limit than reserveMemory
673
     * It is in this case where it is relevant to decrease the memory available to the script before it uses all
674
     * available memory so when we need to increase the memory limit we can do so
675
     * @return bool
676
     */
677 4
    protected function isSuhosinRelevant()
678
    {
679 4
        return extension_loaded('suhosin') && $this->getSuhosinMemoryDifference() < $this->reserveMemory;
680
    }
681
682
    /**
683
     * Returns how close the max memory limit is to the current memory limit
684
     * @return int
685
     */
686
    protected function getSuhosinMemoryDifference()
687
    {
688
        return $this->getSuhosinMemoryLimit() - $this->getMemoryLimit();
689
    }
690
691
    /**
692
     * Set the memory_limit so we have enough to handle errors when suhosin is relevant
693
     */
694
    protected function ensureSuhosinMemory()
695
    {
696
        ini_set(
697
            'memory_limit',
698
            $this->getSuhosinMemoryLimit() - $this->reserveMemory
699
        );
700
    }
701
702
    /**
703
     * Provides ability to stub exits in unit tests
704
     */
705
    protected function terminate()
706
    {
707
        exit(1);
0 ignored issues
show
Coding Style Compatibility introduced by
The method terminate() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
708
    }
709
}
710