5 * Contains \Drupal\security_review\Checklist.
8 namespace Drupal\security_review;
10 use Drupal\Core\Access\AccessException;
11 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
12 use Drupal\Core\Extension\ModuleHandlerInterface;
13 use Drupal\Core\Session\AccountProxyInterface;
16 * Contains static functions for handling checks throughout every module.
20 use DependencySerializationTrait;
25 * @var \Drupal\Core\Session\AccountProxyInterface
27 protected $currentUser;
30 * The security_review service.
32 * @var \Drupal\security_review\SecurityReview
34 protected $securityReview;
39 * @var \Drupal\Core\Extension\ModuleHandlerInterface
41 protected $moduleHandler;
44 * Constructs a Checklist instance.
46 * @param \Drupal\security_review\SecurityReview $security_review
47 * The SecurityReview service.
48 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
50 * @param \Drupal\Core\Session\AccountProxyInterface $current_user
53 public function __construct(SecurityReview $security_review, ModuleHandlerInterface $module_handler, AccountProxyInterface $current_user) {
54 $this->securityReview = $security_review;
55 $this->moduleHandler = $module_handler;
56 $this->currentUser = $current_user;
60 * Array of cached Checks.
62 * @var \Drupal\security_review\Check[]
64 private static $cachedChecks = [];
67 * Clears the cached checks.
69 public static function clearCache() {
70 static::$cachedChecks = [];
74 * Returns every Check.
76 * @return \Drupal\security_review\Check[]
79 public function getChecks() {
80 $checks = &static::$cachedChecks;
81 if (!empty($checks)) {
86 $raw_checks = $this->moduleHandler->invokeAll('security_review_checks');
88 // Filter invalid checks.
90 foreach ($raw_checks as $raw_check) {
91 if ($raw_check instanceof Check) {
92 $checks[] = $raw_check;
97 usort($checks, [$this, 'compareChecks']);
103 * Returns the enabled Checks.
105 * @return \Drupal\security_review\Check[]
106 * Array of enabled Checks.
108 public function getEnabledChecks() {
111 foreach (static::getChecks() as $check) {
112 if (!$check->isSkipped()) {
121 * Groups an array of checks by their namespaces.
123 * @param \Drupal\security_review\Check[] $checks
124 * The array of Checks to group.
127 * Array containing Checks grouped by their namespaces.
129 public function groupChecksByNamespace(array $checks) {
132 foreach ($checks as $check) {
133 $output[$check->getMachineNamespace()][] = $check;
140 * Runs enabled checks and stores their results.
142 public function runChecklist() {
143 if ($this->currentUser->hasPermission('run security checks')) {
144 $checks = $this->getEnabledChecks();
145 $results = $this->runChecks($checks);
146 $this->storeResults($results);
147 $this->securityReview->setLastRun(time());
150 throw new AccessException();
155 * Runs an array of checks.
157 * @param \Drupal\security_review\Check[] $checks
158 * The array of Checks to run.
160 * Whether to call runCli() instead of run().
162 * @return \Drupal\security_review\CheckResult[]
163 * The array of CheckResults generated.
165 public function runChecks(array $checks, $cli = FALSE) {
168 foreach ($checks as $check) {
170 $result = $check->runCli();
173 $result = $check->run();
175 $this->securityReview->logCheckResult($result);
176 $results[] = $result;
183 * Stores an array of CheckResults.
185 * @param \Drupal\security_review\CheckResult[] $results
186 * The CheckResults to store.
188 public function storeResults(array $results) {
189 foreach ($results as $result) {
190 $result->check()->storeResult($result);
195 * Finds a check by its namespace and title.
197 * @param string $namespace
198 * The machine namespace of the requested check.
199 * @param string $title
200 * The machine title of the requested check.
202 * @return null|\Drupal\security_review\Check
203 * The Check or null if it doesn't exist.
205 public function getCheck($namespace, $title) {
206 foreach (static::getChecks() as $check) {
207 $same_namespace = $check->getMachineNamespace() == $namespace;
208 $same_title = $check->getMachineTitle() == $title;
209 if ($same_namespace && $same_title) {
218 * Finds a Check by its id.
221 * The machine namespace of the requested check.
223 * @return null|\Drupal\security_review\Check
224 * The Check or null if it doesn't exist.
226 public function getCheckById($id) {
227 foreach (static::getChecks() as $check) {
228 if ($check->id() == $id) {
237 * Helper function for sorting checks.
239 * @param \Drupal\security_review\Check $a
241 * @param \Drupal\security_review\Check $b
245 * The comparison's result.
247 public function compareChecks(Check $a, Check $b) {
248 // If one comes from security_review and the other doesn't, prefer the one
249 // with the security_review namespace.
250 $a_is_local = $a->getMachineNamespace() == 'security_review';
251 $b_is_local = $b->getMachineNamespace() == 'security_review';
252 if ($a_is_local && !$b_is_local) {
255 elseif (!$a_is_local && $b_is_local) {
259 if ($a->getNamespace() == $b->getNamespace()) {
260 // If the namespaces match, sort by title.
261 return strcmp($a->getTitle(), $b->getTitle());
264 // If the namespaces don't mach, sort by namespace.
265 return strcmp($a->getNamespace(), $b->getNamespace());