mirror of
https://github.com/moodle/moodle.git
synced 2025-07-09 00:17:32 +02:00
Note: Removing the manual require for the attribute from moodlelib because the class does not have to exist at time of definition, only when the Reflection API instantiates an instance in \core\deprecation, by which point the autoloader is available.
1319 lines
41 KiB
PHP
1319 lines
41 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/>.
|
|
|
|
namespace core;
|
|
|
|
use coding_exception;
|
|
use core_text;
|
|
use core\attribute\deprecated;
|
|
use core\ip_utils;
|
|
use invalid_parameter_exception;
|
|
use moodle_exception;
|
|
|
|
// phpcs:disable Generic.CodeAnalysis.EmptyStatement.DetectedIf
|
|
|
|
/**
|
|
* Parameter validation helpers for Moodle.
|
|
*
|
|
* @package core
|
|
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
enum param: string {
|
|
/**
|
|
* PARAM_ALPHA - contains only English ascii letters [a-zA-Z].
|
|
*/
|
|
case ALPHA = 'alpha';
|
|
|
|
/**
|
|
* PARAM_ALPHAEXT the same contents as PARAM_ALPHA (English ascii letters [a-zA-Z]) plus the chars in quotes: "_-" allowed
|
|
* NOTE: originally this allowed "/" too, please use PARAM_SAFEPATH if "/" needed
|
|
*/
|
|
case ALPHAEXT = 'alphaext';
|
|
|
|
/**
|
|
* PARAM_ALPHANUM - expected numbers 0-9 and English ascii letters [a-zA-Z] only.
|
|
*/
|
|
case ALPHANUM = 'alphanum';
|
|
|
|
/**
|
|
* PARAM_ALPHANUMEXT - expected numbers 0-9, letters (English ascii letters [a-zA-Z]) and _- only.
|
|
*/
|
|
case ALPHANUMEXT = 'alphanumext';
|
|
|
|
/**
|
|
* PARAM_AUTH - actually checks to make sure the string is a valid auth plugin
|
|
*/
|
|
case AUTH = 'auth';
|
|
|
|
/**
|
|
* PARAM_BASE64 - Base 64 encoded format
|
|
*/
|
|
case BASE64 = 'base64';
|
|
|
|
/**
|
|
* PARAM_BOOL - converts input into 0 or 1, use for switches in forms and urls.
|
|
*/
|
|
case BOOL = 'bool';
|
|
|
|
/**
|
|
* PARAM_CAPABILITY - A capability name, like 'moodle/role:manage'. Actually
|
|
* checked against the list of capabilities in the database.
|
|
*/
|
|
case CAPABILITY = 'capability';
|
|
|
|
/**
|
|
* PARAM_CLEANHTML - cleans submitted HTML code. Note that you almost never want
|
|
* to use this. The normal mode of operation is to use PARAM_RAW when receiving
|
|
* the input (required/optional_param or formslib) and then sanitise the HTML
|
|
* using format_text on output. This is for the rare cases when you want to
|
|
* sanitise the HTML on input. This cleaning may also fix xhtml strictness.
|
|
*/
|
|
case CLEANHTML = 'cleanhtml';
|
|
|
|
/**
|
|
* PARAM_EMAIL - an email address following the RFC
|
|
*/
|
|
case EMAIL = 'email';
|
|
|
|
/**
|
|
* PARAM_FILE - safe file name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
|
|
*/
|
|
case FILE = 'file';
|
|
|
|
/**
|
|
* PARAM_FLOAT - a real/floating point number.
|
|
*
|
|
* Note that you should not use PARAM_FLOAT for numbers typed in by the user.
|
|
* It does not work for languages that use , as a decimal separator.
|
|
* Use PARAM_LOCALISEDFLOAT instead.
|
|
*/
|
|
case FLOAT = 'float';
|
|
|
|
/**
|
|
* PARAM_LOCALISEDFLOAT - a localised real/floating point number.
|
|
* This is preferred over PARAM_FLOAT for numbers typed in by the user.
|
|
* Cleans localised numbers to computer readable numbers; false for invalid numbers.
|
|
*/
|
|
case LOCALISEDFLOAT = 'localisedfloat';
|
|
|
|
/**
|
|
* PARAM_HOST - expected fully qualified domain name (FQDN) or an IPv4 dotted quad (IP address)
|
|
*/
|
|
case HOST = 'host';
|
|
|
|
/**
|
|
* PARAM_INT - integers only, use when expecting only numbers.
|
|
*/
|
|
case INT = 'int';
|
|
|
|
/**
|
|
* PARAM_LANG - checks to see if the string is a valid installed language in the current site.
|
|
*/
|
|
case LANG = 'lang';
|
|
|
|
/**
|
|
* PARAM_LOCALURL - expected properly formatted URL as well as one that refers to the local server itself. (NOT orthogonal to the
|
|
* others! Implies PARAM_URL!)
|
|
*/
|
|
case LOCALURL = 'localurl';
|
|
|
|
/**
|
|
* PARAM_NOTAGS - all html tags are stripped from the text. Do not abuse this type.
|
|
*/
|
|
case NOTAGS = 'notags';
|
|
|
|
/**
|
|
* PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory
|
|
* traversals note: the leading slash is not removed, window drive letter is not allowed
|
|
*/
|
|
case PATH = 'path';
|
|
|
|
/**
|
|
* PARAM_PEM - Privacy Enhanced Mail format
|
|
*/
|
|
case PEM = 'pem';
|
|
|
|
/**
|
|
* PARAM_PERMISSION - A permission, one of CAP_INHERIT, CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT.
|
|
*/
|
|
case PERMISSION = 'permission';
|
|
|
|
/**
|
|
* PARAM_RAW specifies a parameter that is not cleaned/processed in any way except the discarding of the invalid utf-8 characters
|
|
*/
|
|
case RAW = 'raw';
|
|
|
|
/**
|
|
* PARAM_RAW_TRIMMED like PARAM_RAW but leading and trailing whitespace is stripped.
|
|
*/
|
|
case RAW_TRIMMED = 'raw_trimmed';
|
|
|
|
/**
|
|
* PARAM_SAFEDIR - safe directory name, suitable for include() and require()
|
|
*/
|
|
case SAFEDIR = 'safedir';
|
|
|
|
/**
|
|
* PARAM_SAFEPATH - several PARAM_SAFEDIR joined by "/", suitable for include() and require(), plugin paths
|
|
* and other references to Moodle code files.
|
|
*
|
|
* This is NOT intended to be used for absolute paths or any user uploaded files.
|
|
*/
|
|
case SAFEPATH = 'safepath';
|
|
|
|
/**
|
|
* PARAM_SEQUENCE - expects a sequence of numbers like 8 to 1,5,6,4,6,8,9. Numbers and comma only.
|
|
*/
|
|
case SEQUENCE = 'sequence';
|
|
|
|
/**
|
|
* PARAM_TAG - one tag (interests, blogs, etc.) - mostly international characters and space, <> not supported
|
|
*/
|
|
case TAG = 'tag';
|
|
|
|
/**
|
|
* PARAM_TAGLIST - list of tags separated by commas (interests, blogs, etc.)
|
|
*/
|
|
case TAGLIST = 'taglist';
|
|
|
|
/**
|
|
* PARAM_TEXT - general plain text compatible with multilang filter, no other html tags. Please note '<', or '>' are allowed here.
|
|
*/
|
|
case TEXT = 'text';
|
|
|
|
/**
|
|
* PARAM_THEME - Checks to see if the string is a valid theme name in the current site
|
|
*/
|
|
case THEME = 'theme';
|
|
|
|
/**
|
|
* PARAM_URL - expected properly formatted URL. Please note that domain part is required, http://localhost/ is not accepted but
|
|
* http://localhost.localdomain/ is ok.
|
|
*/
|
|
case URL = 'url';
|
|
|
|
/**
|
|
* PARAM_USERNAME - Clean username to only contains allowed characters. This is to be used ONLY when manually creating user
|
|
* accounts, do NOT use when syncing with external systems!!
|
|
*/
|
|
case USERNAME = 'username';
|
|
|
|
/**
|
|
* PARAM_STRINGID - used to check if the given string is valid string identifier for get_string()
|
|
*/
|
|
case STRINGID = 'stringid';
|
|
|
|
/**
|
|
* PARAM_CLEAN - obsoleted, please use a more specific type of parameter.
|
|
* It was one of the first types, that is why it is abused so much ;-)
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'a more specific type of parameter',
|
|
since: '2.0',
|
|
reason: 'The CLEAN param type is too generic to perform satisfactory validation',
|
|
emit: false,
|
|
)]
|
|
case CLEAN = 'clean';
|
|
|
|
/**
|
|
* PARAM_INTEGER - deprecated alias for PARAM_INT
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'param::INT',
|
|
since: '2.0',
|
|
reason: 'Alias for INT',
|
|
final: true,
|
|
)]
|
|
case INTEGER = 'integer';
|
|
|
|
/**
|
|
* PARAM_NUMBER - deprecated alias of PARAM_FLOAT
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'param::FLOAT',
|
|
since: '2.0',
|
|
reason: 'Alias for FLOAT',
|
|
final: true,
|
|
)]
|
|
case NUMBER = 'number';
|
|
|
|
/**
|
|
* PARAM_ACTION - deprecated alias for PARAM_ALPHANUMEXT, use for various actions in forms and urls
|
|
* NOTE: originally alias for PARAM_ALPHANUMEXT
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'param::ALPHANUMEXT',
|
|
since: '2.0',
|
|
reason: 'Alias for PARAM_ALPHANUMEXT',
|
|
final: true,
|
|
)]
|
|
case ACTION = 'action';
|
|
|
|
/**
|
|
* PARAM_FORMAT - deprecated alias for PARAM_ALPHANUMEXT, use for names of plugins, formats, etc.
|
|
* NOTE: originally alias for PARAM_APLHA
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'param::ALPHANUMEXT',
|
|
since: '2.0',
|
|
reason: 'Alias for PARAM_ALPHANUMEXT',
|
|
final: true,
|
|
)]
|
|
case FORMAT = 'format';
|
|
|
|
/**
|
|
* PARAM_MULTILANG - deprecated alias of PARAM_TEXT.
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'param::TEXT',
|
|
since: '2.0',
|
|
reason: 'Alias for PARAM_TEXT',
|
|
final: true,
|
|
)]
|
|
case MULTILANG = 'multilang';
|
|
|
|
/**
|
|
* PARAM_TIMEZONE - expected timezone. Timezone can be int +-(0-13) or float +-(0.5-12.5) or
|
|
* string separated by '/' and can have '-' &/ '_' (eg. America/North_Dakota/New_Salem
|
|
* America/Port-au-Prince)
|
|
*/
|
|
case TIMEZONE = 'timezone';
|
|
|
|
/**
|
|
* PARAM_CLEANFILE - deprecated alias of PARAM_FILE; originally was removing regional chars too
|
|
* @deprecated since 2.0
|
|
*/
|
|
#[deprecated(
|
|
replacement: 'param::FILE',
|
|
since: '2.0',
|
|
reason: 'Alias for PARAM_FILE',
|
|
)]
|
|
case CLEANFILE = 'cleanfile';
|
|
|
|
/**
|
|
* PARAM_COMPONENT is used for full component names (aka frankenstyle) such as 'mod_forum = 'core_rating', 'auth_ldap'.
|
|
* Short legacy subsystem names and module names are accepted too ex: 'forum = 'rating', 'user'.
|
|
* Only lowercase ascii letters, numbers and underscores are allowed, it has to start with a letter.
|
|
* NOTE: numbers and underscores are strongly discouraged in plugin names!
|
|
*/
|
|
case COMPONENT = 'component';
|
|
|
|
/**
|
|
* PARAM_AREA is a name of area used when addressing files, comments, ratings, etc.
|
|
* It is usually used together with context id and component.
|
|
* Only lowercase ascii letters, numbers and underscores are allowed, it has to start with a letter.
|
|
*/
|
|
case AREA = 'area';
|
|
|
|
/**
|
|
* PARAM_PLUGIN is used for plugin names such as 'forum = 'glossary', 'ldap', 'paypal', 'completionstatus'.
|
|
* Only lowercase ascii letters, numbers and underscores are allowed, it has to start with a letter.
|
|
* NOTE: numbers and underscores are strongly discouraged in plugin names! Underscores are forbidden in module names.
|
|
*/
|
|
case PLUGIN = 'plugin';
|
|
|
|
/**
|
|
* Get the canonical enumerated parameter from the parameter type name.
|
|
*
|
|
* @param string $paramname
|
|
* @return param
|
|
* @throws coding_exception If the parameter is unknown.
|
|
*/
|
|
public static function from_type(string $paramname): self {
|
|
$from = self::tryFrom($paramname)?->canonical();
|
|
if ($from) {
|
|
return $from;
|
|
}
|
|
|
|
throw new \coding_exception("Unknown parameter type '{$paramname}'");
|
|
}
|
|
|
|
/**
|
|
* Canonicalise the parameter.
|
|
*
|
|
* This method is used to support aliasing of deprecated parameters.
|
|
*
|
|
* @return param
|
|
*/
|
|
private function canonical(): self {
|
|
return match ($this) {
|
|
self::ACTION => self::ALPHANUMEXT,
|
|
self::CLEANFILE => self::FILE,
|
|
self::FORMAT => self::ALPHANUMEXT,
|
|
self::INTEGER => self::INT,
|
|
self::MULTILANG => self::TEXT,
|
|
self::NUMBER => self::FLOAT,
|
|
|
|
default => $this,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Used by {@link optional_param()} and {@link required_param()} to
|
|
* clean the variables and/or cast to specific types, based on
|
|
* an options field.
|
|
*
|
|
* <code>
|
|
* $course->format = param::ALPHA->clean($course->format);
|
|
* $selectedgradeitem = param::INT->clean($selectedgradeitem);
|
|
* </code>
|
|
*
|
|
* @param mixed $value the value to clean
|
|
* @return mixed
|
|
* @throws coding_exception
|
|
*/
|
|
public function clean(mixed $value): mixed {
|
|
// Check and emit a deprecation notice if required.
|
|
deprecation::emit_deprecation_if_present($this);
|
|
|
|
if (is_array($value)) {
|
|
throw new coding_exception('clean() can not process arrays, please use clean_array() instead.');
|
|
} else if (is_object($value)) {
|
|
if (method_exists($value, '__toString')) {
|
|
$value = $value->__toString();
|
|
} else {
|
|
throw new coding_exception('clean() can not process objects, please use clean_array() instead.');
|
|
}
|
|
}
|
|
|
|
$canonical = $this->canonical();
|
|
if ($this !== $canonical) {
|
|
return $canonical->clean($value);
|
|
}
|
|
|
|
$methodname = "clean_param_value_{$this->value}";
|
|
if (!method_exists(self::class, $methodname)) {
|
|
throw new coding_exception("Method not found for cleaning {$this->value}");
|
|
}
|
|
|
|
return $this->{$methodname}($value);
|
|
}
|
|
|
|
/**
|
|
* Returns a value for the named variable, taken from request arguments.
|
|
*
|
|
* This function should be used to initialise all required values
|
|
* in a script that are based on parameters. Usually it will be
|
|
* used like this:
|
|
* $id = param::INT->required_param('id');
|
|
*
|
|
*
|
|
* @param string $paramname the name of the page parameter we want
|
|
* @return mixed
|
|
* @throws moodle_exception
|
|
*/
|
|
public function required_param(string $paramname): mixed {
|
|
return $this->clean($this->get_request_parameter($paramname, true));
|
|
}
|
|
|
|
/**
|
|
* Returns a particular array value for the named variable, taken from request arguments.
|
|
* If the parameter doesn't exist then an error is thrown because we require this variable.
|
|
*
|
|
* This function should be used to initialise all required values
|
|
* in a script that are based on parameters. Usually it will be
|
|
* used like this:
|
|
* $ids = required_param_array('ids', PARAM_INT);
|
|
*
|
|
* Note: arrays of arrays are not supported, only alphanumeric keys with _ and - are supported
|
|
*
|
|
* @param string $paramname the name of the page parameter we want
|
|
* @return array
|
|
* @throws moodle_exception
|
|
*/
|
|
public function required_param_array(string $paramname): array {
|
|
$param = $this->get_request_parameter($paramname, true);
|
|
|
|
if (!is_array($param)) {
|
|
throw new \moodle_exception('missingparam', '', '', $paramname);
|
|
}
|
|
|
|
$result = [];
|
|
foreach ($param as $key => $value) {
|
|
if (!preg_match('/^[a-z0-9_-]+$/i', $key)) {
|
|
debugging(
|
|
"Invalid key name in required_param_array() detected: {$key}, parameter: {$paramname}",
|
|
);
|
|
continue;
|
|
}
|
|
$result[$key] = $this->clean($value);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Returns a particular value for the named variable from the request arguments,
|
|
* otherwise returning a given default.
|
|
*
|
|
* This function should be used to initialise all optional values
|
|
* in a script that are based on parameters. Usually it will be
|
|
* used like this:
|
|
* $name = param::TEXT->optional_param('name', 'Fred');
|
|
*
|
|
* @param string $paramname the name of the page parameter we want
|
|
* @param mixed $default the default value to return if nothing is found
|
|
* @return mixed
|
|
*/
|
|
public function optional_param(string $paramname, mixed $default): mixed {
|
|
$param = $this->get_request_parameter($paramname, false);
|
|
if ($param === null) {
|
|
return $default;
|
|
}
|
|
|
|
return $this->clean($param);
|
|
}
|
|
|
|
/**
|
|
* Returns a particular array value for the named variable from the request arguments,
|
|
* otherwise returning a given default.
|
|
*
|
|
* This function should be used to initialise all optional values
|
|
* in a script that are based on parameters. Usually it will be
|
|
* used like this:
|
|
* $ids = param::INT->optional_param_arrayt('id', array());
|
|
*
|
|
* Note: arrays of arrays are not supported, only alphanumeric keys with _ and - are supported.
|
|
*
|
|
* @param string $paramname the name of the page parameter we want
|
|
* @param mixed $default the default value to return if nothing is found
|
|
* @return array
|
|
*/
|
|
public function optional_param_array(string $paramname, mixed $default): mixed {
|
|
$param = $this->get_request_parameter($paramname, false);
|
|
if ($param === null) {
|
|
return $default;
|
|
}
|
|
|
|
if (!is_array($param)) {
|
|
debugging(
|
|
"optional_param_array() only expects array parameters: {$paramname}",
|
|
);
|
|
return $default;
|
|
}
|
|
|
|
$result = [];
|
|
foreach ($param as $key => $value) {
|
|
if (!preg_match('/^[a-z0-9_-]+$/i', $key)) {
|
|
debugging(
|
|
"Invalid key name in optional_param_array() detected: {$key}, parameter: {$paramname}",
|
|
);
|
|
continue;
|
|
}
|
|
$result[$key] = $this->clean($value);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Returns a particular value for the named variable, taken from the POST, or GET params.
|
|
*
|
|
* Parameters are fetched from POST first, then GET.
|
|
*
|
|
* @param string $paramname
|
|
* @param bool $require
|
|
* @return mixed
|
|
* @throws moodle_exception If the parameter was not found and the value is required
|
|
*/
|
|
private function get_request_parameter(
|
|
string $paramname,
|
|
bool $require,
|
|
): mixed {
|
|
if (isset($_POST[$paramname])) {
|
|
return $_POST[$paramname];
|
|
} else if (isset($_GET[$paramname])) {
|
|
return $_GET[$paramname];
|
|
} else if ($require) {
|
|
throw new \moodle_exception('missingparam', '', '', $paramname);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Strict validation of parameter values, the values are only converted
|
|
* to requested PHP type. Internally it is using clean_param, the values
|
|
* before and after cleaning must be equal - otherwise
|
|
* an invalid_parameter_exception is thrown.
|
|
* Objects and classes are not accepted.
|
|
*
|
|
* @param mixed $param
|
|
* @param bool $allownull are nulls valid value?
|
|
* @param string $debuginfo optional debug information
|
|
* @return mixed the $param value converted to PHP type
|
|
* @throws invalid_parameter_exception if $param is not of given type
|
|
*/
|
|
public function validate_param(
|
|
mixed $param,
|
|
bool $allownull = NULL_NOT_ALLOWED,
|
|
string $debuginfo = '',
|
|
): mixed {
|
|
if (is_null($param)) {
|
|
if ($allownull == NULL_ALLOWED) {
|
|
return null;
|
|
} else {
|
|
throw new invalid_parameter_exception($debuginfo);
|
|
}
|
|
}
|
|
if (is_array($param) || is_object($param)) {
|
|
throw new invalid_parameter_exception($debuginfo);
|
|
}
|
|
|
|
$cleaned = $this->clean($param);
|
|
|
|
if ($this->canonical() === self::FLOAT) {
|
|
// Do not detect precision loss here.
|
|
if (is_float($param) || is_int($param)) {
|
|
// These always fit.
|
|
} else if (!is_numeric($param) || !preg_match('/^[\+-]?[0-9]*\.?[0-9]*(e[-+]?[0-9]+)?$/i', (string)$param)) {
|
|
throw new invalid_parameter_exception($debuginfo);
|
|
}
|
|
} else if ((string) $param !== (string) $cleaned) {
|
|
// Conversion to string is usually lossless.
|
|
throw new invalid_parameter_exception($debuginfo);
|
|
}
|
|
|
|
return $cleaned;
|
|
}
|
|
|
|
/**
|
|
* Makes sure array contains only the allowed types, this function does not validate array key names!
|
|
*
|
|
* <code>
|
|
* $options = param::INT->clean_param_array($options);
|
|
* </code>
|
|
*
|
|
* @param array|null $param the variable array we are cleaning
|
|
* @param bool $recursive clean recursive arrays
|
|
* @return array
|
|
* @throws coding_exception
|
|
*/
|
|
public function clean_param_array(
|
|
?array $param,
|
|
bool $recursive = false,
|
|
) {
|
|
// Convert null to empty array.
|
|
$param = (array) $param;
|
|
foreach ($param as $key => $value) {
|
|
if (is_array($value)) {
|
|
if ($recursive) {
|
|
$param[$key] = $this->clean_param_array($value, true);
|
|
} else {
|
|
throw new coding_exception('clean_param_array can not process multidimensional arrays when $recursive is false.');
|
|
}
|
|
} else {
|
|
$param[$key] = $this->clean($value);
|
|
}
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_RAW.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_raw(mixed $param): mixed {
|
|
// No cleaning at all.
|
|
return fix_utf8($param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_RAW_TRIMMED.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_raw_trimmed(mixed $param): string {
|
|
// No cleaning, but strip leading and trailing whitespace.
|
|
return trim((string) $this->clean_param_value_raw($param));
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_CLEAN.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_clean(mixed $param): string {
|
|
// General HTML cleaning, try to use more specific type if possible this is deprecated!
|
|
// Please use more specific type instead.
|
|
if (is_numeric($param)) {
|
|
return $param;
|
|
}
|
|
$param = fix_utf8($param);
|
|
// Sweep for scripts, etc.
|
|
return clean_text($param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_CLEANHTML.
|
|
*/
|
|
protected function clean_param_value_cleanhtml(mixed $param): mixed {
|
|
// Clean html fragment.
|
|
$param = (string)fix_utf8($param);
|
|
// Sweep for scripts, etc.
|
|
$param = clean_text($param, FORMAT_HTML);
|
|
|
|
return trim($param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_INT.
|
|
*
|
|
* @param mixed $param
|
|
* @return int
|
|
*/
|
|
protected function clean_param_value_int(mixed $param): int {
|
|
// Convert to integer.
|
|
return (int)$param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_FLOAT.
|
|
*
|
|
* @param mixed $param
|
|
* @return float
|
|
*/
|
|
protected function clean_param_value_float(mixed $param): float {
|
|
// Convert to float.
|
|
return (float)$param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_LOCALISEDFLOAT.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_localisedfloat(mixed $param): mixed {
|
|
// Convert to float.
|
|
return unformat_float($param, true);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_ALPHA.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_alpha(mixed $param): mixed {
|
|
// Remove everything not `a-z`.
|
|
return preg_replace('/[^a-zA-Z]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_ALPHAEXT.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_alphaext(mixed $param): mixed {
|
|
// Remove everything not `a-zA-Z_-` (originally allowed "/" too).
|
|
return preg_replace('/[^a-zA-Z_-]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_ALPHANUM.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_alphanum(mixed $param): mixed {
|
|
// Remove everything not `a-zA-Z0-9`.
|
|
return preg_replace('/[^A-Za-z0-9]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_ALPHANUMEXT.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_alphanumext(mixed $param): mixed {
|
|
// Remove everything not `a-zA-Z0-9_-`.
|
|
return preg_replace('/[^A-Za-z0-9_-]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_SEQUENCE.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_sequence(mixed $param): mixed {
|
|
// Remove everything not `0-9,`.
|
|
return preg_replace('/[^0-9,]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_BOOL.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_bool(mixed $param): mixed {
|
|
// Convert to 1 or 0.
|
|
$tempstr = strtolower((string)$param);
|
|
if ($tempstr === 'on' || $tempstr === 'yes' || $tempstr === 'true') {
|
|
$param = 1;
|
|
} else if ($tempstr === 'off' || $tempstr === 'no' || $tempstr === 'false') {
|
|
$param = 0;
|
|
} else {
|
|
$param = empty($param) ? 0 : 1;
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_NOTAGS.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_notags(mixed $param): mixed {
|
|
// Strip all tags.
|
|
$param = fix_utf8($param);
|
|
return strip_tags((string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_TEXT.
|
|
*
|
|
* @param mixed $param
|
|
* @return mixed
|
|
*/
|
|
protected function clean_param_value_text(mixed $param): mixed {
|
|
// Leave only tags needed for multilang.
|
|
$param = fix_utf8($param);
|
|
// If the multilang syntax is not correct we strip all tags because it would break xhtml strict which is required
|
|
// for accessibility standards please note this cleaning does not strip unbalanced '>' for BC compatibility reasons.
|
|
do {
|
|
if (strpos((string)$param, '</lang>') !== false) {
|
|
// Old and future mutilang syntax.
|
|
$param = strip_tags($param, '<lang>');
|
|
if (!preg_match_all('/<.*>/suU', $param, $matches)) {
|
|
break;
|
|
}
|
|
$open = false;
|
|
foreach ($matches[0] as $match) {
|
|
if ($match === '</lang>') {
|
|
if ($open) {
|
|
$open = false;
|
|
continue;
|
|
} else {
|
|
break 2;
|
|
}
|
|
}
|
|
if (!preg_match('/^<lang lang="[a-zA-Z0-9_-]+"\s*>$/u', $match)) {
|
|
break 2;
|
|
} else {
|
|
$open = true;
|
|
}
|
|
}
|
|
if ($open) {
|
|
break;
|
|
}
|
|
return $param;
|
|
} else if (strpos((string)$param, '</span>') !== false) {
|
|
// Current problematic multilang syntax.
|
|
$param = strip_tags($param, '<span>');
|
|
if (!preg_match_all('/<.*>/suU', $param, $matches)) {
|
|
break;
|
|
}
|
|
$open = false;
|
|
foreach ($matches[0] as $match) {
|
|
if ($match === '</span>') {
|
|
if ($open) {
|
|
$open = false;
|
|
continue;
|
|
} else {
|
|
break 2;
|
|
}
|
|
}
|
|
if (!preg_match('/^<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>$/u', $match)) {
|
|
break 2;
|
|
} else {
|
|
$open = true;
|
|
}
|
|
}
|
|
if ($open) {
|
|
break;
|
|
}
|
|
return $param;
|
|
}
|
|
} while (false);
|
|
// Easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string().
|
|
return strip_tags((string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_COMPONENT.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_component(mixed $param): string {
|
|
// We do not want any guessing here, either the name is correct or not
|
|
// please note only normalised component names are accepted.
|
|
$param = (string)$param;
|
|
if (!preg_match('/^[a-z][a-z0-9]*(_[a-z][a-z0-9_]*)?[a-z0-9]+$/', $param)) {
|
|
return '';
|
|
}
|
|
if (strpos($param, '__') !== false) {
|
|
return '';
|
|
}
|
|
if (strpos($param, 'mod_') === 0) {
|
|
// Module names must not contain underscores because we need to differentiate them from invalid plugin types.
|
|
if (substr_count($param, '_') != 1) {
|
|
return '';
|
|
}
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_PLUGIN.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_plugin(mixed $param): string {
|
|
return $this->clean_param_value_area($param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_AREA.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_area(mixed $param): string {
|
|
// We do not want any guessing here, either the name is correct or not.
|
|
if (!is_valid_plugin_name($param)) {
|
|
return '';
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_SAFEDIR
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_safedir(mixed $param): string {
|
|
// Remove everything not a-zA-Z0-9_- .
|
|
return preg_replace('/[^a-zA-Z0-9_-]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_SAFEPATH.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_safepath(mixed $param): string {
|
|
// Remove everything not a-zA-Z0-9/_- .
|
|
return preg_replace('/[^a-zA-Z0-9\/_-]/i', '', (string)$param);
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_FILE.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_file(mixed $param): string {
|
|
// Strip all suspicious characters from filename.
|
|
$param = (string)fix_utf8($param);
|
|
// phpcs:ignore moodle.Strings.ForbiddenStrings.Found
|
|
$param = preg_replace('~[[:cntrl:]]|[&<>"`\|\':\\\\/]~u', '', $param);
|
|
if ($param === '.' || $param === '..') {
|
|
$param = '';
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_PATH.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_path(mixed $param): string {
|
|
// Strip all suspicious characters from file path.
|
|
$param = (string)fix_utf8($param);
|
|
$param = str_replace('\\', '/', $param);
|
|
|
|
// Explode the path and clean each element using the PARAM_FILE rules.
|
|
$breadcrumb = explode('/', $param);
|
|
foreach ($breadcrumb as $key => $crumb) {
|
|
if ($crumb === '.' && $key === 0) {
|
|
// Special condition to allow for relative current path such as ./currentdirfile.txt.
|
|
} else {
|
|
$crumb = clean_param($crumb, PARAM_FILE);
|
|
}
|
|
$breadcrumb[$key] = $crumb;
|
|
}
|
|
$param = implode('/', $breadcrumb);
|
|
|
|
// Remove multiple current path (./././) and multiple slashes (///).
|
|
$param = preg_replace('~//+~', '/', $param);
|
|
$param = preg_replace('~/(\./)+~', '/', $param);
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_HOST.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_host(mixed $param): string {
|
|
// Allow FQDN or IPv4 dotted quad.
|
|
if (!ip_utils::is_domain_name($param) && !ip_utils::is_ipv4_address($param)) {
|
|
$param = '';
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_URL.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_url(mixed $param): string {
|
|
global $CFG;
|
|
|
|
// Allow safe urls.
|
|
$param = (string)fix_utf8($param);
|
|
include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
|
|
if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E-u-P-a?I?p?f?q?r?')) {
|
|
// All is ok, param is respected.
|
|
} else {
|
|
// Not really ok.
|
|
$param = '';
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_LOCALURL.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_localurl(mixed $param): string {
|
|
global $CFG;
|
|
|
|
// Allow http absolute, root relative and relative URLs within wwwroot.
|
|
$param = clean_param($param, PARAM_URL);
|
|
if (!empty($param)) {
|
|
if ($param === $CFG->wwwroot) {
|
|
// Exact match.
|
|
} else if (preg_match(':^/:', $param)) {
|
|
// Root-relative, ok!
|
|
} else if (preg_match('/^' . preg_quote($CFG->wwwroot . '/', '/') . '/i', $param)) {
|
|
// Absolute, and matches our wwwroot.
|
|
} else {
|
|
// Relative - let's make sure there are no tricks.
|
|
if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?') && !preg_match('/javascript:/i', $param)) {
|
|
// Looks ok.
|
|
} else {
|
|
$param = '';
|
|
}
|
|
}
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_PEM.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_pem(mixed $param): string {
|
|
$param = trim((string)$param);
|
|
// PEM formatted strings may contain letters/numbers and the symbols:
|
|
// - forward slash: /
|
|
// - plus sign: +
|
|
// - equal sign: =
|
|
// - , surrounded by BEGIN and END CERTIFICATE prefix and suffixes.
|
|
if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
|
|
[$wholething, $body] = $matches;
|
|
unset($wholething, $matches);
|
|
$b64 = clean_param($body, PARAM_BASE64);
|
|
if (!empty($b64)) {
|
|
return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n";
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_BASE64.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_base64(mixed $param): string {
|
|
if (!empty($param)) {
|
|
// PEM formatted strings may contain letters/numbers and the symbols
|
|
// - forward slash: /
|
|
// - plus sign: +
|
|
// - equal sign: =.
|
|
if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) {
|
|
return '';
|
|
}
|
|
$lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
|
|
// Each line of base64 encoded data must be 64 characters in length, except for the last line which may be less
|
|
// than (or equal to) 64 characters long.
|
|
for ($i = 0, $j = count($lines); $i < $j; $i++) {
|
|
if ($i + 1 == $j) {
|
|
if (64 < strlen($lines[$i])) {
|
|
return '';
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (64 != strlen($lines[$i])) {
|
|
return '';
|
|
}
|
|
}
|
|
return implode("\n", $lines);
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_TAG.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_tag(mixed $param): string {
|
|
$param = (string)fix_utf8($param);
|
|
// Please note it is not safe to use the tag name directly anywhere,
|
|
// it must be processed with s(), urlencode() before embedding anywhere.
|
|
// Remove some nasties.
|
|
// phpcs:ignore moodle.Strings.ForbiddenStrings.Found
|
|
$param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
|
|
// Convert many whitespace chars into one.
|
|
$param = preg_replace('/\s+/u', ' ', $param);
|
|
$param = core_text::substr(trim($param), 0, TAG_MAX_LENGTH);
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_TAGLIST.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_taglist(mixed $param): string {
|
|
$param = (string)fix_utf8($param);
|
|
$tags = explode(',', $param);
|
|
$result = [];
|
|
foreach ($tags as $tag) {
|
|
$res = clean_param($tag, PARAM_TAG);
|
|
if ($res !== '') {
|
|
$result[] = $res;
|
|
}
|
|
}
|
|
if ($result) {
|
|
return implode(',', $result);
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_CAPABILITY.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_capability(mixed $param): string {
|
|
if (get_capability_info($param)) {
|
|
return $param;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_PERMISSION.
|
|
*
|
|
* @param mixed $param
|
|
* @return int
|
|
*/
|
|
protected function clean_param_value_permission(mixed $param): int {
|
|
$param = (int)$param;
|
|
if (in_array($param, [CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT])) {
|
|
return $param;
|
|
} else {
|
|
return CAP_INHERIT;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_AUTH.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_auth(mixed $param): string {
|
|
$param = clean_param($param, PARAM_PLUGIN);
|
|
if (empty($param)) {
|
|
return '';
|
|
} else if (exists_auth_plugin($param)) {
|
|
return $param;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_LANG.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_lang(mixed $param): string {
|
|
$param = clean_param($param, PARAM_SAFEDIR);
|
|
if (get_string_manager()->translation_exists($param)) {
|
|
return $param;
|
|
} else {
|
|
// Specified language is not installed or param malformed.
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_THEME.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_theme(mixed $param): string {
|
|
global $CFG;
|
|
|
|
$param = clean_param($param, PARAM_PLUGIN);
|
|
if (empty($param)) {
|
|
return '';
|
|
} else if (file_exists("$CFG->dirroot/theme/$param/config.php")) {
|
|
return $param;
|
|
} else if (!empty($CFG->themedir) && file_exists("$CFG->themedir/$param/config.php")) {
|
|
return $param;
|
|
} else {
|
|
// Specified theme is not installed.
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_USERNAME.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_username(mixed $param): string {
|
|
global $CFG;
|
|
|
|
$param = (string)fix_utf8($param);
|
|
$param = trim($param);
|
|
// Convert uppercase to lowercase MDL-16919.
|
|
$param = core_text::strtolower($param);
|
|
if (empty($CFG->extendedusernamechars)) {
|
|
$param = str_replace(" ", "", $param);
|
|
// Regular expression, eliminate all chars EXCEPT:
|
|
// alphanum, dash (-), underscore (_), at sign (@) and period (.) characters.
|
|
$param = preg_replace('/[^-\.@_a-z0-9]/', '', $param);
|
|
}
|
|
return $param;
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_EMAIL.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_email(mixed $param): string {
|
|
$param = fix_utf8($param);
|
|
if (validate_email($param ?? '')) {
|
|
return $param;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_STRINGID.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_stringid(mixed $param): string {
|
|
if (preg_match('|^[a-zA-Z][a-zA-Z0-9\.:/_-]*$|', (string)$param)) {
|
|
return $param;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation for PARAM_TIMEZONE.
|
|
*
|
|
* @param mixed $param
|
|
* @return string
|
|
*/
|
|
protected function clean_param_value_timezone(mixed $param): string {
|
|
// Can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'.
|
|
$param = (string)fix_utf8($param);
|
|
$timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3](\.0)?|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
|
|
if (preg_match($timezonepattern, $param)) {
|
|
return $param;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the parameter is deprecated.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_deprecated(): bool {
|
|
return deprecation::is_deprecated($this);
|
|
}
|
|
}
|