Commit 77e35128 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Fixes #12636. Implements FormElement.encoding = 'single tick'

parent 0d386f88
Pipeline #5285 passed with stages
in 3 minutes and 31 seconds
...@@ -21,6 +21,7 @@ use IMATHUZH\Qfq\Core\Helper\OnArray; ...@@ -21,6 +21,7 @@ use IMATHUZH\Qfq\Core\Helper\OnArray;
use IMATHUZH\Qfq\Core\Helper\Path; use IMATHUZH\Qfq\Core\Helper\Path;
use IMATHUZH\Qfq\Core\Helper\Sanitize; use IMATHUZH\Qfq\Core\Helper\Sanitize;
use IMATHUZH\Qfq\Core\Helper\Support; use IMATHUZH\Qfq\Core\Helper\Support;
use IMATHUZH\Qfq\Core\Helper\OnString;
use IMATHUZH\Qfq\Core\Report\Link; use IMATHUZH\Qfq\Core\Report\Link;
use IMATHUZH\Qfq\Core\Report\Report; use IMATHUZH\Qfq\Core\Report\Report;
use IMATHUZH\Qfq\Core\Store\Sip; use IMATHUZH\Qfq\Core\Store\Sip;
...@@ -862,6 +863,8 @@ abstract class AbstractBuildForm { ...@@ -862,6 +863,8 @@ abstract class AbstractBuildForm {
if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) { if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
// $value = htmlspecialchars_decode($value, ENT_QUOTES); // $value = htmlspecialchars_decode($value, ENT_QUOTES);
$value = Support::htmlEntityEncodeDecode(MODE_DECODE, $value); $value = Support::htmlEntityEncodeDecode(MODE_DECODE, $value);
} elseif ($formElement[FE_ENCODE] === FE_ENCODE_SINGLE_TICK) {
$value = OnString::escapeSingleTickInHtml($value);
} }
// Typically: $htmlElementNameIdZero = true // Typically: $htmlElementNameIdZero = true
......
...@@ -1332,6 +1332,7 @@ const TYPEAHEAD_PLACEHOLDER = '?'; ...@@ -1332,6 +1332,7 @@ const TYPEAHEAD_PLACEHOLDER = '?';
// Values // Values
const FE_ENCODE_SPECIALCHAR = 'specialchar'; const FE_ENCODE_SPECIALCHAR = 'specialchar';
const FE_ENCODE_SINGLE_TICK = 'single tick';
const FE_ENCODE_NONE = 'none'; const FE_ENCODE_NONE = 'none';
const FE_FILE_CAPTURE_CAMERA = 'camera'; const FE_FILE_CAPTURE_CAMERA = 'camera';
......
...@@ -204,6 +204,11 @@ $UPDATE_ARRAY = array( ...@@ -204,6 +204,11 @@ $UPDATE_ARRAY = array(
"ALTER TABLE `FormSubmitLog` ADD `formName` VARCHAR(255) NOT NULL DEFAULT '' AFTER `formId`;", "ALTER TABLE `FormSubmitLog` ADD `formName` VARCHAR(255) NOT NULL DEFAULT '' AFTER `formId`;",
], ],
'21.6.0' => [
"ALTER TABLE `FormElement` CHANGE `encode` `encode` ENUM('none','specialchar','single tick') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'specialchar';",
],
); );
......
...@@ -514,14 +514,13 @@ class OnString { ...@@ -514,14 +514,13 @@ class OnString {
* @param $line * @param $line
* @return string * @return string
*/ */
public static function escapeSingleTickInHtml($line) { public static function escapeSingleTickInHtml($line): string {
$flagTag = false; $flagTag = false;
$flagAttribute = false; $flagAttribute = false;
$flagAttributeStartSingleTick = false; $flagAttributeStartSingleTick = false;
$flagAttributeStartDoubleTick = false; $flagAttributeStartDoubleTick = false;
$flagAttributeInsideDoubleTick = false; $flagAttributeStarted = false;
$flagDoubleTickInAttribute = false; $flagDoubleTickInAttribute = false;
$flagAttibuteNoQuote = false;
$posStartSingleTick = null; $posStartSingleTick = null;
$new = ''; $new = '';
...@@ -536,8 +535,8 @@ class OnString { ...@@ -536,8 +535,8 @@ class OnString {
$flagDoubleTickInAttribute = false; $flagDoubleTickInAttribute = false;
$flagAttributeStartSingleTick = false; $flagAttributeStartSingleTick = false;
$flagAttributeStartDoubleTick = false; $flagAttributeStartDoubleTick = false;
$flagAttributeInsideDoubleTick = false; $flagAttributeStarted = false;
$flagAttibuteNoQuote = false; $flagDoubleTickInAttribute = false;
$posStartSingleTick = null; $posStartSingleTick = null;
break; break;
...@@ -554,61 +553,86 @@ class OnString { ...@@ -554,61 +553,86 @@ class OnString {
break; break;
case ' ': case ' ':
if ($flagTag && ($flagAttributeStartSingleTick || $flagAttributeInsideDoubleTick)) { // Space after start attribute and no quotes used, means: attribute ends here.
if ($flagTag && $flagAttribute && $flagAttributeStarted && !$flagAttributeStartSingleTick && !$flagAttributeStartDoubleTick) {
$flagAttribute = false; $flagAttribute = false;
$flagDoubleTickInAttribute = false; $flagDoubleTickInAttribute = false;
$flagAttributeStartSingleTick = false; $flagAttributeStartSingleTick = false;
$flagAttributeStartDoubleTick = false; $flagAttributeStartDoubleTick = false;
$flagAttributeInsideDoubleTick = false; $flagAttributeStarted = false;
$flagAttibuteNoQuote = false; $flagDoubleTickInAttribute = false;
$posStartSingleTick = null; $posStartSingleTick = null;
} }
break; break;
case "'": case "'":
if ($flagTag) { if ($flagTag) {
if ($flagAttribute && !$flagAttributeStartSingleTick && !$flagAttributeInsideDoubleTick) { if ($flagAttribute) {
$flagAttributeStartSingleTick = true; if (!$flagAttributeStartSingleTick && !$flagAttributeStartDoubleTick && !$flagAttributeStarted) {
$posStartSingleTick = strlen($new); // Attribute quoted by single tick.
break; // Remember position to later replace by double tick, if there is no double tick inside.
} $flagAttributeStartSingleTick = true;
$posStartSingleTick = strlen($new);
if ($flagAttribute && $flagAttributeStartSingleTick && !$flagDoubleTickInAttribute) { $flagAttributeStarted = true;
$new[$posStartSingleTick] = '"'; break;
$new .= '"'; }
$flagAttributeStartSingleTick = false;
continue 2; if ($flagAttributeStartSingleTick) {
} // Closing single tick. Attribute ends here.
$flagAttributeStartSingleTick = false;
if ($flagAttribute && !$flagAttributeStartSingleTick) { $flagAttribute = false;
$new .= '''; $flagAttributeStarted = false;
continue 2;
if (!$flagDoubleTickInAttribute) {
// No double tick found: single ticks can be replaced by double tick
$new[$posStartSingleTick] = '"';
$new .= '"';
continue 2;
}
$flagDoubleTickInAttribute = false;
break;
}
if ($flagAttributeStartDoubleTick) {
// Single tick inside a double tick quoted attribute: can be replaced
$new .= ''';
continue 2;
}
} }
} else { } else {
// regular content (no tag)
$new .= '''; $new .= ''';
continue 2; continue 2;
} }
break; break;
case '"': case '"':
if ($flagAttribute) { if ($flagTag) {
if (!$flagAttributeStartSingleTick && !$flagAttributeInsideDoubleTick) { if ($flagAttribute) {
$flagAttributeStartDoubleTick = true; if (!$flagAttributeStartSingleTick && !$flagAttributeStartDoubleTick && !$flagAttributeStarted) {
break; // Attribute starting with double tick.
} $flagAttributeStartDoubleTick = true;
if ($flagAttributeStartSingleTick) { $flagAttributeStarted = true;
$flagDoubleTickInAttribute = true; break;
break; }
if ($flagAttributeStartDoubleTick) {
// Attribute ending with double tick
$flagAttributeStartDoubleTick = false;
$flagAttribute = false;
$flagAttributeStarted = false;
break;
}
if ($flagAttributeStartSingleTick) {
$flagDoubleTickInAttribute = true;
break;
}
} }
} }
break; break;
default: default:
if ($flagAttribute) { if ($flagAttribute) {
if (!$flagAttributeStartSingleTick && !$flagAttributeInsideDoubleTick) { $flagAttributeStarted = true;
$flagAttibuteNoQuote = true;
break;
}
} }
} }
$new .= $c; $new .= $c;
......
...@@ -962,7 +962,7 @@ class Support { ...@@ -962,7 +962,7 @@ class Support {
case FE_TYPE_EDITOR: case FE_TYPE_EDITOR:
case FE_TYPE_TEXT: case FE_TYPE_TEXT:
if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR || $formElement[FE_ENCODE] === FE_ENCODE_SINGLE_TICK)
$checkType = SANITIZE_ALLOW_ALL; $checkType = SANITIZE_ALLOW_ALL;
break; break;
} }
......
...@@ -15,6 +15,7 @@ use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser; ...@@ -15,6 +15,7 @@ use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\Logger; use IMATHUZH\Qfq\Core\Helper\Logger;
use IMATHUZH\Qfq\Core\Helper\Sanitize; use IMATHUZH\Qfq\Core\Helper\Sanitize;
use IMATHUZH\Qfq\Core\Helper\Support; use IMATHUZH\Qfq\Core\Helper\Support;
use IMATHUZH\Qfq\Core\Helper\OnString;
/** /**
* Class FillStoreForm * Class FillStoreForm
...@@ -367,7 +368,10 @@ class FillStoreForm { ...@@ -367,7 +368,10 @@ class FillStoreForm {
if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) { if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
// $value = htmlspecialchars($value, ENT_QUOTES); // $value = htmlspecialchars($value, ENT_QUOTES);
$value = Support::htmlEntityEncodeDecode(MODE_ENCODE, $value); $value = Support::htmlEntityEncodeDecode(MODE_ENCODE, $value);
} elseif ($formElement[FE_ENCODE] === FE_ENCODE_SINGLE_TICK) {
$value = OnString::escapeSingleTickInHtml($value);
} }
} }
break; break;
} }
......
...@@ -77,7 +77,7 @@ CREATE TABLE IF NOT EXISTS `FormElement` ...@@ -77,7 +77,7 @@ CREATE TABLE IF NOT EXISTS `FormElement`
'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave',
'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail', 'paste') NOT NULL DEFAULT 'text', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail', 'paste') NOT NULL DEFAULT 'text',
`subrecordOption` SET ('edit', 'delete', 'new') NOT NULL DEFAULT '', `subrecordOption` SET ('edit', 'delete', 'new') NOT NULL DEFAULT '',
`encode` ENUM ('none', 'specialchar') NOT NULL DEFAULT 'specialchar', `encode` ENUM ('none', 'specialchar', 'single tick') NOT NULL DEFAULT 'specialchar',
`checkType` ENUM ('auto', 'alnumx', 'digit', 'numerical', 'email', 'pattern', 'allbut', `checkType` ENUM ('auto', 'alnumx', 'digit', 'numerical', 'email', 'pattern', 'allbut',
'all') NOT NULL DEFAULT 'auto', 'all') NOT NULL DEFAULT 'auto',
`checkPattern` VARCHAR(255) NOT NULL DEFAULT '', `checkPattern` VARCHAR(255) NOT NULL DEFAULT '',
......
...@@ -243,17 +243,22 @@ class OnStringTest extends TestCase { ...@@ -243,17 +243,22 @@ class OnStringTest extends TestCase {
$this->assertEquals("<b>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b>hel'lo</b>")); $this->assertEquals("<b>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b>hel'lo</b>"));
$this->assertEquals('<b>hel"lo</b>', OnString::escapeSingleTickInHtml('<b>hel"lo</b>')); $this->assertEquals('<b>hel"lo</b>', OnString::escapeSingleTickInHtml('<b>hel"lo</b>'));
$this->assertEquals('<b>hel&apos;l"o</b>', OnString::escapeSingleTickInHtml('<b>hel\'l"o</b>')); $this->assertEquals('<b>hel&apos;l"o</b>', OnString::escapeSingleTickInHtml('<b>hel\'l"o</b>'));
$this->assertEquals("<b title=test>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=test>hel'lo</b>")); $this->assertEquals("<b title=test>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=test>hel'lo</b>"));
$this->assertEquals("<b title = test > hel&apos;lo </b>", OnString::escapeSingleTickInHtml("<b title = test > hel'lo </b>"));
$this->assertEquals("<b title=\"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title='test'>hel'lo</b>")); $this->assertEquals("<b title=\"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title='test'>hel'lo</b>"));
$this->assertEquals("<b title = \"test\" > hel&apos;lo </b>", OnString::escapeSingleTickInHtml("<b title = 'test' > hel'lo </b>"));
$this->assertEquals("<b title=\"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"test\">hel'lo</b>")); $this->assertEquals("<b title=\"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"test\">hel'lo</b>"));
$this->assertEquals("<b title= \"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'test'>hel'lo</b>")); $this->assertEquals("<b title= \"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'test'>hel'lo</b>"));
$this->assertEquals("<b title= \"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"test\">hel'lo</b>")); $this->assertEquals("<b title= \"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"test\">hel'lo</b>"));
$this->assertEquals("<b title=\"test\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"test\" >hel'lo</b>")); $this->assertEquals("<b title=\"test\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"test\" >hel'lo</b>"));
$this->assertEquals("<b title= \"test\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"test\" >hel'lo</b>")); $this->assertEquals("<b title= \"test\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"test\" >hel'lo</b>"));
$this->assertEquals("<b title=\"te'st\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"te'st\">hel'lo</b>")); $this->assertEquals("<b title=\"te&apos;st\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"te'st\">hel'lo</b>"));
$this->assertEquals("<b title= \"te'st\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"te'st\">hel'lo</b>")); $this->assertEquals("<b title= \"te&apos;st\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"te'st\">hel'lo</b>"));
$this->assertEquals("<b title= \"te'st\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"te'st\" >hel'lo</b>")); $this->assertEquals("<b title= \"te&apos;st\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"te'st\" >hel'lo</b>"));
$this->assertEquals("<b title='te\"st'>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title='te\"st'>hel'lo</b>")); $this->assertEquals("<b title='te\"st'>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title='te\"st'>hel'lo</b>"));
$this->assertEquals("<b title= 'te\"st'>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'te\"st'>hel'lo</b>")); $this->assertEquals("<b title= 'te\"st'>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'te\"st'>hel'lo</b>"));
...@@ -268,10 +273,10 @@ class OnStringTest extends TestCase { ...@@ -268,10 +273,10 @@ class OnStringTest extends TestCase {
$this->assertEquals("<b src=\"gif\" title='te\"st' alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title='te\"st' alt=\"jpg\">hel'lo</b>")); $this->assertEquals("<b src=\"gif\" title='te\"st' alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title='te\"st' alt=\"jpg\">hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title='te\"st' alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title='te\"st' alt='jpg'>hel'lo</b>")); $this->assertEquals("<b src=\"gif\" title='te\"st' alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title='te\"st' alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=gif title=\"te'st\" alt=jpg>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=gif title=\"te'st\" alt=jpg>hel'lo</b>")); $this->assertEquals("<b src=gif title=\"te&apos;st\" alt=jpg>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=gif title=\"te'st\" alt=jpg>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"te'st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title=\"te'st\" alt='jpg'>hel'lo</b>")); $this->assertEquals("<b src=\"gif\" title=\"te&apos;st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title=\"te'st\" alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"te'st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title=\"te'st\" alt='jpg'>hel'lo</b>")); $this->assertEquals("<b src=\"gif\" title=\"te&apos;st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title=\"te'st\" alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"te'st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title=\"te'st\" alt=\"jpg\">hel'lo</b>")); $this->assertEquals("<b src=\"gif\" title=\"te&apos;st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title=\"te'st\" alt=\"jpg\">hel'lo</b>"));
} }
} }
\ No newline at end of file
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