3 namespace Drupal\security_review;
5 use Drupal\Core\Access\AccessException;
6 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\Session\AccountProxyInterface;
11 * Contains static functions for handling checks throughout every module.
15 use DependencySerializationTrait;
20 * @var \Drupal\Core\Session\AccountProxyInterface
22 protected $currentUser;
25 * The security_review service.
27 * @var \Drupal\security_review\SecurityReview
29 protected $securityReview;
34 * @var \Drupal\Core\Extension\ModuleHandlerInterface
36 protected $moduleHandler;
39 * Constructs a Checklist instance.
41 * @param \Drupal\security_review\SecurityReview $security_review
42 * The SecurityReview service.
43 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
45 * @param \Drupal\Core\Session\AccountProxyInterface $current_user
48 public function __construct(SecurityReview $security_review, ModuleHandlerInterface $module_handler, AccountProxyInterface $current_user) {
49 $this->securityReview = $security_review;
50 $this->moduleHandler = $module_handler;
51 $this->currentUser = $current_user;
55 * Array of cached Checks.
57 * @var \Drupal\security_review\Check[]
59 private static $cachedChecks = [];
62 * Clears the cached checks.
64 public static function clearCache() {
65 static::$cachedChecks = [];
69 * Returns every Check.
71 * @return \Drupal\security_review\Check[]
74 public function getChecks() {
75 $checks = &static::$cachedChecks;
76 if (!empty($checks)) {
81 $raw_checks = $this->moduleHandler->invokeAll('security_review_checks');
83 // Filter invalid checks.
85 foreach ($raw_checks as $raw_check) {
86 if ($raw_check instanceof Check) {
87 $checks[] = $raw_check;
92 usort($checks, [$this, 'compareChecks']);
98 * Returns the enabled Checks.
100 * @return \Drupal\security_review\Check[]
101 * Array of enabled Checks.
103 public function getEnabledChecks() {
106 foreach (static::getChecks() as $check) {
107 if (!$check->isSkipped()) {
116 * Groups an array of checks by their namespaces.
118 * @param \Drupal\security_review\Check[] $checks
119 * The array of Checks to group.
122 * Array containing Checks grouped by their namespaces.
124 public function groupChecksByNamespace(array $checks) {
127 foreach ($checks as $check) {
128 $output[$check->getMachineNamespace()][] = $check;
135 * Runs enabled checks and stores their results.
137 public function runChecklist() {
138 if ($this->currentUser->hasPermission('run security checks')) {
139 $checks = $this->getEnabledChecks();
140 $results = $this->runChecks($checks);
141 $this->storeResults($results);
142 $this->securityReview->setLastRun(time());
145 throw new AccessException();
150 * Runs an array of checks.
152 * @param \Drupal\security_review\Check[] $checks
153 * The array of Checks to run.
155 * Whether to call runCli() instead of run().
157 * @return \Drupal\security_review\CheckResult[]
158 * The array of CheckResults generated.
160 public function runChecks(array $checks, $cli = FALSE) {
163 foreach ($checks as $check) {
165 $result = $check->runCli();
168 $result = $check->run();
170 $this->securityReview->logCheckResult($result);
171 $results[] = $result;
178 * Stores an array of CheckResults.
180 * @param \Drupal\security_review\CheckResult[] $results
181 * The CheckResults to store.
183 public function storeResults(array $results) {
184 foreach ($results as $result) {
185 $result->check()->storeResult($result);
190 * Finds a check by its namespace and title.
192 * @param string $namespace
193 * The machine namespace of the requested check.
194 * @param string $title
195 * The machine title of the requested check.
197 * @return null|\Drupal\security_review\Check
198 * The Check or null if it doesn't exist.
200 public function getCheck($namespace, $title) {
201 foreach (static::getChecks() as $check) {
202 $same_namespace = $check->getMachineNamespace() == $namespace;
203 $same_title = $check->getMachineTitle() == $title;
204 if ($same_namespace && $same_title) {
213 * Finds a Check by its id.
216 * The machine namespace of the requested check.
218 * @return null|\Drupal\security_review\Check
219 * The Check or null if it doesn't exist.
221 public function getCheckById($id) {
222 foreach (static::getChecks() as $check) {
223 if ($check->id() == $id) {
232 * Helper function for sorting checks.
234 * @param \Drupal\security_review\Check $a
236 * @param \Drupal\security_review\Check $b
240 * The comparison's result.
242 public function compareChecks(Check $a, Check $b) {
243 // If one comes from security_review and the other doesn't, prefer the one
244 // with the security_review namespace.
245 $a_is_local = $a->getMachineNamespace() == 'security_review';
246 $b_is_local = $b->getMachineNamespace() == 'security_review';
247 if ($a_is_local && !$b_is_local) {
250 elseif (!$a_is_local && $b_is_local) {
254 if ($a->getNamespace() == $b->getNamespace()) {
255 // If the namespaces match, sort by title.
256 return strcmp($a->getTitle(), $b->getTitle());
259 // If the namespaces don't mach, sort by namespace.
260 return strcmp($a->getNamespace(), $b->getNamespace());