Commit 4f52831e authored by Carsten  Rose's avatar Carsten Rose
Browse files

Refs #7743. Detection of 'pattern error' on per QFQ default, custom instance...

Refs #7743. Detection of 'pattern error' on per QFQ default, custom instance wide, per form or per FormElement - per FormElement overwrites other. System specific generated overwrites QFQ static default, instance wide default. Move default pattern to constants.
parent 8c117f3c
Pipeline #1413 passed with stage
in 2 minutes and 22 seconds
......@@ -89,6 +89,15 @@ const SANITIZE_EMPTY_STRING = 'empty string';
const SANITIZE_VIOLATE = '!!';
const PATTERN_ALNUMX = '^[@\-_\.,;: \/\(\)a-zA-Z0-9ÀÈÌÒÙàèìòùÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÃÑÕãñõÄËÏÖÜŸäëïöüÿç]*$';
const PATTERN_DIGIT = '^[\d]*$';
const PATTERN_NUMERICAL = '^[\d.+-]*$';
const PATTERN_EMAIL = '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$';
const PATTERN_ALLBUT = '^[^\[\]{}%\\\\#]*$';
const PATTERN_ALL = '.*';
// Index wrap setup table
const WRAP_SETUP_TITLE = 'title';
const WRAP_SETUP_ELEMENT = 'element';
......@@ -167,13 +176,13 @@ const ERROR_PATTERN_VIOLATION = 1043;
const ERROR_RECORDID_0_FORBIDDEN = 1044;
const ERROR_LOG_NOT_WRITABLE = 1045;
const ERROR_UNNOWN_STORE = 1046;
const ERROR_GET_STORE_ZERO = 1047;
const ERROR_GET_INVALID_STORE = 1047;
const ERROR_SET_STORE_ZERO = 1048;
const ERROR_INVALID_OR_MISSING_PARAMETER = 1050;
const ERROR_UNKNOWN_SQL_LOG_MODE = 1051;
const ERROR_FORM_NOT_FOUND = 1052;
const ERROR_DATE_TIME_FORMAT_NOT_RECOGNISED = 1053;
const ERROR_SANATIZE_INVALID_VALUE = 1054;
const ERROR_SANITIZE_INVALID_VALUE = 1054;
const ERROR_REQUIRED_VALUE_EMPTY = 1055;
const ERROR_DATE_UNEXPECTED_FORMAT = 1056;
const ERROR_UNEXPECTED_TYPE = 1057;
......@@ -886,6 +895,8 @@ const F_RECORD_LOCK_TIMEOUT_SECONDS = SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS;
const F_SESSION_TIMEOUT_SECONDS = SYSTEM_SESSION_TIMEOUT_SECONDS;
const F_FE_DATA_PATTERN_ERROR = 'data-pattern-error';
const F_FE_DATA_PATTERN_ERROR_SYSTEM = 'data-pattern-error-system'; // only used to determine final text
const F_FE_DATA_REQUIRED_ERROR = 'data-required-error';
const F_FE_DATA_MATCH_ERROR = 'data-match-error'; // contains id of the sibling input to check that i
const F_FE_DATA_ERROR = 'data-error';
......
......@@ -901,6 +901,7 @@ class QuickFormQuery {
} else {
$form[F_DB_INDEX] = $this->evaluate->parse($form[F_DB_INDEX]);
}
if (empty($form[F_PRIMARY_KEY])) {
$form[F_PRIMARY_KEY] = F_PRIMARY_KEY_DEFAULT;
}
......@@ -1221,6 +1222,8 @@ class QuickFormQuery {
FE_INPUT_EXTRA_BUTTON_INFO_CLASS,
F_SHOW_ID_IN_FORM_TITLE,
F_FE_DATA_PATTERN_ERROR_SYSTEM, // Not a classical element to overwrite by form definition, but should be copied to detect changes per custom setting.
];
// By definition: existing vars which are empty, means: EMPTY - do not use any default!
......@@ -1235,13 +1238,15 @@ class QuickFormQuery {
$formSpec[F_FE_LABEL_ALIGN] = $this->store->getVar(SYSTEM_LABEL_ALIGN, STORE_SYSTEM . STORE_EMPTY);
}
$storeSystem=$this->store::getStore(STORE_SYSTEM);
foreach ($keys as $key) {
if (isset($formSpec[$key])) {
$this->store->setVar($key, $formSpec[$key], STORE_SYSTEM);
} else {
// if not found set ''
$formSpec[$key] = $this->store->getVar($key, STORE_SYSTEM . STORE_EMPTY);
$formSpec[$key] = $storeSystem[$key]??'';
}
}
......
......@@ -254,6 +254,9 @@ class HelperFormElement {
// Iterate over all FormElement
foreach ($feSpecNative as $key => $element) {
$feSpecNative[$key][F_FE_DATA_PATTERN_ERROR_SYSTEM] = $formSpec[F_FE_DATA_PATTERN_ERROR_SYSTEM];
Support::setIfNotSet($feSpecNative[$key], F_FE_DATA_PATTERN_ERROR, $formSpec[F_FE_DATA_PATTERN_ERROR]);
Support::setIfNotSet($feSpecNative[$key], F_FE_DATA_REQUIRED_ERROR, $formSpec[F_FE_DATA_REQUIRED_ERROR]);
Support::setIfNotSet($feSpecNative[$key], F_FE_DATA_MATCH_ERROR, $formSpec[F_FE_DATA_MATCH_ERROR]);
......
......@@ -23,13 +23,13 @@ require_once(__DIR__ . '/../../core/Constants.php');
class Sanitize {
private static $sanitizePattern = [
SANITIZE_ALLOW_ALNUMX => '^[@\-_\.,;: \/\(\)a-zA-Z0-9ÀÈÌÒÙàèìòùÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÃÑÕãñõÄËÏÖÜŸäëïöüÿç]*$', // ':alnum:' does not work here in FF
SANITIZE_ALLOW_DIGIT => '^[\d]*$',
SANITIZE_ALLOW_NUMERICAL => '^[\d.+-]*$',
SANITIZE_ALLOW_EMAIL => '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
SANITIZE_ALLOW_ALNUMX => PATTERN_ALNUMX, // ':alnum:' does not work here in FF
SANITIZE_ALLOW_DIGIT => PATTERN_DIGIT,
SANITIZE_ALLOW_NUMERICAL => PATTERN_NUMERICAL,
SANITIZE_ALLOW_EMAIL => PATTERN_EMAIL,
SANITIZE_ALLOW_ALLBUT => PATTERN_ALLBUT,
SANITIZE_ALLOW_ALL => PATTERN_ALL,
SANITIZE_ALLOW_PATTERN => '',
SANITIZE_ALLOW_ALLBUT => '^[^\[\]{}%\\\\#]*$',
SANITIZE_ALLOW_ALL => '.*',
];
private static $sanitizeMessage = [
......@@ -37,9 +37,9 @@ class Sanitize {
SANITIZE_ALLOW_DIGIT => 'Allowed characters: 0...9',
SANITIZE_ALLOW_NUMERICAL => 'Allowed characters: 0...9 and .+-',
SANITIZE_ALLOW_EMAIL => 'Requested format: string@domain.tld',
SANITIZE_ALLOW_PATTERN => 'Please match the requested format',
SANITIZE_ALLOW_ALLBUT => 'Forbidden characters: ^[]{}%\#',
SANITIZE_ALLOW_ALL => '',
SANITIZE_ALLOW_PATTERN => 'Please match the requested format',
];
......@@ -56,13 +56,14 @@ class Sanitize {
* @param string $pattern Pattern as regexp
* @param string $decimalFormat with 'size,precision'
* @param string $mode SANITIZE_EXCEPTION | SANITIZE_EMPTY_STRING
* @param string $dataPatternErrorText
* @return string
* @throws CodeException
* @throws UserFormException
*/
public static function sanitize($value, $sanitizeClass = SANITIZE_DEFAULT, $pattern = '', $decimalFormat = '', $mode = SANITIZE_EMPTY_STRING) {
public static function sanitize($value, $sanitizeClass = SANITIZE_DEFAULT, $pattern = '', $decimalFormat = '', $mode = SANITIZE_EMPTY_STRING, $dataPatternErrorText = '') {
$pattern = self::getInputCheckPattern($sanitizeClass, $pattern, $decimalFormat, $dummy);
$pattern = self::getInputCheckPattern($sanitizeClass, $pattern, $decimalFormat, $sanitizeMessage);
// Pattern check
if ($pattern === '' || preg_match("/$pattern/", $value) === 1) {
......@@ -72,7 +73,18 @@ class Sanitize {
// check failed
if ($mode === SANITIZE_EXCEPTION) {
$errorCode = ERROR_PATTERN_VIOLATION;
$errorText = "Value '$value' violates checkrule " . $sanitizeClass . " with pattern '$pattern'.";
// Depending on default regexp & custom data-pattern-error, define error text.
if ($dataPatternErrorText == '') {
if ($sanitizeMessage == '') {
$errorText = "Value '$value' violates check rule " . $sanitizeClass . " with pattern '$pattern'.";
} else {
$errorText = $sanitizeMessage;
}
} else {
$errorText = $dataPatternErrorText;
}
throw new UserFormException($errorText, $errorCode);
}
......@@ -81,7 +93,7 @@ class Sanitize {
/**
* Returns the final validation pattern based on a given $checkType, $pattern, and $decimalFormat
* @param string $checkType
* @param string $checkType SANITIZE_DEFAULT, ...
* @param string $pattern
* @param string $decimalFormat e.g. "10,2"
*
......@@ -89,7 +101,9 @@ class Sanitize {
* @return string
* @throws CodeException
*/
public static function getInputCheckPattern($checkType = SANITIZE_DEFAULT, $pattern = '', $decimalFormat = '', &$rcSanitizeMessage) {
public static function getInputCheckPattern($checkType, $pattern, $decimalFormat, &$rcSanitizeMessage) {
$rcSanitizeMessage = self::$sanitizeMessage[$checkType] ?? '';
switch ($checkType) {
case SANITIZE_ALLOW_PATTERN:
......@@ -111,8 +125,6 @@ class Sanitize {
throw new CodeException("Unknown checkType: " . $checkType, ERROR_UNKNOWN_CHECKTYPE);
}
$rcSanitizeMessage = self::$sanitizeMessage[$checkType];
// decimalFormat
if ($decimalFormat != '' && $checkType !== SANITIZE_ALLOW_DIGIT) {
// overwrite pattern with decimalFormat pattern
......@@ -252,7 +264,6 @@ class Sanitize {
return $item;
}
/**
* Check a given $_GET[$key] is digit.
......
......@@ -204,12 +204,13 @@ class Support {
/**
* Format's an attribute: $type=$value. If $flagOmitEmpty==true && $value=='': return ''.
* Escape double tick - assumes that attributes will always be enclosed by double ticks.
* Add's a space at the end.
*
* @param string $type
* @param string|array $value
* @param bool $flagOmitEmpty
* @param string $modeEscape
* @param bool $flagOmitEmpty true|false
* @param string $modeEscape ESCAPE_WITH_BACKSLASH | ESCAPE_WITH_HTML_QUOTE
*
* @return string correctly formatted attribute. Space at the end.
* @throws CodeException
......@@ -798,7 +799,7 @@ class Support {
self::setIfNotSet($formElement, FE_HTML_BEFORE);
self::setIfNotSet($formElement, FE_HTML_AFTER);
self::setIfNotSet($formElement, FE_DATA_REFERENCE, ($formElement[FE_NAME]=='' ? $formElement[FE_ID] : $formElement[FE_NAME]) );
self::setIfNotSet($formElement, FE_DATA_REFERENCE, ($formElement[FE_NAME] == '' ? $formElement[FE_ID] : $formElement[FE_NAME]));
self::setIfNotSet($formElement, FE_SUBRECORD_TABLE_CLASS, SUBRECORD_TABLE_CLASS_DEFAULT);
......@@ -965,12 +966,12 @@ class Support {
}
// If nothing given, set default
if ( $formElement[FE_MIN]=='' ){
if ($formElement[FE_MIN] == '') {
$formElement[FE_MIN] = $min;
}
// If nothing given, set default
if ( $formElement[FE_MAX]==''){
if ($formElement[FE_MAX] == '') {
$formElement[FE_MAX] = $max;
}
......@@ -989,8 +990,8 @@ class Support {
}
// If min or max is set and if there is the standard error text given, define a more detailed error text if the value is outside.
if( ($formElement[FE_MIN]!='' ||$formElement[FE_MAX]!='') && $formElement[F_FE_DATA_ERROR]==F_FE_DATA_ERROR_DEFAULT ){
$formElement[F_FE_DATA_ERROR]=F_FE_DATA_ERROR_DEFAULT . ' - value to low / high';
if (($formElement[FE_MIN] != '' || $formElement[FE_MAX] != '') && ($formElement[F_FE_DATA_ERROR]??'') == F_FE_DATA_ERROR_DEFAULT) {
$formElement[F_FE_DATA_ERROR] = F_FE_DATA_ERROR_DEFAULT . ' - allowed values: ' . $formElement[FE_MIN] . '...' . $formElement[FE_MAX];
}
}
......@@ -1268,7 +1269,7 @@ class Support {
} else {
throw new UserFormException(json_encode(
[ERROR_MESSAGE_TO_USER => 'Copy upload failed - file already exist',
ERROR_MESSAGE_SUPPORT => 'File: ' .$pathFileName]), ERROR_IO_FILE_EXIST);
ERROR_MESSAGE_SUPPORT => 'File: ' . $pathFileName]), ERROR_IO_FILE_EXIST);
}
}
......
......@@ -148,6 +148,9 @@ class Config {
self::checkForAttack($config);
// Copy values to detect custom settings later
$config[F_FE_DATA_PATTERN_ERROR_SYSTEM]=$config[F_FE_DATA_PATTERN_ERROR];
return $config;
}
......@@ -356,6 +359,7 @@ class Config {
F_FE_DATA_REQUIRED_ERROR => F_FE_DATA_REQUIRED_ERROR_DEFAULT,
F_FE_DATA_MATCH_ERROR => F_FE_DATA_MATCH_ERROR_DEFAULT,
F_FE_DATA_ERROR => F_FE_DATA_ERROR_DEFAULT,
F_FE_DATA_PATTERN_ERROR => F_FE_DATA_PATTERN_ERROR_DEFAULT,
SYSTEM_FLAG_PRODUCTION => 'yes',
SYSTEM_THROW_GENERAL_ERROR => 'auto',
......
......@@ -308,7 +308,7 @@ class FillStoreForm {
// Check only if there is something.
if ($val !== '' && $formMode != FORM_UPDATE && $formElement[FE_MODE] != FE_MODE_HIDDEN) {
$val = Sanitize::sanitize($val, $formElement[FE_CHECK_TYPE], $formElement[FE_CHECK_PATTERN],
$formElement[FE_DECIMAL_FORMAT], SANITIZE_EXCEPTION);
$formElement[FE_DECIMAL_FORMAT], SANITIZE_EXCEPTION, $formElement[F_FE_DATA_PATTERN_ERROR]??'');
if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
// $val = htmlspecialchars($val, ENT_QUOTES);
......
......@@ -423,7 +423,7 @@ class Store {
/**
* Returns a complete $store.
*
* @param $store
* @param string $store STORE_SYSTEM, ...
*
* @return array
* @throws UserFormException
......@@ -432,13 +432,13 @@ class Store {
public static function getStore($store) {
$vars = array();
// Check valid Storename
// Check valid store name
if (!isset(self::$sanitizeStore[$store])) {
throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
}
if ($store === STORE_ZERO) {
throw new CodeException("getStore() for STORE_ZERO is impossible - there are no values saved.", ERROR_GET_STORE_ZERO);
if ($store === STORE_ZERO || $store === STORE_EMPTY) {
throw new CodeException("getStore() for $store is impossible - there are no values saved.", ERROR_GET_INVALID_STORE);
}
if (isset(self::$raw[$store])) {
......
......@@ -382,7 +382,7 @@ class StoreTest extends TestCase {
F_BS_NOTE_COLUMNS => 'col-md-3 col-lg-3',
F_FE_DATA_REQUIRED_ERROR => 'data required',
F_FE_DATA_MATCH_ERROR => 'fields do not match',
F_FE_DATA_ERROR => 'error',
F_FE_DATA_ERROR => F_FE_DATA_ERROR_DEFAULT,
F_CLASS_PILL => 'qfq-color-grey-1',
F_CLASS_BODY => 'qfq-color-grey-2',
F_BUTTON_ON_CHANGE_CLASS => 'btn-info alert-info',
......@@ -401,6 +401,8 @@ class StoreTest extends TestCase {
'LDAP_1_RDN' => 'LDAP_1_RDN',
'LDAP_1_PASSWORD' => 'LDAP_1_PASSWORD',
SYSTEM_LABEL_ALIGN => SYSTEM_LABEL_ALIGN_LEFT,
F_FE_DATA_PATTERN_ERROR=> F_FE_DATA_PATTERN_ERROR_DEFAULT,
F_FE_DATA_PATTERN_ERROR_SYSTEM=> F_FE_DATA_PATTERN_ERROR_DEFAULT,
];
$body = <<< EOT
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment