5 * Contains \Drupal\security_review\Security.
8 namespace Drupal\security_review;
10 use Drupal\Core\Config\ConfigFactoryInterface;
11 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
12 use Drupal\Core\DrupalKernelInterface;
13 use Drupal\Core\Extension\ModuleHandlerInterface;
14 use Drupal\Core\Session\AccountInterface;
15 use Drupal\user\Entity\Role;
18 * Provides frequently used security-related data.
22 use DependencySerializationTrait;
27 * @var \Drupal\Core\Config\ConfigFactoryInterface
29 protected $configFactory;
34 * @var \Drupal\Core\DrupalKernelInterface
41 * @var \Drupal\Core\Extension\ModuleHandlerInterface
43 protected $moduleHandler;
46 * The security_review service.
48 * @var \Drupal\security_review\SecurityReview
50 protected $securityReview;
53 * Constructs a Security instance.
55 * @param \Drupal\security_review\SecurityReview $security_review
56 * The SecurityReview service.
57 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
59 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
61 * @param \Drupal\Core\DrupalKernelInterface $kernel
64 public function __construct(SecurityReview $security_review, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, DrupalKernelInterface $kernel) {
65 // Store the dependencies.
66 $this->securityReview = $security_review;
67 $this->moduleHandler = $module_handler;
68 $this->configFactory = $config_factory;
69 $this->kernel = $kernel;
73 * Returns the IDs of untrusted roles.
75 * If the module hasn't been configured yet, it returns the default untrusted
79 * Untrusted roles' IDs.
81 public function untrustedRoles() {
82 // If the module hasn't been manually configured yet, return the untrusted
83 // roles depending on Drupal's actual configuration.
84 if (!$this->securityReview->isConfigured()) {
85 return static::defaultUntrustedRoles();
88 // Else return the stored untrusted roles.
89 return $this->securityReview->getUntrustedRoles();
93 * Returns the default untrusted roles.
95 * The default untrusted roles are:
97 * Authenticated : if visitors are allowed to create accounts.
100 * Default untrusted roles' IDs.
102 public function defaultUntrustedRoles() {
103 // Add the Anonymous role to the output array.
104 $roles = [AccountInterface::ANONYMOUS_ROLE];
106 // Check whether visitors can create accounts.
107 $user_register = $this->configFactory->get('user.settings')
109 if ($user_register !== USER_REGISTER_ADMINISTRATORS_ONLY) {
110 // If visitors are allowed to create accounts they are considered
112 $roles[] = AccountInterface::AUTHENTICATED_ROLE;
115 // Return the untrusted roles.
120 * Returns the permission strings that a group of roles have.
122 * @param string[] $role_ids
123 * The array of roleIDs to check.
124 * @param bool $group_by_role_id
125 * Choose whether to group permissions by role ID.
128 * An array of the permissions untrusted roles have. If $groupByRoleId is
129 * true, the array key is the role ID, the value is the array of permissions
132 public function rolePermissions(array $role_ids, $group_by_role_id = FALSE) {
133 // Get the permissions the given roles have, grouped by roles.
134 $permissions_grouped = user_role_permissions($role_ids);
136 // Fill up the administrative roles' permissions too.
137 foreach ($role_ids as $role_id) {
138 $role = Role::load($role_id);
139 /** @var Role $role */
140 if ($role->isAdmin()) {
141 $permissions_grouped[$role_id] = $this->permissions();
145 if ($group_by_role_id) {
146 // If the result should be grouped, we have nothing else to do.
147 return $permissions_grouped;
150 // Merge the grouped permissions into $untrusted_permissions.
151 $untrusted_permissions = [];
152 foreach ($permissions_grouped as $permissions) {
153 $untrusted_permissions = array_merge($untrusted_permissions, $permissions);
156 // Remove duplicate elements and fix indexes.
157 $untrusted_permissions = array_values(array_unique($untrusted_permissions));
158 return $untrusted_permissions;
163 * Returns the permission strings that untrusted roles have.
165 * @param bool $group_by_role_id
166 * Choose whether to group permissions by role ID.
169 * An array of the permissions untrusted roles have. If $groupByRoleId is
170 * true, the array key is the role ID, the value is the array of permissions
173 public function untrustedPermissions($group_by_role_id = FALSE) {
174 return $this->rolePermissions($this->untrustedRoles(), $group_by_role_id);
178 * Returns the trusted roles.
181 * Trusted roles' IDs.
183 public function trustedRoles() {
184 // Get the stored/default untrusted roles.
185 $untrusted_roles = $this->untrustedRoles();
187 // Iterate through all the roles, and store which are not untrusted.
189 foreach (user_roles() as $role) {
190 if (!in_array($role->id(), $untrusted_roles)) {
191 $trusted[] = $role->id();
195 // Return the trusted roles.
200 * Returns the permission strings that trusted roles have.
202 * @param bool $group_by_role_id
203 * Choose whether to group permissions by role ID.
206 * An array of the permissions trusted roles have. If $groupByRoleId is
207 * true, the array key is the role ID, the value is the array of permissions
210 public function trustedPermissions($group_by_role_id = FALSE) {
211 return $this->rolePermissions($this->trustedRoles(), $group_by_role_id);
216 * Gets all the permissions.
219 * Whether to return only permission strings or metadata too.
221 * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
224 * Array of every permission.
226 public function permissions($meta = FALSE) {
227 // Not injected because of hard testability.
228 $permissions = \Drupal::service('user.permissions')->getPermissions();
231 return array_keys($permissions);
237 * Gets the list of unsafe HTML tags.
240 * List of unsafe tags.
242 public function unsafeTags() {
288 $this->moduleHandler->alter('security_review_unsafe_tags', $unsafe_tags);
294 * Gets the list of unsafe file extensions.
297 * List of unsafe extensions.
299 public function unsafeExtensions() {
316 ->alter('security_review_unsafe_extensions', $unsafe_ext);
322 * Returns the site path.
325 * Absolute site path.
327 public function sitePath() {
328 return DRUPAL_ROOT . '/' . $this->kernel->getSitePath();
332 * Finds files and directories that are writable by the web server.
334 * @param string[] $files
335 * The files to iterate through.
337 * Whether it is being invoked in CLI context.
340 * The files that are writable.
342 public function findWritableFiles(array $files, $cli = FALSE) {
346 foreach ($files as $file) {
347 if (is_writable($file)) {
353 // Get the web server's user data.
354 $uid = $this->securityReview->getServerUid();
355 $gids = $this->securityReview->getServerGids();
357 foreach ($files as $file) {
358 $perms = 0777 & fileperms($file);
359 // Check write permissions for others.
360 $ow = ($perms >> 1) & 1;
366 // Check write permissions for owner.
367 $uw = ($perms >> 7) & 1;
368 if ($uw === 1 && fileowner($file) == $uid) {
373 // Check write permissions for group.
374 $gw = ($perms >> 4) & 1;
375 if ($gw === 1 && in_array(filegroup($file), $gids)) {