More updates to stop using dev or alpha or beta versions.
[yaffs-website] / web / modules / contrib / security_review / src / Security.php
1 <?php
2
3 namespace Drupal\security_review;
4
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;
11
12 /**
13  * Provides frequently used security-related data.
14  */
15 class Security {
16
17   use DependencySerializationTrait;
18
19   /**
20    * The config factory.
21    *
22    * @var \Drupal\Core\Config\ConfigFactoryInterface
23    */
24   protected $configFactory;
25
26   /**
27    * The Drupal kernel.
28    *
29    * @var \Drupal\Core\DrupalKernelInterface
30    */
31   protected $kernel;
32
33   /**
34    * The module handler.
35    *
36    * @var \Drupal\Core\Extension\ModuleHandlerInterface
37    */
38   protected $moduleHandler;
39
40   /**
41    * The security_review service.
42    *
43    * @var \Drupal\security_review\SecurityReview
44    */
45   protected $securityReview;
46
47   /**
48    * Constructs a Security instance.
49    *
50    * @param \Drupal\security_review\SecurityReview $security_review
51    *   The SecurityReview service.
52    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
53    *   The module handler.
54    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
55    *   The config factory.
56    * @param \Drupal\Core\DrupalKernelInterface $kernel
57    *   The Drupal kernel.
58    */
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;
65   }
66
67   /**
68    * Returns the IDs of untrusted roles.
69    *
70    * If the module hasn't been configured yet, it returns the default untrusted
71    * roles.
72    *
73    * @return string[]
74    *   Untrusted roles' IDs.
75    */
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();
81     }
82
83     // Else return the stored untrusted roles.
84     return $this->securityReview->getUntrustedRoles();
85   }
86
87   /**
88    * Returns the default untrusted roles.
89    *
90    * The default untrusted roles are:
91    *   Anonymous      : always
92    *   Authenticated  : if visitors are allowed to create accounts.
93    *
94    * @return string[]
95    *   Default untrusted roles' IDs.
96    */
97   public function defaultUntrustedRoles() {
98     // Add the Anonymous role to the output array.
99     $roles = [AccountInterface::ANONYMOUS_ROLE];
100
101     // Check whether visitors can create accounts.
102     $user_register = $this->configFactory->get('user.settings')
103       ->get('register');
104     if ($user_register !== USER_REGISTER_ADMINISTRATORS_ONLY) {
105       // If visitors are allowed to create accounts they are considered
106       // untrusted.
107       $roles[] = AccountInterface::AUTHENTICATED_ROLE;
108     }
109
110     // Return the untrusted roles.
111     return $roles;
112   }
113
114   /**
115    * Returns the permission strings that a group of roles have.
116    *
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.
121    *
122    * @return array
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
125    *   the role has.
126    */
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);
130
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();
137       }
138     }
139
140     if ($group_by_role_id) {
141       // If the result should be grouped, we have nothing else to do.
142       return $permissions_grouped;
143     }
144     else {
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);
149       }
150
151       // Remove duplicate elements and fix indexes.
152       $untrusted_permissions = array_values(array_unique($untrusted_permissions));
153       return $untrusted_permissions;
154     }
155   }
156
157   /**
158    * Returns the permission strings that untrusted roles have.
159    *
160    * @param bool $group_by_role_id
161    *   Choose whether to group permissions by role ID.
162    *
163    * @return array
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
166    *   the role has.
167    */
168   public function untrustedPermissions($group_by_role_id = FALSE) {
169     return $this->rolePermissions($this->untrustedRoles(), $group_by_role_id);
170   }
171
172   /**
173    * Returns the trusted roles.
174    *
175    * @return array
176    *   Trusted roles' IDs.
177    */
178   public function trustedRoles() {
179     // Get the stored/default untrusted roles.
180     $untrusted_roles = $this->untrustedRoles();
181
182     // Iterate through all the roles, and store which are not untrusted.
183     $trusted = [];
184     foreach (user_roles() as $role) {
185       if (!in_array($role->id(), $untrusted_roles)) {
186         $trusted[] = $role->id();
187       }
188     }
189
190     // Return the trusted roles.
191     return $trusted;
192   }
193
194   /**
195    * Returns the permission strings that trusted roles have.
196    *
197    * @param bool $group_by_role_id
198    *   Choose whether to group permissions by role ID.
199    *
200    * @return array
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
203    *   the role has.
204    */
205   public function trustedPermissions($group_by_role_id = FALSE) {
206     return $this->rolePermissions($this->trustedRoles(), $group_by_role_id);
207   }
208
209
210   /**
211    * Gets all the permissions.
212    *
213    * @param bool $meta
214    *   Whether to return only permission strings or metadata too.
215    *
216    * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
217    *
218    * @return array
219    *   Array of every permission.
220    */
221   public function permissions($meta = FALSE) {
222     // Not injected because of hard testability.
223     $permissions = \Drupal::service('user.permissions')->getPermissions();
224
225     if (!$meta) {
226       return array_keys($permissions);
227     }
228     return $permissions;
229   }
230
231   /**
232    * Gets the list of unsafe HTML tags.
233    *
234    * @return string[]
235    *   List of unsafe tags.
236    */
237   public function unsafeTags() {
238     $unsafe_tags = [
239       'applet',
240       'area',
241       'audio',
242       'base',
243       'basefont',
244       'body',
245       'button',
246       'comment',
247       'embed',
248       'eval',
249       'form',
250       'frame',
251       'frameset',
252       'head',
253       'html',
254       'iframe',
255       'image',
256       'img',
257       'input',
258       'isindex',
259       'label',
260       'link',
261       'map',
262       'math',
263       'meta',
264       'noframes',
265       'noscript',
266       'object',
267       'optgroup',
268       'option',
269       'param',
270       'script',
271       'select',
272       'style',
273       'svg',
274       'table',
275       'td',
276       'textarea',
277       'title',
278       'video',
279       'vmlframe',
280     ];
281
282     // Alter data.
283     $this->moduleHandler->alter('security_review_unsafe_tags', $unsafe_tags);
284
285     return $unsafe_tags;
286   }
287
288   /**
289    * Gets the list of unsafe file extensions.
290    *
291    * @return string[]
292    *   List of unsafe extensions.
293    */
294   public function unsafeExtensions() {
295     $unsafe_ext = [
296       'swf',
297       'exe',
298       'html',
299       'htm',
300       'php',
301       'phtml',
302       'py',
303       'js',
304       'vb',
305       'vbe',
306       'vbs',
307     ];
308
309     // Alter data.
310     $this->moduleHandler
311       ->alter('security_review_unsafe_extensions', $unsafe_ext);
312
313     return $unsafe_ext;
314   }
315
316   /**
317    * Returns the site path.
318    *
319    * @return string
320    *   Absolute site path.
321    */
322   public function sitePath() {
323     return DRUPAL_ROOT . '/' . $this->kernel->getSitePath();
324   }
325
326   /**
327    * Finds files and directories that are writable by the web server.
328    *
329    * @param string[] $files
330    *   The files to iterate through.
331    * @param bool $cli
332    *   Whether it is being invoked in CLI context.
333    *
334    * @return string[]
335    *   The files that are writable.
336    */
337   public function findWritableFiles(array $files, $cli = FALSE) {
338     $writable = [];
339     if (!$cli) {
340       // Running from UI.
341       foreach ($files as $file) {
342         if (is_writable($file)) {
343           $writable[] = $file;
344         }
345       }
346     }
347     else {
348       // Get the web server's user data.
349       $uid = $this->securityReview->getServerUid();
350       $gids = $this->securityReview->getServerGids();
351
352       foreach ($files as $file) {
353         $perms = 0777 & fileperms($file);
354         // Check write permissions for others.
355         $ow = ($perms >> 1) & 1;
356         if ($ow === 1) {
357           $writable[] = $file;
358           continue;
359         }
360
361         // Check write permissions for owner.
362         $uw = ($perms >> 7) & 1;
363         if ($uw === 1 && fileowner($file) == $uid) {
364           $writable[] = $file;
365           continue;
366         }
367
368         // Check write permissions for group.
369         $gw = ($perms >> 4) & 1;
370         if ($gw === 1 && in_array(filegroup($file), $gids)) {
371           $writable[] = $file;
372         }
373       }
374     }
375     return $writable;
376   }
377
378 }