5 * Drush commands for Security Review module.
8 use Drupal\security_review\CheckResult;
11 * Implements hook_drush_command().
13 function security_review_drush_command() {
16 $items['security-review'] = [
17 'aliases' => ['secrev'],
18 'callback' => 'security_review_drush',
19 'description' => "Run the Security Review checklist",
21 'store' => 'Write results to the database',
22 'log' => 'Log results of each check to watchdog, defaults to off',
23 'lastrun' => 'Do not run the checklist, just print last results',
24 'check' => 'Comma-separated list of specified checks to run. See README.txt for list of options',
25 'skip' => 'Comma-separated list of specified checks not to run. This takes precedence over --check.',
26 'short' => "Short result messages instead of full description (e.g. 'Text formats')",
27 'results' => 'Show the incorrect settings for failed checks',
30 'secrev' => 'Run the checklist and output the results',
31 'secrev --store' => 'Run the checklist, store, and output the results',
32 'secrev --lastrun' => 'Output the stored results from the last run of the checklist',
34 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
37 'pipe-format' => 'csv',
38 'fields-default' => ['message', 'status'],
40 'message' => 'Message',
42 'findings' => 'Findings',
44 'output-data-type' => 'format-table',
52 * Implements hook_drush_help().
54 function security_review_drush_help($section) {
56 case 'drush:security-review':
57 return dt("Run configuration security checks on your Drupal site.");
62 * Runs a checklist and displays results.
64 function security_review_drush() {
65 /** @var \Drupal\security_review\SecurityReview $security_review */
66 $security_review = Drupal::service('security_review');
68 /** @var \Drupal\security_review\Checklist $checklist */
69 $checklist = Drupal::service('security_review.checklist');
71 $store = drush_get_option('store');
72 $log = drush_get_option('log');
73 $last_run = drush_get_option('lastrun');
74 $run_checks = drush_get_option_list('check');
75 $skip_checks = drush_get_option_list('skip');
76 $short_titles = drush_get_option('short');
77 $show_findings = drush_get_option('results');
79 // Set temporary logging.
80 $log = in_array($log, [TRUE, 1, 'TRUE']);
81 $security_review->setLogging($log, TRUE);
83 if (!empty($short_titles)) {
87 $short_titles = FALSE;
92 // Do a normal security review run.
93 /** @var \Drupal\security_review\Check[] $checks */
95 /** @var \Drupal\security_review\Check[] $to_skip */
98 // Fill the $checks array.
99 if (!empty($run_checks)) {
100 // Get explicitly specified checks.
101 foreach ($run_checks as $check) {
102 $checks[] = _security_review_drush_get_check($check);
106 // Get the whole checklist.
107 $checks = $checklist->getChecks();
110 // Mark checks listed after --skip for removal.
111 if (!empty($skip_checks)) {
112 foreach ($skip_checks as $skip_check) {
113 $to_skip[] = _security_review_drush_get_check($skip_check);
117 // If storing, mark skipped checks for removal.
119 foreach ($checks as $check) {
120 if ($check->isSkipped()) {
126 // Remove the skipped checks from $checks.
127 foreach ($to_skip as $skip_check) {
128 foreach ($checks as $key => $check) {
129 if ($check->id() == $skip_check->id()) {
130 unset($checks[$key]);
135 // If $checks is empty at this point, return with an error.
136 if (empty($checks)) {
137 return drush_set_error('EMPTY_CHECKLIST', dt("No checks to run. Run 'drush help secrev' for option use or consult the drush section of API.txt for further help."));
141 $results = $checklist->runChecks($checks, TRUE);
143 // Store the results.
145 $checklist->storeResults($results);
149 // Show the latest stored results.
150 foreach ($checklist->getChecks() as $check) {
151 $last_result = $check->lastResult($show_findings);
152 if ($last_result instanceof CheckResult) {
153 $results[] = $last_result;
158 return _security_review_drush_format_results($results, $short_titles, $show_findings);
162 * Helper function for parsing input check name strings.
164 * @param string $check_name
167 * @return \Drupal\security_review\Check|null
170 function _security_review_drush_get_check($check_name) {
171 /** @var \Drupal\security_review\Checklist $checklist */
172 $checklist = Drupal::service('security_review.checklist');
174 // Default namespace is Security Review.
175 $namespace = 'security_review';
176 $title = $check_name;
178 // Set namespace and title if explicitly defined.
179 if (strpos($check_name, ':') !== FALSE) {
180 list($namespace, $title) = explode(':', $check_name);
183 // Return the found check if any.
184 return $checklist->getCheck($namespace, $title);
188 * Helper function to compile Security Review results.
190 * @param \Drupal\security_review\CheckResult[] $results
191 * An array of CheckResults.
192 * @param bool $short_titles
193 * Whether to use short message (check title) or full check success or failure
195 * @param bool $show_findings
196 * Whether to print failed check results.
199 * The results of the security review checks.
201 function _security_review_drush_format_results(array $results, $short_titles = FALSE, $show_findings = FALSE) {
204 foreach ($results as $result) {
205 if ($result instanceof CheckResult) {
206 if (!$result->isVisible()) {
207 // Continue with the next check.
211 $check = $result->check();
212 $message = $short_titles ? $check->getTitle() : $result->resultMessage();
215 // Set log level according to check result.
216 switch ($result->result()) {
217 case CheckResult::SUCCESS:
221 case CheckResult::FAIL:
225 case CheckResult::WARN:
229 case CheckResult::INFO:
235 if ($show_findings) {
236 $findings = trim($result->check()->evaluatePlain($result));
237 if ($findings != '') {
238 $message .= "\n" . $findings;
242 $output[$check->id()] = [
243 'message' => (string) $message,
245 'findings' => $result->findings(),