0c9aca07c04aaecca036b5a96dea9c38eee70c79
[yaffs-website] / web / modules / contrib / security_review / src / Checklist.php
1 <?php
2
3 namespace Drupal\security_review;
4
5 use Drupal\Core\Access\AccessException;
6 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\Session\AccountProxyInterface;
9
10 /**
11  * Contains static functions for handling checks throughout every module.
12  */
13 class Checklist {
14
15   use DependencySerializationTrait;
16
17   /**
18    * The current user.
19    *
20    * @var \Drupal\Core\Session\AccountProxyInterface
21    */
22   protected $currentUser;
23
24   /**
25    * The security_review service.
26    *
27    * @var \Drupal\security_review\SecurityReview
28    */
29   protected $securityReview;
30
31   /**
32    * The module handler.
33    *
34    * @var \Drupal\Core\Extension\ModuleHandlerInterface
35    */
36   protected $moduleHandler;
37
38   /**
39    * Constructs a Checklist instance.
40    *
41    * @param \Drupal\security_review\SecurityReview $security_review
42    *   The SecurityReview service.
43    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
44    *   The module handler.
45    * @param \Drupal\Core\Session\AccountProxyInterface $current_user
46    *   The current user.
47    */
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;
52   }
53
54   /**
55    * Array of cached Checks.
56    *
57    * @var \Drupal\security_review\Check[]
58    */
59   private static $cachedChecks = [];
60
61   /**
62    * Clears the cached checks.
63    */
64   public static function clearCache() {
65     static::$cachedChecks = [];
66   }
67
68   /**
69    * Returns every Check.
70    *
71    * @return \Drupal\security_review\Check[]
72    *   Array of Checks.
73    */
74   public function getChecks() {
75     $checks = &static::$cachedChecks;
76     if (!empty($checks)) {
77       return $checks;
78     }
79
80     // Get checks.
81     $raw_checks = $this->moduleHandler->invokeAll('security_review_checks');
82
83     // Filter invalid checks.
84     $checks = [];
85     foreach ($raw_checks as $raw_check) {
86       if ($raw_check instanceof Check) {
87         $checks[] = $raw_check;
88       }
89     }
90
91     // Sort the checks.
92     usort($checks, [$this, 'compareChecks']);
93
94     return $checks;
95   }
96
97   /**
98    * Returns the enabled Checks.
99    *
100    * @return \Drupal\security_review\Check[]
101    *   Array of enabled Checks.
102    */
103   public function getEnabledChecks() {
104     $enabled = [];
105
106     foreach (static::getChecks() as $check) {
107       if (!$check->isSkipped()) {
108         $enabled[] = $check;
109       }
110     }
111
112     return $enabled;
113   }
114
115   /**
116    * Groups an array of checks by their namespaces.
117    *
118    * @param \Drupal\security_review\Check[] $checks
119    *   The array of Checks to group.
120    *
121    * @return array
122    *   Array containing Checks grouped by their namespaces.
123    */
124   public function groupChecksByNamespace(array $checks) {
125     $output = [];
126
127     foreach ($checks as $check) {
128       $output[$check->getMachineNamespace()][] = $check;
129     }
130
131     return $output;
132   }
133
134   /**
135    * Runs enabled checks and stores their results.
136    */
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());
143     }
144     else {
145       throw new AccessException();
146     }
147   }
148
149   /**
150    * Runs an array of checks.
151    *
152    * @param \Drupal\security_review\Check[] $checks
153    *   The array of Checks to run.
154    * @param bool $cli
155    *   Whether to call runCli() instead of run().
156    *
157    * @return \Drupal\security_review\CheckResult[]
158    *   The array of CheckResults generated.
159    */
160   public function runChecks(array $checks, $cli = FALSE) {
161     $results = [];
162
163     foreach ($checks as $check) {
164       if ($cli) {
165         $result = $check->runCli();
166       }
167       else {
168         $result = $check->run();
169       }
170       $this->securityReview->logCheckResult($result);
171       $results[] = $result;
172     }
173
174     return $results;
175   }
176
177   /**
178    * Stores an array of CheckResults.
179    *
180    * @param \Drupal\security_review\CheckResult[] $results
181    *   The CheckResults to store.
182    */
183   public function storeResults(array $results) {
184     foreach ($results as $result) {
185       $result->check()->storeResult($result);
186     }
187   }
188
189   /**
190    * Finds a check by its namespace and title.
191    *
192    * @param string $namespace
193    *   The machine namespace of the requested check.
194    * @param string $title
195    *   The machine title of the requested check.
196    *
197    * @return null|\Drupal\security_review\Check
198    *   The Check or null if it doesn't exist.
199    */
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) {
205         return $check;
206       }
207     }
208
209     return NULL;
210   }
211
212   /**
213    * Finds a Check by its id.
214    *
215    * @param string $id
216    *   The machine namespace of the requested check.
217    *
218    * @return null|\Drupal\security_review\Check
219    *   The Check or null if it doesn't exist.
220    */
221   public function getCheckById($id) {
222     foreach (static::getChecks() as $check) {
223       if ($check->id() == $id) {
224         return $check;
225       }
226     }
227
228     return NULL;
229   }
230
231   /**
232    * Helper function for sorting checks.
233    *
234    * @param \Drupal\security_review\Check $a
235    *   Check A.
236    * @param \Drupal\security_review\Check $b
237    *   Check B.
238    *
239    * @return int
240    *   The comparison's result.
241    */
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) {
248       return -1;
249     }
250     elseif (!$a_is_local && $b_is_local) {
251       return 1;
252     }
253     else {
254       if ($a->getNamespace() == $b->getNamespace()) {
255         // If the namespaces match, sort by title.
256         return strcmp($a->getTitle(), $b->getTitle());
257       }
258       else {
259         // If the namespaces don't mach, sort by namespace.
260         return strcmp($a->getNamespace(), $b->getNamespace());
261       }
262     }
263   }
264
265 }