Completed
Push — 8.x-1.x ( 5d8f74...bc9e07 )
by Frédéric G.
28s queued 11s
created

ExternalCode::checkExternal()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 13
c 1
b 0
f 1
dl 0
loc 20
rs 9.8333
cc 4
nc 4
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Drupal\qa\Plugin\QaCheck\System;
6
7
use Drupal\Core\Extension\ModuleExtensionList;
8
use Drupal\Core\Extension\ThemeExtensionList;
9
use Drupal\qa\Data;
10
use Drupal\qa\Pass;
11
use Drupal\qa\Plugin\QaCheckBase;
12
use Drupal\qa\Plugin\QaCheckInterface;
13
use Drupal\qa\Plugin\QaCheckManager;
14
use Drupal\qa\Result;
15
use ReflectionFunction;
16
use Symfony\Component\DependencyInjection\ContainerInterface;
17
18
/**
19
 * ExternalCode identifies code loaded from outside the project root and vendor.
20
 *
21
 * @QaCheck(
22
 *   id = "system.external",
23
 *   label = @Translation("System: external code"),
24
 *   details = @Translation("External code may be the result of an exploit."),
25
 *   usesBatch = false,
26
 *   steps = 1,
27
 * )
28
 */
29
class ExternalCode extends QaCheckBase implements QaCheckInterface {
30
31
  const NAME = 'system.external';
32
33
  /**
34
   * The element_list.module service.
35
   *
36
   * @var \Drupal\Core\Extension\ModuleExtensionList
37
   */
38
  protected $elm;
39
40
  /**
41
   * The extension.list.theme service.
42
   *
43
   * @var \Drupal\Core\Extension\ThemeExtensionList
44
   */
45
  protected $elt;
46
47
  /**
48
   * The list of internal functions.
49
   *
50
   * @var array
51
   */
52
  protected $internalFunctions;
53
54
  /**
55
   * The plugin_manager.qa_check service.
56
   *
57
   * @var \Drupal\qa\Plugin\QaCheckManager
58
   */
59
  protected $qam;
60
61
  /**
62
   * ExternalCode constructor.
63
   *
64
   * @param array $configuration
65
   *   The plugin configuration.
66
   * @param string $id
67
   *   The plugin ID.
68
   * @param array $definition
69
   *   The plugin definition.
70
   * @param \Drupal\Core\Extension\ModuleExtensionList $elm
71
   *   The extension.list.module service.
72
   * @param \Drupal\Core\Extension\ThemeExtensionList $elt
73
   *   The extension.list.theme service.
74
   * @param \Drupal\qa\Plugin\QaCheckManager $qam
75
   *   The plugin_manager.qa_check service.
76
   */
77
  public function __construct(
78
    array $configuration,
79
    string $id,
80
    array $definition,
81
    ModuleExtensionList $elm,
82
    ThemeExtensionList $elt,
83
    QaCheckManager $qam
84
  ) {
85
    parent::__construct($configuration, $id, $definition);
86
    $this->elm = $elm;
87
    $this->elt = $elt;
88
    $this->qam = $qam;
89
90
    $this->qam->initInternalFunctions();
91
  }
92
93
  /**
94
   * {@inheritdoc}
95
   */
96
  public static function create(
97
    ContainerInterface $container,
98
    array $configuration,
99
    $id,
100
    $definition
101
  ) {
102
    $elm = $container->get('extension.list.module');
103
    assert($elm instanceof ModuleExtensionList);
104
    $elt = $container->get('extension.list.theme');
105
    assert($elt instanceof ThemeExtensionList);
106
    $qam = $container->get(Data::MANAGER);
107
    assert($qam instanceof QaCheckManager);
108
    return new static($configuration, $id, $definition, $elm, $elt, $qam);
109
  }
110
111
  /**
112
   * Identify code loaded from outside the web root and vendor directory.
113
   *
114
   * This is not necessarily an issue, but warrants a manual verification.
115
   *
116
   * @return \Drupal\qa\Result
117
   *   The check result.
118
   */
119
  protected function checkExternal(): Result {
120
    $external = [];
121
    $funcs = array_flip(get_defined_functions()['user']);
122
    foreach ($funcs as $func => $_) {
123
      try {
124
        $rf = new ReflectionFunction($func);
125
      }
126
      catch (\ReflectionException $e) {
127
        // XXX probably cannot happen since the function is loaded.
128
        $external[$func] = ('function not found');
129
        continue;
130
      }
131
      $file = realpath($rf->getFileName());
132
      // TODO improve resilience, probably at the cost of computing time.
133
      if (strpos($file, $this->qam->root) === FALSE) {
134
        $external[$func] = $file;
135
      }
136
    }
137
    $res = new Result('external', empty($external), $external);
138
    return $res;
139
140
  }
141
142
  /**
143
   * {@inheritdoc}
144
   */
145
  public function run(): Pass {
146
    $pass = parent::run();
147
    $pass->record($this->checkExternal());
148
    $pass->life->end();
149
    return $pass;
150
  }
151
152
}
153