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