mirror of
https://github.com/moodle/moodle.git
synced 2025-07-27 09:20:36 +02:00
308 lines
10 KiB
PHP
308 lines
10 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Check the presence of public paths via curl.
|
|
*
|
|
* @package core
|
|
* @category check
|
|
* @copyright 2020 Brendan Heywood <brendan@catalyst-au.net>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
namespace core\check\environment;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
use core\check\check;
|
|
use core\check\result;
|
|
|
|
/**
|
|
* Check the public access of various paths.
|
|
*
|
|
* @copyright 2020 Brendan Heywood <brendan@catalyst-au.net>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class publicpaths extends check {
|
|
|
|
/**
|
|
* Get the short check name
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_name(): string {
|
|
return get_string('check_publicpaths_name', 'report_security');
|
|
}
|
|
|
|
/**
|
|
* Returns a list of test urls and metadata.
|
|
*/
|
|
public function get_pathsets() {
|
|
global $CFG;
|
|
|
|
// The intention here is that each pattern is a simple regex such that
|
|
// in future perhaps the various webserver config could be generated as more
|
|
// pattens are added to these checks.
|
|
return [
|
|
[
|
|
'pattern' => '/vendor/',
|
|
'404' => [
|
|
'vendor/',
|
|
'vendor/bin/behat',
|
|
],
|
|
'details' => get_string('check_vendordir_details', 'report_security', ['path' => $CFG->dirroot.'/vendor']),
|
|
'summary' => get_string('check_vendordir_info', 'report_security'),
|
|
],
|
|
[
|
|
'pattern' => '/node_modules/',
|
|
'404' => [
|
|
'node_modules/',
|
|
'node_modules/cli/cli.js',
|
|
],
|
|
'summary' => get_string('check_nodemodules_info', 'report_security'),
|
|
'details' => get_string('check_nodemodules_details', 'report_security',
|
|
['path' => $CFG->dirroot . '/node_modules']),
|
|
],
|
|
[
|
|
'pattern' => '^\..*',
|
|
'404' => [
|
|
'.git/',
|
|
'.git/HEAD',
|
|
'.github/FUNDING.yml',
|
|
'.stylelintrc',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => 'composer.json',
|
|
'404' => [
|
|
'composer.json',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => '.lock',
|
|
'404' => [
|
|
'composer.lock',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => 'environment.xml',
|
|
'404' => [
|
|
'admin/environment.xml',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => '',
|
|
'404' => [
|
|
'doesnotexist', // Just to make sure that real 404s are still 404s.
|
|
],
|
|
'summary' => '',
|
|
],
|
|
[
|
|
'pattern' => '',
|
|
'404' => [
|
|
'lib/classes/',
|
|
],
|
|
'summary' => get_string('check_dirindex_info', 'report_security'),
|
|
],
|
|
[
|
|
'pattern' => 'db/install.xml',
|
|
'404' => [
|
|
'lib/db/install.xml',
|
|
'mod/assign/db/install.xml',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => 'readme.txt',
|
|
'404' => [
|
|
'lib/scssphp/readme_moodle.txt',
|
|
'mod/resource/readme.txt',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => 'README',
|
|
'404' => [
|
|
'mod/README.txt',
|
|
'mod/book/README.md',
|
|
'mod/chat/README.txt',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => '/upgrade.txt',
|
|
'404' => [
|
|
'auth/manual/upgrade.txt',
|
|
'lib/upgrade.txt',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => 'phpunit.xml',
|
|
'404' => ['phpunit.xml.dist'],
|
|
],
|
|
[
|
|
'pattern' => '/fixtures/',
|
|
'404' => [
|
|
'privacy/tests/fixtures/logo.png',
|
|
'enrol/lti/tests/fixtures/input.xml',
|
|
],
|
|
],
|
|
[
|
|
'pattern' => '/behat/',
|
|
'404' => ['blog/tests/behat/delete.feature'],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Return result
|
|
* @return result
|
|
*/
|
|
public function get_result(): result {
|
|
global $CFG, $OUTPUT;
|
|
|
|
$status = result::OK;
|
|
$details = '';
|
|
$summary = get_string('check_publicpaths_ok', 'report_security');
|
|
$errors = [];
|
|
|
|
$c = new \curl();
|
|
$paths = $this->get_pathsets();
|
|
|
|
$table = new \html_table();
|
|
$table->align = ['center', 'right', 'left'];
|
|
$table->size = ['1%', '1%', '1%', '1%', '1%', '99%'];
|
|
$table->head = [
|
|
get_string('status'),
|
|
get_string('checkexpected'),
|
|
get_string('checkactual'),
|
|
get_string('url'),
|
|
get_string('category'),
|
|
get_string('details'),
|
|
];
|
|
$table->attributes['class'] = 'flexible generaltable generalbox table-sm';
|
|
$table->data = [];
|
|
|
|
// Used to track duplicated errors.
|
|
$lastdetail = '-';
|
|
|
|
$curl = new \curl();
|
|
$requests = [];
|
|
|
|
// Build up a list of all url so we can load them in parallel.
|
|
foreach ($paths as $path) {
|
|
foreach (['200', '404'] as $expected) {
|
|
if (!isset($path[$expected])) {
|
|
continue;
|
|
}
|
|
foreach ($path[$expected] as $test) {
|
|
$requests[] = [
|
|
'nobody' => true,
|
|
'header' => 1,
|
|
'url' => $CFG->wwwroot . '/' . $test,
|
|
'returntransfer' => true,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
$headers = $curl->download($requests);
|
|
|
|
foreach ($paths as $path) {
|
|
foreach (['200', '404'] as $expected) {
|
|
if (!isset($path[$expected])) {
|
|
continue;
|
|
}
|
|
foreach ($path[$expected] as $test) {
|
|
$rowsummary = '';
|
|
$rowdetail = '';
|
|
|
|
$url = $CFG->wwwroot . '/' . $test;
|
|
|
|
// Parse the HTTP header to get the 200 / 404 code.
|
|
$header = array_shift($headers);
|
|
$actual = strtok($header, "\n");
|
|
$actual = strtok($actual, " ");
|
|
$actual = strtok(" ");
|
|
|
|
if ($actual != $expected) {
|
|
if (isset($path['summary'])) {
|
|
$rowsummary = $path['summary'];
|
|
} else {
|
|
$rowsummary = get_string('check_publicpaths_generic',
|
|
'report_security', $path['pattern']);
|
|
}
|
|
|
|
// Special case where a 404 is ideal but a 403 is ok too.
|
|
if ($actual == 403) {
|
|
$result = new result(result::INFO, '', '');
|
|
$rowsummary .= get_string('check_publicpaths_403', 'report_security');
|
|
} else {
|
|
$result = new result(result::ERROR, '', '');
|
|
$status = result::ERROR;
|
|
$summary = get_string('check_publicpaths_warning', 'report_security');
|
|
}
|
|
|
|
$rowdetail = isset($path['details']) ? $path['details'] : $rowsummary;
|
|
|
|
if (empty($errors[$path['pattern']])) {
|
|
$summary .= '<li>' . $rowsummary . '</li>';
|
|
$errors[$path['pattern']] = 1;
|
|
}
|
|
|
|
} else {
|
|
$result = new result(result::OK, '', '');
|
|
}
|
|
|
|
$table->data[] = [
|
|
$OUTPUT->check_result($result),
|
|
$expected,
|
|
$actual,
|
|
$OUTPUT->action_link($url, $test, null, ['target' => '_blank']),
|
|
"<pre>{$path['pattern']}</pre>",
|
|
];
|
|
|
|
// Merge duplicate details to display a nicer table.
|
|
if ($rowdetail == $lastdetail) {
|
|
$duplicates++;
|
|
} else {
|
|
$duplicates = 1;
|
|
}
|
|
$detailcell = new \html_table_cell($rowdetail);
|
|
$detailcell->rowspan = $duplicates;
|
|
$rows = count($table->data);
|
|
$table->data[$rows - $duplicates][5] = $detailcell;
|
|
$lastdetail = $rowdetail;
|
|
}
|
|
}
|
|
}
|
|
|
|
$details .= \html_writer::table($table);
|
|
|
|
return new result($status, $summary, $details);
|
|
}
|
|
|
|
/**
|
|
* Link to the dev docs for more info.
|
|
*
|
|
* @return \action_link|null
|
|
*/
|
|
public function get_action_link(): ?\action_link {
|
|
return new \action_link(
|
|
new \moodle_url(\get_docs_url('Installing_Moodle#Set_up_your_server')),
|
|
get_string('moodledocs'));
|
|
}
|
|
|
|
}
|
|
|