diff --git a/composer/Plugin/VendorHardening/FileSecurity.php b/composer/Plugin/VendorHardening/FileSecurity.php new file mode 100644 index 0000000000000000000000000000000000000000..c8bcd6d328dae6ff209cce34d1cbaf6313a424c2 --- /dev/null +++ b/composer/Plugin/VendorHardening/FileSecurity.php @@ -0,0 +1,164 @@ +<?php + +namespace Drupal\Composer\Plugin\VendorHardening; + +/** + * Provides file security functions. + * + * IMPORTANT: This file is duplicated at /lib/Drupal/Component/FileSecurity. + * If any change is made here, the same change should be made in the duplicate. + * See https://www.drupal.org/project/drupal/issues/3079481. + */ +class FileSecurity { + + /** + * Writes an .htaccess file in the given directory, if it doesn't exist. + * + * @param string $directory + * The directory. + * @param bool $deny_public_access + * (optional) Set to FALSE to ensure an .htaccess file for an open and + * public directory. Default is TRUE. + * @param bool $force + * (optional) Set to TRUE to force overwrite an existing file. + * + * @return bool + * TRUE if the file already exists or was created. FALSE otherwise. + */ + public static function writeHtaccess($directory, $deny_public_access = TRUE, $force = FALSE) { + return self::writeFile($directory, '/.htaccess', self::htaccessLines($deny_public_access), $force); + } + + /** + * Returns the standard .htaccess lines that Drupal writes. + * + * @param bool $deny_public_access + * (optional) Set to FALSE to return the .htaccess lines for an open and + * public directory that allows Apache to serve files, but not execute code. + * The default is TRUE, which returns the .htaccess lines for a private and + * protected directory that Apache will deny all access to. + * + * @return string + * The desired contents of the .htaccess file. + * + * @see file_save_htaccess() + */ + public static function htaccessLines($deny_public_access = TRUE) { + $lines = static::htaccessPreventExecution(); + + if ($deny_public_access) { + $lines = static::denyPublicAccess() . "\n\n$lines"; + } + + return $lines; + } + + /** + * Returns htaccess directives to deny execution in a given directory. + * + * @return string + * Apache htaccess directives to prevent execution of files in a location. + */ + protected static function htaccessPreventExecution() { + return <<<EOF +# Turn off all options we don't need. +Options -Indexes -ExecCGI -Includes -MultiViews + +# Set the catch-all handler to prevent scripts from being executed. +SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 +<Files *> + # Override the handler again if we're run later in the evaluation list. + SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003 +</Files> + +# If we know how to do it safely, disable the PHP engine entirely. +<IfModule mod_php5.c> + php_flag engine off +</IfModule> +<IfModule mod_php7.c> + php_flag engine off +</IfModule> +EOF; + } + + /** + * Returns htaccess directives to block all access to a given directory. + * + * @return string + * Apache htaccess directives to block access to a location. + */ + protected static function denyPublicAccess() { + return <<<EOF +# Deny all requests from Apache 2.4+. +<IfModule mod_authz_core.c> + Require all denied +</IfModule> + +# Deny all requests from Apache 2.0-2.2. +<IfModule !mod_authz_core.c> + Deny from all +</IfModule> +EOF; + } + + /** + * Writes a web.config file in the given directory, if it doesn't exist. + * + * @param string $directory + * The directory. + * @param bool $force + * (optional) Set to TRUE to force overwrite an existing file. + * + * @return bool + * TRUE if the file already exists or was created. FALSE otherwise. + */ + public static function writeWebConfig($directory, $force = FALSE) { + return self::writeFile($directory, '/web.config', self::webConfigLines(), $force); + } + + /** + * Returns the standard web.config lines for security. + * + * @return string + * The contents of the web.config file. + */ + public static function webConfigLines() { + return <<<EOT +<configuration> + <system.webServer> + <authorization> + <deny users="*"> + </authorization> + </system.webServer> +</configuration> +EOT; + } + + /** + * Writes the contents to the file in the given directory. + * + * @param string $directory + * The directory to write to. + * @param string $filename + * The file name. + * @param string $contents + * The file contents. + * @param bool $force + * TRUE if we should force the write over an existing file. + * + * @return bool + * TRUE if writing the file was successful. + */ + protected static function writeFile($directory, $filename, $contents, $force) { + $file_path = $directory . DIRECTORY_SEPARATOR . $filename; + // Don't overwrite if the file exists unless forced. + if (file_exists($file_path) && !$force) { + return TRUE; + } + if (file_exists($directory) && is_writable($directory) && file_put_contents($file_path, $contents)) { + return @chmod($file_path, 0444); + } + return FALSE; + } + +} diff --git a/composer/Plugin/VendorHardening/VendorHardeningPlugin.php b/composer/Plugin/VendorHardening/VendorHardeningPlugin.php index cf049003669f24480d82552dfa3ed7be8fa8e399..7e36e0e9e18440ea6dd35bdd07fb91e198fcc2ce 100644 --- a/composer/Plugin/VendorHardening/VendorHardeningPlugin.php +++ b/composer/Plugin/VendorHardening/VendorHardeningPlugin.php @@ -11,7 +11,6 @@ use Composer\Util\Filesystem; use Composer\Script\Event; use Composer\Installer\PackageEvents; -use Drupal\Component\FileSecurity\FileSecurity; /** * A Composer plugin to clean out your project's vendor directory. diff --git a/composer/Plugin/VendorHardening/composer.json b/composer/Plugin/VendorHardening/composer.json index 3bafc9a7858da7637df76686567d9913d71fbc32..da71b12a1f3f362bc9119aedbe781fc47026877e 100644 --- a/composer/Plugin/VendorHardening/composer.json +++ b/composer/Plugin/VendorHardening/composer.json @@ -15,7 +15,6 @@ }, "require": { "php": ">=7.0.8", - "composer-plugin-api": "^1.1", - "drupal/core-file-security": "^8.8" + "composer-plugin-api": "^1.1" } } diff --git a/core/lib/Drupal/Component/FileSecurity/FileSecurity.php b/core/lib/Drupal/Component/FileSecurity/FileSecurity.php index efe1119c2707aa01a0bb11e98042e796e79f0158..8673d3c2ab29a6b20e1645ce8675f1f00f230cd1 100644 --- a/core/lib/Drupal/Component/FileSecurity/FileSecurity.php +++ b/core/lib/Drupal/Component/FileSecurity/FileSecurity.php @@ -4,6 +4,10 @@ /** * Provides file security functions. + * + * IMPORTANT: This file is duplicated at /composer/Plugin/VendorHardening. + * If any change is made here, the same change should be made in the duplicate. + * See https://www.drupal.org/project/drupal/issues/3079481 */ class FileSecurity {