X-Git-Url: https://yaffs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=web%2Fmodules%2Fcontrib%2Fsecurity_review%2Fsrc%2FChecks%2FExecutablePhp.php;fp=web%2Fmodules%2Fcontrib%2Fsecurity_review%2Fsrc%2FChecks%2FExecutablePhp.php;h=b2377225f1f9f78fbc2e375e1c1d9470de4e5aa3;hb=ba1b5c55c66590c41ccc9844d3e62391b0399abb;hp=0000000000000000000000000000000000000000;hpb=93ef30d42f68e55d11d97312531118bbcd4cf318;p=yaffs-website diff --git a/web/modules/contrib/security_review/src/Checks/ExecutablePhp.php b/web/modules/contrib/security_review/src/Checks/ExecutablePhp.php new file mode 100644 index 000000000..b2377225f --- /dev/null +++ b/web/modules/contrib/security_review/src/Checks/ExecutablePhp.php @@ -0,0 +1,231 @@ +httpClient = $this->container->get('http_client'); + } + + /** + * {@inheritdoc} + */ + public function getNamespace() { + return 'Security Review'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return 'Executable PHP'; + } + + /** + * {@inheritdoc} + */ + public function run($cli = FALSE) { + global $base_url; + $result = CheckResult::SUCCESS; + $findings = []; + + // Set up test file data. + $message = 'Security review test ' . date('Ymdhis'); + $content = "httpClient->get($base_url . '/' . $file_path); + if ($response->getStatusCode() == 200 && $response->getBody() === $message) { + $result = CheckResult::FAIL; + $findings[] = 'executable_php'; + } + } + catch (RequestException $e) { + // Access was denied to the file. + } + + // Remove the test file. + if (file_exists('./' . $file_path)) { + @unlink('./' . $file_path); + } + + // Check for presence of the .htaccess file and if the contents are correct. + $htaccess_path = PublicStream::basePath() . '/.htaccess'; + if (!file_exists($htaccess_path)) { + $result = CheckResult::FAIL; + $findings[] = 'missing_htaccess'; + } + else { + // Check whether the contents of .htaccess are correct. + $contents = file_get_contents($htaccess_path); + $expected = FileStorage::htaccessLines(FALSE); + + // Trim each line separately then put them back together. + $contents = implode("\n", array_map('trim', explode("\n", trim($contents)))); + $expected = implode("\n", array_map('trim', explode("\n", trim($expected)))); + + if ($contents !== $expected) { + $result = CheckResult::FAIL; + $findings[] = 'incorrect_htaccess'; + } + + // Check whether .htaccess is writable. + if (!$cli) { + $writable_htaccess = is_writable($htaccess_path); + } + else { + $writable = $this->security()->findWritableFiles([$htaccess_path], TRUE); + $writable_htaccess = !empty($writable); + } + + if ($writable_htaccess) { + $findings[] = 'writable_htaccess'; + if ($result !== CheckResult::FAIL) { + $result = CheckResult::WARN; + } + } + } + + return $this->createResult($result, $findings); + } + + /** + * {@inheritdoc} + */ + public function runCli() { + return $this->run(TRUE); + } + + /** + * {@inheritdoc} + */ + public function help() { + $paragraphs = []; + $paragraphs[] = $this->t('The Drupal files directory is for user-uploaded files and by default provides some protection against a malicious user executing arbitrary PHP code against your site.'); + $paragraphs[] = $this->t('Read more about the risk of PHP code execution on Drupal.org.'); + + return [ + '#theme' => 'check_help', + '#title' => $this->t('Executable PHP in files directory'), + '#paragraphs' => $paragraphs, + ]; + } + + /** + * {@inheritdoc} + */ + public function evaluate(CheckResult $result) { + $paragraphs = []; + foreach ($result->findings() as $label) { + switch ($label) { + case 'executable_php': + $paragraphs[] = $this->t('Security Review was able to execute a PHP file written to your files directory.'); + break; + + case 'missing_htaccess': + $directory = PublicStream::basePath(); + $paragraphs[] = $this->t("The .htaccess file is missing from the files directory at @path", ['@path' => $directory]); + $paragraphs[] = $this->t("Note, if you are using a webserver other than Apache you should consult your server's documentation on how to limit the execution of PHP scripts in this directory."); + break; + + case 'incorrect_htaccess': + $paragraphs[] = $this->t("The .htaccess file exists but does not contain the correct content. It is possible it's been maliciously altered."); + break; + + case 'writable_htaccess': + $paragraphs[] = $this->t("The .htaccess file is writable which poses a risk should a malicious user find a way to execute PHP code they could alter the .htaccess file to allow further PHP code execution."); + break; + } + } + + return [ + '#theme' => 'check_evaluation', + '#paragraphs' => $paragraphs, + '#items' => [], + ]; + } + + /** + * {@inheritdoc} + */ + public function evaluatePlain(CheckResult $result) { + $paragraphs = []; + $directory = PublicStream::basePath(); + foreach ($result->findings() as $label) { + switch ($label) { + case 'executable_php': + $paragraphs[] = $this->t('PHP file executed in @path', ['@path' => $directory]); + break; + + case 'missing_htaccess': + $paragraphs[] = $this->t('.htaccess is missing from @path', ['@path' => $directory]); + break; + + case 'incorrect_htaccess': + $paragraphs[] = $this->t('.htaccess wrong content'); + break; + + case 'writable_htaccess': + $paragraphs[] = $this->t('.htaccess writable'); + break; + } + } + + return implode("\n", $paragraphs); + } + + /** + * {@inheritdoc} + */ + public function getMessage($result_const) { + switch ($result_const) { + case CheckResult::SUCCESS: + return $this->t('PHP files in the Drupal files directory cannot be executed.'); + + case CheckResult::FAIL: + return $this->t('PHP files in the Drupal files directory can be executed.'); + + case CheckResult::WARN: + return $this->t('The .htaccess file in the files directory is writable.'); + + default: + return $this->t('Unexpected result.'); + } + } + +}