1<?php 2 3namespace Drupal\Tests\system\Functional\System; 4 5use Drupal\Component\Render\FormattableMarkup; 6use Drupal\Core\Site\Settings; 7use Drupal\Core\StringTranslation\StringTranslationTrait; 8use Drupal\Tests\BrowserTestBase; 9 10/** 11 * Tests Drupal permissions hardening of /sites subdirectories. 12 * 13 * @group system 14 */ 15class SitesDirectoryHardeningTest extends BrowserTestBase { 16 use StringTranslationTrait; 17 18 /** 19 * {@inheritdoc} 20 */ 21 protected $defaultTheme = 'stark'; 22 23 /** 24 * Tests the default behavior to restrict directory permissions is enforced. 25 * 26 * Checks both the current sites directory and settings.php. 27 */ 28 public function testSitesDirectoryHardening() { 29 $site_path = $this->kernel->getSitePath(); 30 $settings_file = $this->settingsFile($site_path); 31 32 // First, we check based on what the initial install has set. 33 $this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file.', ['@file' => $site_path])); 34 35 // We intentionally don't check for settings.local.php as that file is 36 // not created by Drupal. 37 $this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file.', ['@file' => $settings_file])); 38 39 $this->makeWritable($site_path); 40 $this->checkSystemRequirements(); 41 42 $this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file after manual permissions change.', ['@file' => $site_path])); 43 $this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file after manual permissions change.', ['@file' => $settings_file])); 44 } 45 46 /** 47 * Tests writable files remain writable when directory hardening is disabled. 48 */ 49 public function testSitesDirectoryHardeningConfig() { 50 $site_path = $this->kernel->getSitePath(); 51 $settings_file = $this->settingsFile($site_path); 52 53 // Disable permissions enforcement. 54 $settings = Settings::getAll(); 55 $settings['skip_permissions_hardening'] = TRUE; 56 new Settings($settings); 57 $this->assertTrue(Settings::get('skip_permissions_hardening'), 'Able to set skip permissions hardening to true.'); 58 $this->makeWritable($site_path); 59 60 // Manually trigger the requirements check. 61 $requirements = $this->checkSystemRequirements(); 62 $this->assertEqual(REQUIREMENT_WARNING, $requirements['configuration_files']['severity'], 'Warning severity is properly set.'); 63 $this->assertEquals('Protection disabled', (string) $requirements['configuration_files']['value']); 64 $description = strip_tags(\Drupal::service('renderer')->renderPlain($requirements['configuration_files']['description'])); 65 $this->assertStringContainsString('settings.php is not protected from modifications and poses a security risk.', $description); 66 $this->assertStringContainsString('services.yml is not protected from modifications and poses a security risk.', $description); 67 68 // Verify that site directory and the settings.php remain writable when 69 // automatically enforcing file permissions is disabled. 70 $this->assertDirectoryIsWritable($site_path); 71 $this->assertFileIsWritable($settings_file); 72 73 // Re-enable permissions enforcement. 74 $settings = Settings::getAll(); 75 $settings['skip_permissions_hardening'] = FALSE; 76 new Settings($settings); 77 78 // Manually trigger the requirements check. 79 $requirements = $this->checkSystemRequirements(); 80 $this->assertEquals('Protected', (string) $requirements['configuration_files']['value']); 81 82 // Verify that site directory and the settings.php remain protected when 83 // automatically enforcing file permissions is enabled. 84 $this->assertDirectoryNotIsWritable($site_path); 85 $this->assertFileNotIsWritable($settings_file); 86 } 87 88 /** 89 * Checks system runtime requirements. 90 * 91 * @return array 92 * An array of system requirements. 93 */ 94 protected function checkSystemRequirements() { 95 module_load_install('system'); 96 return system_requirements('runtime'); 97 } 98 99 /** 100 * Makes the given path and settings file writable. 101 * 102 * @param string $site_path 103 * The sites directory path, such as 'sites/default'. 104 */ 105 protected function makeWritable($site_path) { 106 chmod($site_path, 0755); 107 chmod($this->settingsFile($site_path), 0644); 108 } 109 110 /** 111 * Returns the path to settings.php 112 * 113 * @param string $site_path 114 * The sites subdirectory path. 115 * 116 * @return string 117 * The path to settings.php. 118 */ 119 protected function settingsFile($site_path) { 120 $settings_file = $site_path . '/settings.php'; 121 return $settings_file; 122 } 123 124} 125