diff --git a/Policy/ModuleSearch.policy.yml b/Policy/ModuleSearch.policy.yml new file mode 100644 index 0000000..1e1b292 --- /dev/null +++ b/Policy/ModuleSearch.policy.yml @@ -0,0 +1,29 @@ +title: "Search for modules" +name: Drupal:ModuleScan +class: \Drutiny\GovCMS\Audit\ModuleSearch +tags: + - Compliance +description: | + Scan a specified directory or the default theme path for additional themes or modules. +remediation: | + Additonal themes will not fail - remove any modules contained in the directory. +success: | + No modules were found in the specified directory. + + The following themes were found: + {{#themesFound}} + - {{ . }} + {{/themesFound}} +failure: | + The following modules were found in the specified directory: + + {{#modulesFound}} + - {{ . }} + {{/modulesFound}} +parameters: + directory: + type: string + default: "" + description: | + Path to the directory to scan for nested modules and themes. + Leave as default ("") to dynamically detect the default theme path. diff --git a/Profiles/gitlab.profile.yml b/Profiles/gitlab.profile.yml new file mode 100644 index 0000000..fe6ee5d --- /dev/null +++ b/Profiles/gitlab.profile.yml @@ -0,0 +1,48 @@ +title: 'Drupal Gitlab Pipeline' +format: + html: + template: govcms-page +policies: + Drupal:LintTheme: + severity: low + Drupal:ModuleScan: + severity: critical + Drupal:ThemeSecurity: + severity: critical + parameters: + filetypes: + - php + - inc + - theme + patterns: + - "_POST" + - "exec\\(" + - "db_query" + - "db_select" + - "db_merge" + - "db_update" + - "db_write_record" + - "\\->query" + - "drupal_http_request" + - "curl_init" + - "passthru" + - "proc_open" + - "system\\(" + - "sleep\\(" + - "mysql_" + - "mysqli" + - "sqlite" + - "db_query" + - "db_fetch" + - "db_result" + - "pager_query" + - "db_set_active" + - "db_select" + - "db_insert" + - "db_update" + - "db_delete" + - "fetchAll" + - "fetchField" + - "fetchObject" + - "fetchAssoc" + - "countQuery" diff --git a/src/Audit/ModuleSearch.php b/src/Audit/ModuleSearch.php new file mode 100644 index 0000000..5ba6505 --- /dev/null +++ b/src/Audit/ModuleSearch.php @@ -0,0 +1,88 @@ +drush(['format' => 'json'])->status(); + $themeName = $info['theme']; + $rootPath = $info['root']; + $themePath = $sandbox->drush()->eval("'return drupal_get_path('theme', '{$themeName}');'"); + $results = array(); + $modulesFound = array(); + $themesFound = array(); + + $directory = $sandbox->getParameter('directory', ""); + if ($directory === '') { + $directory = "$rootPath/$themePath"; + } + + $types = array('info', 'module', 'theme', 'info.yml'); + $command = ['find', $directory, '-type f']; + + $conditions = []; + foreach ($types as $type) { + $conditions[] = '-iname "*.' . $type . '"'; + } + + $command[] = '\( ' . implode(' -or ', $conditions) . ' \)'; + $command[] = " || exit 0"; + + $command = '\'' . implode(' ', $command) . '\''; + $sandbox->logger()->info('[' . __CLASS__ . '] ' . $command); + $output = $sandbox->drush()->ssh($command); + + if (empty($output)) { + return Audit::NOT_APPLICABLE; + } + + $matches = array_filter(explode(PHP_EOL, $output)); + $matches = array_map(function ($line) { + list($filepath, $line_number, $code) = explode(':', $line, 3); + return [ + 'file' => basename($filepath), + 'directory' => implode('/', array_slice(explode('/', $filepath), 0, -1)), + 'machine_name' => implode('.', array_slice(explode('.', basename($filepath)), 0, 1)), + + ]; + }, $matches); + + if ($result = $sandbox->drush(['format' => 'json', 'fields' => 'type'])->pmList()) { + foreach ($matches[0] as $module) { + if ($result[$module]['type'] === 'module') { + $modulesFound[] = $module; + } + if ($result[$module]['type'] === 'theme') { + $themesFound[] = $module; + } + } + } + else { + return Audit::ERROR; + } + + $sandbox->setParameter('themesFound', $themesFound); + $sandbox->setParameter('modulesFound', $modulesFound); + + return empty($modulesFound); + } + +}