Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Check the presence of public paths via curl.
19
 *
20
 * @package    core
21
 * @category   check
22
 * @copyright  2020 Brendan Heywood <brendan@catalyst-au.net>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace core\check\environment;
27
 
28
defined('MOODLE_INTERNAL') || die();
29
 
30
use core\check\check;
31
use core\check\result;
32
 
33
/**
34
 * Check the public access of various paths.
35
 *
36
 * @copyright  2020 Brendan Heywood <brendan@catalyst-au.net>
37
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
class publicpaths extends check {
40
 
41
    /**
42
     * Get the short check name
43
     *
44
     * @return string
45
     */
46
    public function get_name(): string {
47
        return get_string('check_publicpaths_name', 'report_security');
48
    }
49
 
50
    /**
51
     * Returns a list of test urls and metadata.
52
     */
53
    public function get_pathsets() {
54
        global $CFG;
55
 
56
        // The intention here is that each pattern is a simple regex such that
57
        // in future perhaps the various webserver config could be generated as more
58
        // pattens are added to these checks.
59
        return [
60
            [
61
                'pattern'   => '/vendor/',
62
                '404'       => [
63
                    'vendor/',
64
                    'vendor/bin/behat',
65
                ],
66
                'details'   => get_string('check_vendordir_details', 'report_security', ['path' => $CFG->dirroot.'/vendor']),
67
                'summary'   => get_string('check_vendordir_info', 'report_security'),
68
            ],
69
            [
70
                'pattern'   => '/node_modules/',
71
                '404'       => [
72
                    'node_modules/',
73
                    'node_modules/cli/cli.js',
74
                ],
75
                'summary'   => get_string('check_nodemodules_info', 'report_security'),
76
                'details'   => get_string('check_nodemodules_details', 'report_security',
77
                        ['path' => $CFG->dirroot . '/node_modules']),
78
            ],
79
            [
80
                'pattern'   => '^\..*',
81
                '404'       => [
82
                    '.git/',
83
                    '.git/HEAD',
84
                    '.github/FUNDING.yml',
85
                    '.stylelintrc',
86
                ],
87
            ],
88
            [
89
                'pattern'   => 'composer.json',
90
                '404'       => [
91
                    'composer.json',
92
                ],
93
            ],
94
            [
95
                'pattern'   => '.lock',
96
                '404'       => [
97
                    'composer.lock',
98
                ],
99
            ],
100
            [
101
                'pattern'   => 'environment.xml',
102
                '404'       => [
103
                    'admin/environment.xml',
104
                ],
105
            ],
106
            [
107
                'pattern'   => '',
108
                '404'       => [
109
                    'doesnotexist', // Just to make sure that real 404s are still 404s.
110
                ],
111
                'summary'   => '',
112
            ],
113
            [
114
                'pattern'   => '',
115
                '404'       => [
116
                    'lib/classes/',
117
                ],
118
                'summary'   => get_string('check_dirindex_info', 'report_security'),
119
            ],
120
            [
121
                'pattern'   => 'db/install.xml',
122
                '404'       => [
123
                    'lib/db/install.xml',
124
                    'mod/assign/db/install.xml',
125
                ],
126
            ],
127
            [
128
                'pattern'   => 'readme.txt',
129
                '404'       => [
130
                    'lib/scssphp/readme_moodle.txt',
131
                    'mod/resource/readme.txt',
132
                ],
133
            ],
134
            [
135
                'pattern'   => 'README',
136
                '404'       => [
137
                    'mod/README.txt',
138
                    'mod/book/README.md',
139
                    'mod/chat/README.txt',
140
                ],
141
            ],
142
            [
143
                'pattern'   => '/upgrade.txt',
144
                '404'       => [
145
                    'auth/manual/upgrade.txt',
146
                    'lib/upgrade.txt',
147
                ],
148
            ],
149
            [
150
                'pattern'   => 'phpunit.xml',
151
                '404'       => ['phpunit.xml.dist'],
152
            ],
153
            [
154
                'pattern'   => '/fixtures/',
155
                '404'       => [
156
                    'privacy/tests/fixtures/logo.png',
157
                    'enrol/lti/tests/fixtures/input.xml',
158
                ],
159
            ],
160
            [
161
                'pattern'   => '/behat/',
162
                '404'       => ['blog/tests/behat/delete.feature'],
163
            ],
164
        ];
165
    }
166
 
167
    /**
168
     * Return result
169
     * @return result
170
     */
