mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
MDL-69333 report_security: Add checks for many public & private urls
This commit is contained in:
parent
5922222778
commit
a6e9ac9af7
@ -1013,6 +1013,8 @@ $string['changedpassword'] = 'Changed password';
|
||||
$string['changepassword'] = 'Change password';
|
||||
$string['changessaved'] = 'Changes saved';
|
||||
$string['check'] = 'Check';
|
||||
$string['checkactual'] = 'Actual';
|
||||
$string['checkexpected'] = 'Expected';
|
||||
$string['checks'] = 'Checks';
|
||||
$string['checksok'] = 'All \'{$a}\' checks OK';
|
||||
$string['checkall'] = 'Check all';
|
||||
|
@ -1,69 +0,0 @@
|
||||
<?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 the node_modules directory.
|
||||
*
|
||||
* @package core
|
||||
* @category check
|
||||
* @copyright 2020 Brendan Heywood <brendan@catalyst-au.net>
|
||||
* @copyright 2008 petr Skoda
|
||||
* @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 presence of the node_modules directory.
|
||||
*
|
||||
* @copyright 2020 Brendan Heywood <brendan@catalyst-au.net>
|
||||
* @copyright 2008 petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class nodemodules extends check {
|
||||
|
||||
/**
|
||||
* Get the short check name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return get_string('check_nodemodules_name', 'report_security');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return result
|
||||
* @return result
|
||||
*/
|
||||
public function get_result(): result {
|
||||
global $CFG;
|
||||
$summary = get_string('check_nodemodules_info', 'report_security');
|
||||
$details = get_string('check_nodemodules_details', 'report_security', ['path' => $CFG->dirroot . '/node_modules']);
|
||||
|
||||
if (is_dir($CFG->dirroot . '/node_modules')) {
|
||||
$status = result::WARNING;
|
||||
} else {
|
||||
$status = result::OK;
|
||||
}
|
||||
return new result($status, $summary, $details);
|
||||
}
|
||||
}
|
||||
|
306
lib/classes/check/environment/publicpaths.php
Normal file
306
lib/classes/check/environment/publicpaths.php
Normal file
@ -0,0 +1,306 @@
|
||||
<?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/moodle_readme.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 = '';
|
||||
$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;
|
||||
}
|
||||
|
||||
$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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
<?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 the vendor directory.
|
||||
*
|
||||
* @package core
|
||||
* @category check
|
||||
* @copyright 2020 Brendan Heywood <brendan@catalyst-au.net>
|
||||
* @copyright 2008 petr Skoda
|
||||
* @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 presence of the vendor directory.
|
||||
*
|
||||
* @copyright 2020 Brendan Heywood <brendan@catalyst-au.net>
|
||||
* @copyright 2008 petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class vendordir extends check {
|
||||
|
||||
/**
|
||||
* Get the short check name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return get_string('check_vendordir_name', 'report_security');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return result
|
||||
* @return result
|
||||
*/
|
||||
public function get_result(): result {
|
||||
global $CFG;
|
||||
$details = get_string('check_vendordir_details', 'report_security', ['path' => $CFG->dirroot.'/vendor']);
|
||||
$summary = get_string('check_vendordir_info', 'report_security');
|
||||
|
||||
if (is_dir($CFG->dirroot.'/vendor')) {
|
||||
$status = result::WARNING;
|
||||
} else {
|
||||
$status = result::OK;
|
||||
}
|
||||
return new result($status, $summary, $details);
|
||||
}
|
||||
}
|
||||
|
@ -121,8 +121,7 @@ class manager {
|
||||
$checks = [
|
||||
new environment\displayerrors(),
|
||||
new environment\unsecuredataroot(),
|
||||
new environment\vendordir(),
|
||||
new environment\nodemodules(),
|
||||
new environment\publicpaths(),
|
||||
new environment\configrw(),
|
||||
new environment\preventexecpath(),
|
||||
new security\mediafilterswf(),
|
||||
|
@ -66,6 +66,8 @@ $string['check_crawlers_error'] = 'Search engine access is allowed but guest acc
|
||||
$string['check_crawlers_info'] = 'Search engines may enter as guests.';
|
||||
$string['check_crawlers_name'] = 'Open to search engines';
|
||||
$string['check_crawlers_ok'] = 'Search engine access is not enabled.';
|
||||
$string['check_dotfiles_info'] = 'All dotfiles except /.well-known/* should not be public';
|
||||
$string['check_dirindex_info'] = 'Directory index should not be enabled';
|
||||
$string['check_guestrole_details'] = '<p>The guest role is used for guests, not logged in users and temporary guest course access. Please make sure no risky capabilities are allowed in this role.</p>
|
||||
<p>The only supported legacy type for guest role is <em>Guest</em>.</p>';
|
||||
$string['check_guestrole_error'] = 'The guest role "{$a}" is incorrectly defined!';
|
||||
@ -92,7 +94,9 @@ $string['check_preventexecpath_name'] = 'Executable paths';
|
||||
$string['check_preventexecpath_ok'] = 'Executable paths only settable in config.php.';
|
||||
$string['check_preventexecpath_warning'] = 'Executable paths can be set in the Admin GUI.';
|
||||
$string['check_preventexecpath_details'] = '<p>Allowing executable paths to be set via the Admin GUI is a vector for privilege escalation. This must be forced in config.php:</p><p><code>$CFG->preventexecpath = true;<code></p>';
|
||||
|
||||
$string['check_publicpaths_name'] = 'Check all public / private paths';
|
||||
$string['check_publicpaths_generic'] = '{$a} files should not be public';
|
||||
$string['check_publicpaths_403'] = ' (Returned a 403, ideally should be 404)';
|
||||
$string['check_riskadmin_detailsok'] = '<p>Please verify the following list of system administrators:</p>{$a}';
|
||||
$string['check_riskadmin_detailswarning'] = '<p>Please verify the following list of system administrators:</p>{$a->admins}
|
||||
<p>It is recommended to assign administrator role in the system context only. The following users have (unsupported) admin role assignments in other contexts:</p>{$a->unsupported}';
|
||||
|
Loading…
x
Reference in New Issue
Block a user