Including security review as a submodule - with patched for Yaffs.
[yaffs-website] / web / modules / contrib / security_review / src / Checks / UploadExtensions.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\security_review\Checks\UploadExtensions.
6  */
7
8 namespace Drupal\security_review\Checks;
9
10 use Drupal\Core\Entity\Entity;
11 use Drupal\Core\Url;
12 use Drupal\field\Entity\FieldConfig;
13 use Drupal\security_review\Check;
14 use Drupal\security_review\CheckResult;
15 use Symfony\Component\Routing\Exception\RouteNotFoundException;
16
17 /**
18  * Checks for unsafe extensions in the allowed extensions settings of fields.
19  */
20 class UploadExtensions extends Check {
21
22   /**
23    * {@inheritdoc}
24    */
25   public function getNamespace() {
26     return 'Security Review';
27   }
28
29   /**
30    * {@inheritdoc}
31    */
32   public function getTitle() {
33     return 'Allowed upload extensions';
34   }
35
36   /**
37    * {@inheritdoc}
38    */
39   public function getMachineTitle() {
40     return 'upload_extensions';
41   }
42
43   /**
44    * {@inheritdoc}
45    */
46   public function run() {
47     // If field is not enabled return with INFO.
48     if (!$this->moduleHandler()->moduleExists('field')) {
49       return $this->createResult(CheckResult::INFO);
50     }
51
52     $result = CheckResult::SUCCESS;
53     $findings = [];
54
55     // Check field configuration entities.
56     foreach (FieldConfig::loadMultiple() as $entity) {
57       /** @var FieldConfig $entity */
58       $extensions = $entity->getSetting('file_extensions');
59       if ($extensions != NULL) {
60         $extensions = explode(' ', $extensions);
61         $intersect = array_intersect($extensions, $this->security()->unsafeExtensions());
62         // $intersect holds the unsafe extensions this entity allows.
63         foreach ($intersect as $unsafe_extension) {
64           $findings[$entity->id()][] = $unsafe_extension;
65         }
66       }
67     }
68
69     if (!empty($findings)) {
70       $result = CheckResult::FAIL;
71     }
72
73     return $this->createResult($result, $findings);
74   }
75
76   /**
77    * {@inheritdoc}
78    */
79   public function help() {
80     $paragraphs = [];
81     $paragraphs[] = $this->t(
82       'File and image fields allow for uploaded files. Some extensions are considered dangerous because the files can be evaluated and then executed in the browser. A malicious user could use this opening to gain control of your site. Review <a href=":url">all fields on your site</a>.',
83       [':url' => Url::fromRoute('entity.field_storage_config.collection')->toString()]
84     );
85
86     return [
87       '#theme' => 'check_help',
88       '#title' => 'Allowed upload extensions',
89       '#paragraphs' => $paragraphs,
90     ];
91   }
92
93   /**
94    * {@inheritdoc}
95    */
96   public function evaluate(CheckResult $result) {
97     $findings = $result->findings();
98     if (empty($findings)) {
99       return [];
100     }
101
102     $paragraphs = [];
103     $paragraphs[] = $this->t('The following extensions are considered unsafe and should be removed or limited from use. Or, be sure you are not granting untrusted users the ability to upload files.');
104
105     $items = [];
106     foreach ($findings as $entity_id => $unsafe_extensions) {
107       $entity = FieldConfig::load($entity_id);
108       /** @var FieldConfig $entity */
109
110       foreach ($unsafe_extensions as $extension) {
111         $item = $this->t(
112           'Review @type in <em>@field</em> field on @bundle',
113           [
114             '@type' => $extension,
115             '@field' => $entity->label(),
116             '@bundle' => $entity->getTargetBundle(),
117           ]
118         );
119
120         // Try to get an edit url.
121         try {
122           $url_params = ['field_config' => $entity->id()];
123           if ($entity->getTargetEntityTypeId() == 'node') {
124             $url_params['node_type'] = $entity->getTargetBundle();
125           }
126           $url = Url::fromRoute(
127             'entity.field_config.' . $entity->getTargetEntityTypeId() . '_field_edit_form',
128             $url_params
129           );
130           $items[] = $this->l($item, $url);
131         }
132         catch (RouteNotFoundException $e) {
133           $items[] = $item;
134         }
135       }
136     }
137
138     return [
139       '#theme' => 'check_evaluation',
140       '#paragraphs' => $paragraphs,
141       '#items' => $items,
142     ];
143   }
144
145   /**
146    * {@inheritdoc}
147    */
148   public function evaluatePlain(CheckResult $result) {
149     $findings = $result->findings();
150     if (empty($findings)) {
151       return '';
152     }
153
154     $output = '';
155     foreach ($findings as $entity_id => $unsafe_extensions) {
156       $entity = FieldConfig::load($entity_id);
157       /** @var FieldConfig $entity */
158
159       $output .= $this->t(
160         '@bundle: field @field',
161         [
162           '@bundle' => $entity->getTargetBundle(),
163           '@field' => $entity->label(),
164         ]
165       );
166       $output .= "\n\t" . implode(', ', $unsafe_extensions) . "\n";
167     }
168
169     return $output;
170   }
171
172   /**
173    * {@inheritdoc}
174    */
175   public function getMessage($result_const) {
176     switch ($result_const) {
177       case CheckResult::SUCCESS:
178         return $this->t('Only safe extensions are allowed for uploaded files and images.');
179
180       case CheckResult::FAIL:
181         return $this->t('Unsafe file extensions are allowed in uploads.');
182
183       case CheckResult::INFO:
184         return $this->t('Module field is not enabled.');
185
186       default:
187         return $this->t('Unexpected result.');
188     }
189   }
190
191 }