171
    public function get_result(): result {
172
        global $CFG, $OUTPUT;
173
 
174
        $status = result::OK;
175
        $details = '';
176
        $summary = get_string('check_publicpaths_ok', 'report_security');
177
        $errors = [];
178
 
179
        $c = new \curl();
180
        $paths = $this->get_pathsets();
181
 
182
        $table = new \html_table();
183
        $table->align = ['center', 'right', 'left'];
184
        $table->size = ['1%', '1%', '1%', '1%', '1%', '99%'];
185
        $table->head = [
186
            get_string('status'),
187
            get_string('checkexpected'),
188
            get_string('checkactual'),
189
            get_string('url'),
190
            get_string('category'),
191
            get_string('details'),
192
        ];
193
        $table->attributes['class'] = 'flexible generaltable generalbox table-sm';
194
        $table->data = [];
195
 
196
        // Used to track duplicated errors.
197
        $lastdetail = '-';
198
 
199
        $curl = new \curl();
200
        $requests = [];
201
 
202
        // Build up a list of all url so we can load them in parallel.
203
        foreach ($paths as $path) {
204
            foreach (['200', '404'] as $expected) {
205
                if (!isset($path[$expected])) {
206
                    continue;
207
                }
208
                foreach ($path[$expected] as $test) {
209
                    $requests[] = [
210
                        'nobody'    => true,
211
                        'header'    => 1,
212
                        'url'       => $CFG->wwwroot . '/' . $test,
213
                        'returntransfer' => true,
214
                    ];
215
                }
216
            }
217
        }
218
 
219
        $headers = $curl->download($requests);
220
 
221
        foreach ($paths as $path) {
222
            foreach (['200', '404'] as $expected) {
223
                if (!isset($path[$expected])) {
224
                    continue;
225
                }
226
                foreach ($path[$expected] as $test) {
227
                    $rowsummary = '';
228
                    $rowdetail = '';
229
 
230
                    $url = $CFG->wwwroot . '/' . $test;
231
 
232
                    // Parse the HTTP header to get the 200 / 404 code.
233
                    $header = array_shift($headers);
234
                    $actual = strtok($header, "\n");
235
                    $actual = strtok($actual, " ");
236
                    $actual = strtok(" ");
237
 
238
                    if ($actual != $expected) {
239
                        if (isset($path['summary'])) {
240
                            $rowsummary = $path['summary'];
241
                        } else {
242
                            $rowsummary = get_string('check_publicpaths_generic',
243
                                'report_security', $path['pattern']);
244
                        }
245
 
246
                        // Special case where a 404 is ideal but a 403 is ok too.
247
                        if ($actual == 403) {
248
                            $result = new result(result::INFO, '', '');
249
                            $rowsummary .= get_string('check_publicpaths_403', 'report_security');
250
                        } else {
251
                            $result = new result(result::ERROR, '', '');
252
                            $status = result::ERROR;
253
                            $summary = get_string('check_publicpaths_warning', 'report_security');
254
                        }
255
 
256
                        $rowdetail = isset($path['details']) ? $path['details'] : $rowsummary;
257
 
258
                        if (empty($errors[$path['pattern']])) {
259
                            $summary .= '<li>' . $rowsummary . '</li>';
260
                            $errors[$path['pattern']] = 1;
261
                        }
262
 
263
                    } else {
264
                        $result = new result(result::OK, '', '');
265
                    }
266
 
267
                    $table->data[] = [
268
                        $OUTPUT->check_result($result),
269
                        $expected,
270
                        $actual,
271
                        $OUTPUT->action_link($url, $test, null, ['target' => '_blank']),
272
                        "<pre>{$path['pattern']}</pre>",
273
                    ];
274
 
275
                    // Merge duplicate details to display a nicer table.
276
                    if ($rowdetail == $lastdetail) {
277
                        $duplicates++;
278
                    } else {
279
                        $duplicates = 1;
280
                    }
281
                    $detailcell = new \html_table_cell($rowdetail);
282
                    $detailcell->rowspan = $duplicates;
283
                    $rows = count($table->data);
284
                    $table->data[$rows - $duplicates][5] = $detailcell;
285
                    $lastdetail = $rowdetail;
286
                }
287
            }
288
        }
289
 
290
        $details .= \html_writer::table($table);
291
 
292
        return new result($status, $summary, $details);
293
    }
294
 
295
    /**
296
     * Link to the dev docs for more info.
297
     *
298
     * @return \action_link|null
299
     */
300
    public function get_action_link(): ?\action_link {
301
        return new \action_link(
302
            new \moodle_url(\get_docs_url('Installing_Moodle#Set_up_your_server')),
303
            get_string('moodledocs'));
304
    }
305
 
306
}
307