3 namespace Drupal\security_review;
5 use Drupal\Core\Config\ConfigFactoryInterface;
6 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
7 use Drupal\Core\DrupalKernelInterface;
8 use Drupal\Core\Extension\ModuleHandlerInterface;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\user\Entity\Role;
13 * Provides frequently used security-related data.
17 use DependencySerializationTrait;
22 * @var \Drupal\Core\Config\ConfigFactoryInterface
24 protected $configFactory;
29 * @var \Drupal\Core\DrupalKernelInterface
36 * @var \Drupal\Core\Extension\ModuleHandlerInterface
38 protected $moduleHandler;
41 * The security_review service.
43 * @var \Drupal\security_review\SecurityReview
45 protected $securityReview;
48 * Constructs a Security instance.
50 * @param \Drupal\security_review\SecurityReview $security_review
51 * The SecurityReview service.
52 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
54 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
56 * @param \Drupal\Core\DrupalKernelInterface $kernel
59 public function __construct(SecurityReview $security_review, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, DrupalKernelInterface $kernel) {
60 // Store the dependencies.
61 $this->securityReview = $security_review;
62 $this->moduleHandler = $module_handler;
63 $this->configFactory = $config_factory;
64 $this->kernel = $kernel;
68 * Returns the IDs of untrusted roles.
70 * If the module hasn't been configured yet, it returns the default untrusted
74 * Untrusted roles' IDs.
76 public function untrustedRoles() {
77 // If the module hasn't been manually configured yet, return the untrusted
78 // roles depending on Drupal's actual configuration.
79 if (!$this->securityReview->isConfigured()) {
80 return static::defaultUntrustedRoles();
83 // Else return the stored untrusted roles.
84 return $this->securityReview->getUntrustedRoles();
88 * Returns the default untrusted roles.
90 * The default untrusted roles are:
92 * Authenticated : if visitors are allowed to create accounts.
95 * Default untrusted roles' IDs.
97 public function defaultUntrustedRoles() {
98 // Add the Anonymous role to the output array.
99 $roles = [AccountInterface::ANONYMOUS_ROLE];
101 // Check whether visitors can create accounts.
102 $user_register = $this->configFactory->get('user.settings')
104 if ($user_register !== USER_REGISTER_ADMINISTRATORS_ONLY) {
105 // If visitors are allowed to create accounts they are considered
107 $roles[] = AccountInterface::AUTHENTICATED_ROLE;
110 // Return the untrusted roles.
115 * Returns the permission strings that a group of roles have.
117 * @param string[] $role_ids
118 * The array of roleIDs to check.
119 * @param bool $group_by_role_id
120 * Choose whether to group permissions by role ID.
123 * An array of the permissions untrusted roles have. If $groupByRoleId is
124 * true, the array key is the role ID, the value is the array of permissions
127 public function rolePermissions(array $role_ids, $group_by_role_id = FALSE) {
128 // Get the permissions the given roles have, grouped by roles.
129 $permissions_grouped = user_role_permissions($role_ids);
131 // Fill up the administrative roles' permissions too.
132 foreach ($role_ids as $role_id) {
133 $role = Role::load($role_id);
134 /** @var Role $role */
135 if ($role->isAdmin()) {
136 $permissions_grouped[$role_id] = $this->permissions();
140 if ($group_by_role_id) {
141 // If the result should be grouped, we have nothing else to do.
142 return $permissions_grouped;
145 // Merge the grouped permissions into $untrusted_permissions.
146 $untrusted_permissions = [];
147 foreach ($permissions_grouped as $permissions) {
148 $untrusted_permissions = array_merge($untrusted_permissions, $permissions);
151 // Remove duplicate elements and fix indexes.
152 $untrusted_permissions = array_values(array_unique($untrusted_permissions));
153 return $untrusted_permissions;
158 * Returns the permission strings that untrusted roles have.
160 * @param bool $group_by_role_id
161 * Choose whether to group permissions by role ID.
164 * An array of the permissions untrusted roles have. If $groupByRoleId is
165 * true, the array key is the role ID, the value is the array of permissions
168 public function untrustedPermissions($group_by_role_id = FALSE) {
169 return $this->rolePermissions($this->untrustedRoles(), $group_by_role_id);
173 * Returns the trusted roles.
176 * Trusted roles' IDs.
178 public function trustedRoles() {
179 // Get the stored/default untrusted roles.
180 $untrusted_roles = $this->untrustedRoles();
182 // Iterate through all the roles, and store which are not untrusted.
184 foreach (user_roles() as $role) {
185 if (!in_array($role->id(), $untrusted_roles)) {
186 $trusted[] = $role->id();
190 // Return the trusted roles.
195 * Returns the permission strings that trusted roles have.
197 * @param bool $group_by_role_id
198 * Choose whether to group permissions by role ID.
201 * An array of the permissions trusted roles have. If $groupByRoleId is
202 * true, the array key is the role ID, the value is the array of permissions
205 public function trustedPermissions($group_by_role_id = FALSE) {
206 return $this->rolePermissions($this->trustedRoles(), $group_by_role_id);
211 * Gets all the permissions.
214 * Whether to return only permission strings or metadata too.
216 * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
219 * Array of every permission.
221 public function permissions($meta = FALSE) {
222 // Not injected because of hard testability.
223 $permissions = \Drupal::service('user.permissions')->getPermissions();
226 return array_keys($permissions);
232 * Gets the list of unsafe HTML tags.
235 * List of unsafe tags.
237 public function unsafeTags() {
283 $this->moduleHandler->alter('security_review_unsafe_tags', $unsafe_tags);
289 * Gets the list of unsafe file extensions.
292 * List of unsafe extensions.
294 public function unsafeExtensions() {
311 ->alter('security_review_unsafe_extensions', $unsafe_ext);
317 * Returns the site path.
320 * Absolute site path.
322 public function sitePath() {
323 return DRUPAL_ROOT . '/' . $this->kernel->getSitePath();
327 * Finds files and directories that are writable by the web server.
329 * @param string[] $files
330 * The files to iterate through.
332 * Whether it is being invoked in CLI context.
335 * The files that are writable.
337 public function findWritableFiles(array $files, $cli = FALSE) {
341 foreach ($files as $file) {
342 if (is_writable($file)) {
348 // Get the web server's user data.
349 $uid = $this->securityReview->getServerUid();
350 $gids = $this->securityReview->getServerGids();
352 foreach ($files as $file) {
353 $perms = 0777 & fileperms($file);
354 // Check write permissions for others.
355 $ow = ($perms >> 1) & 1;
361 // Check write permissions for owner.
362 $uw = ($perms >> 7) & 1;
363 if ($uw === 1 && fileowner($file) == $uid) {
368 // Check write permissions for group.
369 $gw = ($perms >> 4) & 1;
370 if ($gw === 1 && in_array(filegroup($file), $gids)) {