From d78bca84e8e8e30438fe9b82b654ce11c0e6541a Mon Sep 17 00:00:00 2001
From: skodak
Date: Thu, 15 Jan 2009 20:16:12 +0000
Subject: [PATCH] MDL-17222 backported security report into 1.9.x
---
admin/report/security/db/access.php | 36 +
admin/report/security/index.php | 113 +++
admin/report/security/lib.php | 1009 +++++++++++++++++++++++++++
admin/report/security/settings.php | 3 +
admin/report/security/version.php | 29 +
lang/en_utf8/report_security.php | 137 ++++
theme/standard/styles_color.css | 11 +
version.php | 2 +-
8 files changed, 1339 insertions(+), 1 deletion(-)
create mode 100644 admin/report/security/db/access.php
create mode 100644 admin/report/security/index.php
create mode 100644 admin/report/security/lib.php
create mode 100644 admin/report/security/settings.php
create mode 100644 admin/report/security/version.php
create mode 100644 lang/en_utf8/report_security.php
diff --git a/admin/report/security/db/access.php b/admin/report/security/db/access.php
new file mode 100644
index 00000000000..8534dd81e46
--- /dev/null
+++ b/admin/report/security/db/access.php
@@ -0,0 +1,36 @@
+ array(
+ 'riskbitmask' => RISK_CONFIG,
+ 'captype' => 'read',
+ 'contextlevel' => CONTEXT_SYSTEM,
+ 'legacy' => array(
+ 'admin' => CAP_ALLOW
+ ),
+ )
+);
diff --git a/admin/report/security/index.php b/admin/report/security/index.php
new file mode 100644
index 00000000000..f243abbeb40
--- /dev/null
+++ b/admin/report/security/index.php
@@ -0,0 +1,113 @@
+dirroot.'/'.$CFG->admin.'/report/security/lib.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+require_login();
+
+$issue = optional_param('issue', '', PARAM_FILE); // show detailed info about one issue only
+
+$issues = report_security_get_issue_list();
+
+// test if issue valid string
+if (array_search($issue, $issues, true) === false) {
+ $issue = '';
+}
+
+// Print the header.
+admin_externalpage_setup('reportsecurity');
+admin_externalpage_print_header();
+
+print_heading(get_string('reportsecurity', 'report_security'));
+
+$strok = ''.get_string('statusok', 'report_security').'';
+$strinfo = ''.get_string('statusinfo', 'report_security').'';
+$strwarning = ''.get_string('statuswarning', 'report_security').'';
+$strserious = ''.get_string('statusserious', 'report_security').'';
+$strcritical = ''.get_string('statuscritical', 'report_security').'';
+
+$strissue = get_string('issue', 'report_security');
+$strstatus = get_string('status', 'report_security');
+$strdesc = get_string('description', 'report_security');
+$strconfig = get_string('configuration', 'report_security');
+
+$statusarr = array(REPORT_SECURITY_OK => $strok,
+ REPORT_SECURITY_INFO => $strinfo,
+ REPORT_SECURITY_WARNING => $strwarning,
+ REPORT_SECURITY_SERIOUS => $strserious,
+ REPORT_SECURITY_CRITICAL => $strcritical);
+
+$url = "$CFG->wwwroot/$CFG->admin/report/security/index.php";
+
+if ($issue and ($result = $issue(true))) {
+ $table = new object();
+ $table->head = array($strissue, $strstatus, $strdesc, $strconfig);
+ $table->size = array('30%', '10%', '50%', '10%' );
+ $table->align = array('left', 'left', 'left', 'left');
+ $table->width = '90%';
+ $table->data = array();
+
+ // print detail of one issue only
+ $row = array();
+ $row[0] = $result->name;
+ $row[1] = $statusarr[$result->status];
+ $row[2] = $result->info;
+ $row[3] = is_null($result->link) ? ' ' : $result->link;
+
+ $table->data[] = $row;
+
+ print_table($table);
+
+ print_box($result->details, 'generalbox boxwidthnormal boxaligncenter'); // TODO: add proper css
+
+ print_continue($url);
+
+} else {
+ $table = new object();
+ $table->head = array($strissue, $strstatus, $strdesc);
+ $table->size = array('30%', '10%', '60%' );
+ $table->align = array('left', 'left', 'left');
+ $table->width = '90%';
+ $table->data = array();
+
+ foreach ($issues as $issue) {
+ $result = $issue(false);
+ if (!$result) {
+ // ignore this test
+ continue;
+ }
+ $row = array();
+ $row[0] = "$result->name";
+ $row[1] = $statusarr[$result->status];
+ $row[2] = $result->info;
+
+ $table->data[] = $row;
+ }
+ print_table($table);
+}
+
+print_footer();
\ No newline at end of file
diff --git a/admin/report/security/lib.php b/admin/report/security/lib.php
new file mode 100644
index 00000000000..c8a75820af5
--- /dev/null
+++ b/admin/report/security/lib.php
@@ -0,0 +1,1009 @@
+libdir/adminlib.php");
+
+
+define('REPORT_SECURITY_OK', 'ok');
+define('REPORT_SECURITY_INFO', 'info');
+define('REPORT_SECURITY_WARNING', 'warning');
+define('REPORT_SECURITY_SERIOUS', 'serious');
+define('REPORT_SECURITY_CRITICAL', 'critical');
+
+
+function report_security_get_issue_list() {
+ return array(
+ 'report_security_check_globals',
+ 'report_security_check_unsecuredataroot',
+ 'report_security_check_displayerrors',
+ 'report_security_check_noauth',
+ 'report_security_check_embed',
+ 'report_security_check_mediafilterswf',
+ 'report_security_check_openprofiles',
+ 'report_security_check_google',
+ 'report_security_check_passwordpolicy',
+ 'report_security_check_emailchangeconfirmation',
+ 'report_security_check_cookiesecure',
+ 'report_security_check_configrw',
+ 'report_security_check_riskxss',
+ 'report_security_check_riskadmin',
+ 'report_security_check_defaultuserrole',
+ 'report_security_check_guestrole',
+ 'report_security_check_frontpagerole',
+ 'report_security_check_defaultcourserole',
+ 'report_security_check_courserole',
+
+ );
+}
+
+///=============================================
+/// Issue checks
+///=============================================
+
+
+/**
+ * Verifies register globals PHP setting.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_globals($detailed=false) {
+ $result = new object();
+ $result->issue = 'report_security_check_globals';
+ $result->name = get_string('check_globals_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+
+ if (ini_get_bool('register_globals')) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_globals_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_globals_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_globals_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies unsupported noauth setting
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_noauth($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_noauth';
+ $result->name = get_string('check_noauth_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=manageauths\">".get_string('authsettings', 'admin').'';
+
+ if (is_enabled_auth('none')) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_noauth_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_noauth_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_noauth_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies if password policy set
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_passwordpolicy($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_passwordpolicy';
+ $result->name = get_string('check_passwordpolicy_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'';
+
+ if (empty($CFG->passwordpolicy)) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_passwordpolicy_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_passwordpolicy_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_passwordpolicy_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sloppy embedding - this should have been removed long ago!!
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_embed($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_embed';
+ $result->name = get_string('check_embed_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'';
+
+ if (!empty($CFG->allowobjectembed)) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_embed_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_embed_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_embed_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sloppy swf embedding - this should have been removed long ago!!
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_mediafilterswf($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_mediafilterswf';
+ $result->name = get_string('check_mediafilterswf_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=filtersettingfiltermediaplugin\">".get_string('filtersettings', 'admin').'';
+
+ if (!empty($CFG->textfilters)) {
+ $activefilters = explode(',', $CFG->textfilters);
+ } else {
+ $activefilters = array();
+ }
+
+ if (array_search('filter/mediaplugin', $activefilters) !== false and !empty($CFG->filter_mediaplugin_enable_swf)) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_mediafilterswf_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_mediafilterswf_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_mediafilterswf_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies fatal misconfiguration of dataroot
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_unsecuredataroot($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_unsecuredataroot';
+ $result->name = get_string('check_unsecuredataroot_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+
+ $insecuredataroot = is_dataroot_insecure(true);
+
+ if ($insecuredataroot == INSECURE_DATAROOT_WARNING) {
+ $result->status = REPORT_SECURITY_SERIOUS;
+ $result->info = get_string('check_unsecuredataroot_warning', 'report_security', $CFG->dataroot);
+
+ } else if ($insecuredataroot == INSECURE_DATAROOT_ERROR) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_unsecuredataroot_error', 'report_security', $CFG->dataroot);
+
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_unsecuredataroot_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_unsecuredataroot_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies disaplying of errors - problem for lib files and 3rd party code
+ * because we can not disable debugging in these scripts (they do not include config.php)
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_displayerrors($detailed=false) {
+ $result = new object();
+ $result->issue = 'report_security_check_displayerrors';
+ $result->name = get_string('check_displayerrors_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+
+ if (defined('WARN_DISPLAY_ERRORS_ENABLED')) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_displayerrors_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_displayerrors_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_displayerrors_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies open profiles - originaly open by default, not anymore because spammer abused it a lot
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_openprofiles($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_openprofiles';
+ $result->name = get_string('check_openprofiles_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'';
+
+ if (empty($CFG->forcelogin) and empty($CFG->forceloginforprofiles)) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_openprofiles_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_openprofiles_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_openprofiles_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies google access not combined with disabled guest access
+ * because attackers might gain guest access by modifying browser signature.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_google($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_google';
+ $result->name = get_string('check_google_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'';
+
+ if (empty($CFG->opentogoogle)) {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_google_ok', 'report_security');
+ } else if (!empty($CFG->guestloginbutton)) {
+ $result->status = REPORT_SECURITY_INFO;
+ $result->info = get_string('check_google_info', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_SERIOUS;
+ $result->info = get_string('check_google_error', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_google_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies email confirmation - spammers were changing mails very often
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_emailchangeconfirmation($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_emailchangeconfirmation';
+ $result->name = get_string('check_emailchangeconfirmation_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'';
+
+ if (empty($CFG->emailchangeconfirmation)) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_emailchangeconfirmation_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_emailchangeconfirmation_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_emailchangeconfirmation_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies if https enabled only secure cookies allowed,
+ * this prevents redirections and sending of cookies to unsecure port.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_cookiesecure($detailed=false) {
+ global $CFG;
+
+ if (strpos($CFG->wwwroot, 'https://') !== 0) {
+ return null;
+ }
+
+ $result = new object();
+ $result->issue = 'report_security_check_cookiesecure';
+ $result->name = get_string('check_cookiesecure_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=httpsecurity\">".get_string('httpsecurity', 'admin').'';
+
+ if (empty($CFG->cookiesecure)) {
+ $result->status = REPORT_SECURITY_SERIOUS;
+ $result->info = get_string('check_cookiesecure_error', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_cookiesecure_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_cookiesecure_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies config.php is not writable anymore after installation,
+ * config files were changed on several outdated server.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_configrw($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_configrw';
+ $result->name = get_string('check_configrw_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+
+ if (is_writable($CFG->dirroot.'/config.php')) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_configrw_warning', 'report_security');
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_configrw_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_configrw_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Lists all users with XSS risk, it would be great to combine this with risk trusts in user table,
+ * unfortunately nobody implemented user trust UI yet :-(
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_riskxss($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_riskxss';
+ $result->name = get_string('check_riskxss_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->link = null;
+
+ $sqlfrom = "FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}capabilities cap ON cap.name = rc.capability
+ JOIN {$CFG->prefix}context c ON c.id = rc.contextid
+ JOIN {$CFG->prefix}context sc ON (sc.path = c.path OR sc.path LIKE ".sql_concat('c.path', "'/%'").")
+ JOIN {$CFG->prefix}role_assignments ra ON (ra.contextid = sc.id AND ra.roleid = rc.roleid)
+ JOIN {$CFG->prefix}user u ON u.id = ra.userid
+ WHERE ".sql_bitand('cap.riskbitmask', RISK_XSS)." <> 0
+ AND rc.permission = ".CAP_ALLOW."
+ AND u.deleted = 0";
+
+ $count = count_records_sql("SELECT COUNT(DISTINCT u.id) $sqlfrom");
+
+ $result->info = get_string('check_riskxss_warning', 'report_security', $count);
+
+ if ($detailed) {
+ $users = get_records_sql("SELECT DISTINCT u.id, u.firstname, u.lastname, u.picture, u.imagealt $sqlfrom");
+ foreach ($users as $uid=>$user) {
+ $users[$uid] = fullname($user);
+ }
+ $users = implode(', ', $users);
+ $result->details = get_string('check_riskxss_details', 'report_security', $users);
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sanity of default user role.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_defaultuserrole($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_defaultuserrole';
+ $result->name = get_string('check_defaultuserrole_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'';;
+
+ if (!$default_role = get_record('role', 'id', $CFG->defaultuserroleid)) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_defaultuserrole_notset', 'report_security');
+ $result->details = $result->info;
+
+ return $result;
+ }
+
+ // first test if do anything enabled - that would be really crazy!
+ $sql = "SELECT COUNT(DISTINCT rc.contextid)
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $default_role->id";
+
+ $anythingcount = count_records_sql($sql);
+
+ // risky caps - usually very dangerous
+ $sql = "SELECT COUNT(DISTINCT rc.contextid)
+ FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}capabilities cap ON cap.name = rc.capability
+ WHERE ".sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $default_role->id";
+
+ $riskycount = count_records_sql($sql);
+
+ // default role can not have view cap in all courses - this would break moodle badly
+ $viewcap = record_exists('role_capabilities', 'roleid', $default_role->id, 'permission', CAP_ALLOW, 'capability', 'moodle/course:view');
+
+ // it may have either no or 'user' legacy type - nothing else, or else it would break during upgrades badly
+ $legacyok = false;
+ $sql = "SELECT rc.capability, 1
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability LIKE 'moodle/legacy:%'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $default_role->id";
+ $legacycaps = get_records_sql($sql);
+ if (!$legacycaps) {
+ $legacyok = true;
+ } else if (count($legacycaps) == 1 and isset($legacycaps['moodle/legacy:user'])) {
+ $legacyok = true;
+ }
+
+ if ($anythingcount or $riskycount or $viewcap or !$legacyok) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_defaultuserrole_error', 'report_security', format_string($default_role->name));
+
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_defaultuserrole_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_defaultuserrole_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sanity of guest role
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_guestrole($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_guestrole';
+ $result->name = get_string('check_guestrole_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'';;
+
+ if (!$guest_role = get_record('role', 'id', $CFG->guestroleid)) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_guestrole_notset', 'report_security');
+ $result->details = $result->info;
+
+ return $result;
+ }
+
+ // first test if do anything enabled - that would be really crazy!
+ $sql = "SELECT COUNT(DISTINCT rc.contextid)
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $guest_role->id";
+
+ $anythingcount = count_records_sql($sql);
+
+ // risky caps - usually very dangerous
+ $sql = "SELECT COUNT(DISTINCT rc.contextid)
+ FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}capabilities cap ON cap.name = rc.capability
+ WHERE ".sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $guest_role->id";
+
+ $riskycount = count_records_sql($sql);
+
+ // it may have either no or 'guest' legacy type - nothing else, or else it would break during upgrades badly
+ $legacyok = false;
+ $sql = "SELECT rc.capability, 1
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability LIKE 'moodle/legacy:%'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $guest_role->id";
+ $legacycaps = get_records_sql($sql);
+ if (!$legacycaps) {
+ $legacyok = true;
+ } else if (count($legacycaps) == 1 and isset($legacycaps['moodle/legacy:guest'])) {
+ $legacyok = true;
+ }
+
+ if ($anythingcount or $riskycount or !$legacyok) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_guestrole_error', 'report_security', format_string($guest_role->name));
+
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_guestrole_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_guestrole_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sanity of frontpage role
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_frontpagerole($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_frontpagerole';
+ $result->name = get_string('check_frontpagerole_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=frontpagesettings\">".get_string('frontpagesettings','admin').'';;
+
+ if (!$frontpage_role = get_record('role', 'id', $CFG->defaultfrontpageroleid)) {
+ $result->status = REPORT_SECURITY_INFO;
+ $result->info = get_string('check_frontpagerole_notset', 'report_security');
+ $result->details = get_string('check_frontpagerole_details', 'report_security');
+
+ return $result;
+ }
+
+ // first test if do anything enabled - that would be really crazy!
+ $sql = "SELECT COUNT(DISTINCT rc.contextid)
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $frontpage_role->id";
+
+ $anythingcount = count_records_sql($sql);
+
+ // risky caps - usually very dangerous
+ $sql = "SELECT COUNT(DISTINCT rc.contextid)
+ FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}capabilities cap ON cap.name = rc.capability
+ WHERE ".sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $frontpage_role->id";
+
+ $riskycount = count_records_sql($sql);
+
+ // there is no legacy role type for frontpage yet - anyway we can not allow teachers or admins there!
+ $sql = "SELECT rc.capability, 1
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability LIKE 'moodle/legacy:%'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $frontpage_role->id";
+ $legacycaps = get_records_sql($sql);
+ $legacyok = (!isset($legacycaps['moodle/legacy:teacher'])
+ and !isset($legacycaps['moodle/legacy:editingteacher'])
+ and !isset($legacycaps['moodle/legacy:coursecreator'])
+ and !isset($legacycaps['moodle/legacy:admin']));
+
+ if ($anythingcount or $riskycount or !$legacyok) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_frontpagerole_error', 'report_security', format_string($frontpage_role->name));
+
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_frontpagerole_ok', 'report_security');
+ }
+
+ if ($detailed) {
+ $result->details = get_string('check_frontpagerole_details', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sanity of site default course role.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_defaultcourserole($detailed=false) {
+ global $CFG;
+
+ $problems = array();
+
+ $result = new object();
+ $result->issue = 'report_security_check_defaultcourserole';
+ $result->name = get_string('check_defaultcourserole_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = "wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'';;
+
+ if ($detailed) {
+ $result->details = get_string('check_defaultcourserole_details', 'report_security');
+ }
+
+ if (!$student_role = get_record('role', 'id', $CFG->defaultcourseroleid)) {
+ $result->status = REPORT_SECURITY_WARNING;
+ $result->info = get_string('check_defaultcourserole_notset', 'report_security');
+ $result->details = get_string('check_defaultcourserole_details', 'report_security');
+
+ return $result;
+ }
+
+ // first test if do anything enabled - that would be really crazy!
+ $sql = "SELECT DISTINCT rc.contextid
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $student_role->id";
+
+ if ($anything_contexts = get_records_sql($sql)) {
+ foreach($anything_contexts as $contextid) {
+ if ($contextid == SYSCONTEXTID) {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&roleid=$CFG->defaultcourseroleid";
+ } else {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid&roleid=$CFG->defaultcourseroleid";
+ }
+ $problems[] = get_string('check_defaultcourserole_anything', 'report_security', $a);
+ }
+ }
+
+ // risky caps - usually very dangerous
+ $sql = "SELECT DISTINCT rc.contextid
+ FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}capabilities cap ON cap.name = rc.capability
+ WHERE ".sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $student_role->id";
+
+ if ($riskycontexts = get_records_sql($sql)) {
+ foreach($riskycontexts as $contextid=>$unused) {
+ if ($contextid == SYSCONTEXTID) {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&roleid=$CFG->defaultcourseroleid";
+ } else {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid&roleid=$CFG->defaultcourseroleid";
+ }
+ $problems[] = get_string('check_defaultcourserole_risky', 'report_security', $a);
+ }
+ }
+
+ // course creator or administrator does not make any sense here
+ $sql = "SELECT rc.capability, 1
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability LIKE 'moodle/legacy:%'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid = $student_role->id";
+ $legacycaps = get_records_sql($sql);
+ if (isset($legacycaps['moodle/legacy:coursecreator']) or isset($legacycaps['moodle/legacy:admin'])) {
+ $problems[] = get_string('check_defaultcourserole_legacy', 'report_security');
+ }
+
+ if ($problems) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_defaultcourserole_error', 'report_security', format_string($student_role->name));
+ if ($detailed) {
+ $result->details .= "";
+ foreach ($problems as $problem) {
+ $result->details .= "- $problem
";
+ }
+ $result->details .= "
";
+ }
+
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_defaultcourserole_ok', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Verifies sanity of default roles in courses.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_courserole($detailed=false) {
+ global $CFG, $SITE;
+
+ $problems = array();
+
+ $result = new object();
+ $result->issue = 'report_security_check_courserole';
+ $result->name = get_string('check_courserole_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+
+ if ($detailed) {
+ $result->details = get_string('check_courserole_details', 'report_security');
+ }
+
+ // get list of all student roles selected in courses excluding the default course role
+ $sql = "SELECT r.*
+ FROM {$CFG->prefix}role r
+ JOIN {$CFG->prefix}course c ON c.defaultrole = r.id
+ WHERE c.id <> $SITE->id AND r.id <> $CFG->defaultcourseroleid";
+
+ if (!$student_roles = get_records_sql($sql)) {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_courserole_notyet', 'report_security');
+ $result->details = get_string('check_courserole_details', 'report_security');
+
+ return $result;
+ }
+
+ $roleids = array_keys($student_roles);
+
+ // first test if do anything enabled - that would be really crazy!!!!!!
+ $inroles = implode(',', $roleids);
+ $sql = "SELECT rc.roleid, rc.contextid
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid IN ($inroles)
+ GROUP BY rc.roleid, rc.contextid
+ ORDER BY rc.roleid, rc.contextid";
+
+ $rs = get_recordset_sql($sql);
+ foreach($rs as $res) {
+ $roleid = $res->roleid;
+ $contextid = $res->contextid;
+ if ($contextid == SYSCONTEXTID) {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&roleid=$roleid";
+ } else {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid&roleid=$roleid";
+ }
+ $problems[] = get_string('check_courserole_anything', 'report_security', $a);
+ }
+ $rs->close();
+
+ // risky caps in any level - usually very dangerous!!
+ $inroles = implode(',', $roleids);
+ $sql = "SELECT rc.roleid, rc.contextid
+ FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}capabilities cap ON cap.name = rc.capability
+ WHERE ".sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid IN ($inroles)
+ GROUP BY rc.roleid, rc.contextid
+ ORDER BY rc.roleid, rc.contextid";
+ $rs = get_recordset_sql($sql);
+ foreach($rs as $res) {
+ $roleid = $res->roleid;
+ $contextid = $res->contextid;
+ if ($contextid == SYSCONTEXTID) {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&roleid=$roleid";
+ } else {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid&roleid=$roleid";
+ }
+ $problems[] = get_string('check_courserole_risky', 'report_security', $a);
+ }
+ $rs->close();
+
+ // course creator or administrator does not make any sense here!
+ $inroles = implode(',', $roleids);
+ $sql = "SELECT DISTINCT rc.roleid
+ FROM {$CFG->prefix}role_capabilities rc
+ WHERE (rc.capability = 'moodle/legacy:coursecreator' OR rc.capability = 'moodle/legacy:admin')
+ AND rc.permission = ".CAP_ALLOW."
+ AND rc.roleid IN ($inroles)";
+ if ($legacys = get_records_sql($sql)) {
+ foreach ($legacys as $roleid=>$unused) {
+ $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&roleid=$roleid";
+ $problems[] = get_string('check_defaultcourserole_legacy', 'report_security', $a);
+ }
+ }
+
+
+ if ($problems) {
+ $result->status = REPORT_SECURITY_CRITICAL;
+ $result->info = get_string('check_courserole_error', 'report_security');
+ if ($detailed) {
+ $result->details .= "";
+ foreach ($problems as $problem) {
+ $result->details .= "- $problem
";
+ }
+ $result->details .= "
";
+ }
+
+ } else {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_courserole_ok', 'report_security');
+ }
+
+ return $result;
+}
+
+/**
+ * Lists all admins.
+ * @param bool $detailed
+ * @return object result
+ */
+function report_security_check_riskadmin($detailed=false) {
+ global $CFG;
+
+ $result = new object();
+ $result->issue = 'report_security_check_riskadmin';
+ $result->name = get_string('check_riskadmin_name', 'report_security');
+ $result->info = null;
+ $result->details = null;
+ $result->status = null;
+ $result->link = null;
+
+ $sql = "SELECT DISTINCT u.id, u.firstname, u.lastname, u.picture, u.imagealt
+ FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}role_assignments ra ON (ra.contextid = rc.contextid AND ra.roleid = rc.roleid)
+ JOIN {$CFG->prefix}user u ON u.id = ra.userid
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND u.deleted = 0
+ AND rc.contextid = ".SYSCONTEXTID."";
+
+ $admins = get_records_sql($sql);
+
+ $sqlfrom = "FROM {$CFG->prefix}role_capabilities rc
+ JOIN {$CFG->prefix}context c ON c.id = rc.contextid
+ JOIN {$CFG->prefix}context sc ON (sc.path = c.path OR sc.path LIKE ".sql_concat('c.path', "'/%'").")
+ JOIN {$CFG->prefix}role_assignments ra ON (ra.contextid = sc.id AND ra.roleid = rc.roleid)
+ JOIN {$CFG->prefix}user u ON u.id = ra.userid
+ WHERE rc.capability = 'moodle/site:doanything'
+ AND rc.permission = ".CAP_ALLOW."
+ AND u.deleted = 0
+ AND ra.contextid <> ".SYSCONTEXTID."";
+
+ $count = count_records_sql("SELECT COUNT(DISTINCT u.id) $sqlfrom");
+
+ if (!$count) {
+ $result->status = REPORT_SECURITY_OK;
+ $result->info = get_string('check_riskadmin_ok', 'report_security', count($admins));
+
+ if ($detailed) {
+ foreach ($admins as $uid=>$user) {
+ $admins[$uid] = fullname($user);
+ }
+ $admins = implode(', ', $admins);
+ $result->details = get_string('check_riskadmin_detailsok', 'report_security', $admins);
+ }
+
+ } else {
+ $result->status = REPORT_SECURITY_WARNING;
+ $a = (object)array('admincount'=>count($admins), 'unsupcount'=>$count);
+ $result->info = get_string('check_riskadmin_warning', 'report_security', $a);
+
+ if ($detailed) {
+ foreach ($admins as $uid=>$user) {
+ $admins[$uid] = fullname($user);
+ }
+ $admins = implode(', ', $admins);
+ $users = get_records_sql("SELECT DISTINCT u.id, u.firstname, u.lastname, u.picture, u.imagealt $sqlfrom");
+ foreach ($users as $uid=>$user) {
+ $users[$uid] = fullname($user);
+ }
+ $users = implode(', ', $users);
+ $a = (object)array('admins'=>$admins, 'unsupported'=>$users);
+ $result->details = get_string('check_riskadmin_detailswarning', 'report_security', $a);
+ }
+ }
+
+ return $result;
+}
diff --git a/admin/report/security/settings.php b/admin/report/security/settings.php
new file mode 100644
index 00000000000..71bf1488f11
--- /dev/null
+++ b/admin/report/security/settings.php
@@ -0,0 +1,3 @@
+add('reports', new admin_externalpage('reportsecurity', get_string('reportsecurity', 'report_security'), "$CFG->wwwroot/$CFG->admin/report/security/index.php",'report/security:view'));
diff --git a/admin/report/security/version.php b/admin/report/security/version.php
new file mode 100644
index 00000000000..7a7fe0954b5
--- /dev/null
+++ b/admin/report/security/version.php
@@ -0,0 +1,29 @@
+version = 2007101500;
+$plugin->requires = 2007101533;
+
+?>
diff --git a/lang/en_utf8/report_security.php b/lang/en_utf8/report_security.php
new file mode 100644
index 00000000000..0cbf0c52a52
--- /dev/null
+++ b/lang/en_utf8/report_security.php
@@ -0,0 +1,137 @@
+It is recommended that the file permissions of config.php are changed after installation so that the file cannot be modified by the web server.
+Please note that this measure does not improve security of the server significantly, though it may slow down or limit general exploits.
';
+$string['check_configrw_name'] = 'Writable config.php';
+$string['check_configrw_ok'] = 'config.php can not be modified by PHP scripts.';
+$string['check_configrw_warning'] = 'PHP scripts may modify config.php.';
+
+$string['check_cookiesecure_details'] = 'If you enable https communication it is recommended that you also enable secure cookies. You should also add permanent redirection from http to https.
';
+$string['check_cookiesecure_error'] = 'Please enable secure cookies';
+$string['check_cookiesecure_name'] = 'Secure cookies';
+$string['check_cookiesecure_ok'] = 'Secure cookies enabled.';
+
+$string['check_courserole_anything'] = 'The do anything capability must not be allowed in this context.';
+$string['check_courserole_details'] = 'Each course has one default enrolment role specified. Please make sure no risky capabilities are allowed for this role.
+The only supported legacy type for the default course role is Student.
';
+$string['check_courserole_error'] = 'Incorrectly defined default course roles detected!';
+$string['check_courserole_legacy'] = 'Unsupported legacy type detected in the role.';
+$string['check_courserole_name'] = 'Default course roles';
+$string['check_courserole_notyet'] = 'Used only default course role.';
+$string['check_courserole_ok'] = 'Default course role definitions OK.';
+$string['check_courserole_risky'] = 'Risky capabilities detected in context.';
+
+$string['check_defaultcourserole_anything'] = 'The do anything capability must not be allowed in this context.';
+$string['check_defaultcourserole_details'] = 'The default student role for course enrolment specifies the default role for courses. Please make sure no risky capabilities are allowed in this role.
+The only supported legacy type for default role is Student.
';
+$string['check_defaultcourserole_error'] = 'Incorrectly defined default course role \"$a\" detected!';
+$string['check_defaultcourserole_legacy'] = 'Unsupported legacy type detected.';
+$string['check_defaultcourserole_name'] = 'Site default course role';
+$string['check_defaultcourserole_notset'] = 'Default role is not set.';
+$string['check_defaultcourserole_ok'] = 'Site default role definition OK.';
+$string['check_defaultcourserole_risky'] = 'Risky capabilities detected in context.';
+
+$string['check_defaultuserrole_details'] = 'All logged in users are given capabilities of the default user role. Please make sure no risky capabilities are allowed in this role.
+The only supported legacy type for the default user role is Authenticated user. The course view capability must not be enabled.
';
+$string['check_defaultuserrole_error'] = 'Incorrectly defined default user role \"$a\" detected!';
+$string['check_defaultuserrole_name'] = 'Registered user role';
+$string['check_defaultuserrole_notset'] = 'Default role is not set.';
+$string['check_defaultuserrole_ok'] = 'Registered user role definition ok.';
+
+$string['check_displayerrors_details'] = 'Enabling the PHP setting display_errors is not recommended on production sites because error messages can reveal sensitive information about your server.
';
+$string['check_displayerrors_error'] = 'The PHP setting to display errors is enabled. It is recommended that this is disabled.';
+$string['check_displayerrors_name'] = 'Displaying of PHP errors';
+$string['check_displayerrors_ok'] = 'Displaying of PHP errors disabled.';
+
+$string['check_emailchangeconfirmation_details'] = 'It is recommended that an email confirmation step is required when users change their email address in their profile. If disabled, spammers may try to exploit the server for resending of spam.
';
+$string['check_emailchangeconfirmation_error'] = 'Users may enter any email address.';
+$string['check_emailchangeconfirmation_name'] = 'Email change confirmation';
+$string['check_emailchangeconfirmation_ok'] = 'Confirmation of change of email address in user profile.';
+
+$string['check_embed_details'] = 'Unlimited object embedding is very dangerous - any registered user may launch an XSS attack against other server users. This setting should be disabled on production servers.
';
+$string['check_embed_error'] = 'Unlimited object embedding enabled - this is very dangerous for the majority of servers.';
+$string['check_embed_name'] = 'Allow EMBED and OBJECT';
+$string['check_embed_ok'] = 'Unlimited object embedding is not allowed.';
+
+$string['check_frontpagerole_details'] = 'The default frontpage role is given to all registered users for frontpage activities. Please make sure no risky capabilities are allowed for this role.
+It is recommended that a special role is created for this purpose and a legacy type role is not used.
';
+$string['check_frontpagerole_error'] = 'Incorrectly defined frontpage role \"$a\" detected!';
+$string['check_frontpagerole_name'] = 'Frontpage role';
+$string['check_frontpagerole_notset'] = 'Frontpage role is not set.';
+$string['check_frontpagerole_ok'] = 'Frontpage role definition OK.';
+
+$string['check_globals_details'] = 'Register globals is considered to be a highly insecure PHP setting.
+register_globals=off must be set in PHP configuration. This setting is controlled by editing your php.ini, Apache/IIS configuration or .htaccess file.
';
+$string['check_globals_error'] = 'Register globals MUST be disabled. Please fix the server PHP settings immediately!';
+$string['check_globals_name'] = 'Register globals';
+$string['check_globals_ok'] = 'Register globals are disabled.';
+
+$string['check_google_details'] = 'The Open to Google setting enables search engines to enter courses with guest access. There is no point in enabling this setting if guest login not allowed.
';
+$string['check_google_error'] = 'Search engines guest access allowed and guest access disabled.';
+$string['check_google_info'] = 'Search engines may enter as guests.';
+$string['check_google_name'] = 'Open to Google';
+$string['check_google_ok'] = 'Search engines guest access not enabled.';
+
+$string['check_guestrole_details'] = '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.
+The only supported legacy type for guest role is Guest.
';
+$string['check_guestrole_error'] = 'Incorrectly defined guest role \"$a\" detected!';
+$string['check_guestrole_name'] = 'Guest role';
+$string['check_guestrole_notset'] = 'Guest role is not set.';
+$string['check_guestrole_ok'] = 'Guest role definition OK.';
+
+$string['check_mediafilterswf_details'] = 'Automatic swf embedding is very dangerous - any registered user may launch an XSS attack against other server users. Please disable it on production servers.
';
+$string['check_mediafilterswf_error'] = 'Flash media filter is enabled - this is very dangerous for the majority of servers.';
+$string['check_mediafilterswf_name'] = 'Enabled .swf media filter';
+$string['check_mediafilterswf_ok'] = 'Flash media filter is not enabled.';
+
+$string['check_noauth_details'] = 'The No authentication plugin is not intended for production sites. Please disable it unless this is a development test site.
';
+$string['check_noauth_error'] = 'The No authentication plugin cannot be used on production sites.';
+$string['check_noauth_name'] = 'No authentication';
+$string['check_noauth_ok'] = 'No authentication plugin is disabled.';
+
+$string['check_openprofiles_details'] = 'Open user profiles can be abused by spammers. It is recommended that either Force users to login for profiles or Force users to login are enabled.
';
+$string['check_openprofiles_error'] = 'Anyone can may view user profiles without logging in.';
+$string['check_openprofiles_name'] = 'Open user profiles';
+$string['check_openprofiles_ok'] = 'Login is required before viewing user profiles.';
+
+$string['check_passwordpolicy_details'] = 'It is recommended that a password policy is set, since password guessing is very often the easiest way to gain unauthorised access.
+Do not make the requirements too strict though, as this can result in users not being able to remember their passwords and either forgetting them or writing them down.
';
+$string['check_passwordpolicy_error'] = 'Password policy not set.';
+$string['check_passwordpolicy_name'] = 'Password policy';
+$string['check_passwordpolicy_ok'] = 'Password policy enabled.';
+
+$string['check_riskadmin_detailsok'] = 'Please verify the following list of administrators:
$a
';
+$string['check_riskadmin_detailswarning'] = 'Please verify the following list of administrators:
$a->admins
+It is recommended to assign administrator role in system context only. Following users have unsupported admin role assignments:
$a->unsupported
';
+$string['check_riskadmin_name'] = 'Administrators';
+$string['check_riskadmin_ok'] = 'Found $a server administrator(s).';
+$string['check_riskadmin_warning'] = 'Found $a->admincount server administrators and $a->unsupcount unsupported admin role assignments.';
+
+$string['check_riskxss_details'] = 'RISK_XSS denotes all dangerous capabilities that only trusted users may use.
+Please verify following list of users and make sure that you trust them completely on this server:
$a
';
+$string['check_riskxss_name'] = 'XSS trusted users';
+$string['check_riskxss_warning'] = 'RISK_XSS - found $a users that have to be trusted.';
+
+$string['check_unsecuredataroot_details'] = 'The dataroot directory must not be accessible via web. The best way to make sure the directory is not accessible is to use a directory outside the public web directory.
+If you move the directory, you need to update the \$CFG->dataroot setting in config.php accordingly.
';
+$string['check_unsecuredataroot_error'] = 'Your dataroot directory $a is in the wrong location and is exposed to the web!';
+$string['check_unsecuredataroot_name'] = 'Insecure dataroot';
+$string['check_unsecuredataroot_ok'] = 'Dataroot directory must not be accessible via the web.';
+$string['check_unsecuredataroot_warning'] = 'Your dataroot directory $a is in the wrong location and might be exposed to the web.';
+?>
\ No newline at end of file
diff --git a/theme/standard/styles_color.css b/theme/standard/styles_color.css
index 870e3c819e4..2d5188930a9 100644
--- a/theme/standard/styles_color.css
+++ b/theme/standard/styles_color.css
@@ -322,6 +322,17 @@ table.flexible .r1 {
background-color: green;
}
+#admin-report-security-index .statuswarning {
+ background-color: #f0e000;
+}
+
+#admin-report-security-index .statusserious {
+ background-color: #f07000;
+}
+
+#admin-report-security-index .statuscritical {
+ background-color: #f00000;
+}
.plugincompattable td.ok {
color: #008000;
diff --git a/version.php b/version.php
index 36ebf81e149..d37a97ab237 100644
--- a/version.php
+++ b/version.php
@@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
- $version = 2007101533.01; // YYYYMMDD = date of the 1.9 branch (don't change)
+ $version = 2007101533.02; // YYYYMMDD = date of the 1.9 branch (don't change)
// X = release number 1.9.[0,1,2,3...]
// Y.YY = micro-increments between releases