From 7e5307bd965340580d99a8e8af6006d7a650e1a0 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 16 Nov 2022 09:31:42 +0100 Subject: [PATCH 1/4] =?UTF-8?q?N=C2=B04867=20-=20"Twig=20content=20not=20a?= =?UTF-8?q?llowed"=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/twig/TwigTest.php | 46 ++++++++++++++++++++++--------- test/twig/attacker/backdoor | 1 + test/twig/test.html | 43 +++++++++++++++++++++-------- test/twig/test.html.twig | 54 ++++++++++++++++++++++++++++--------- 4 files changed, 108 insertions(+), 36 deletions(-) create mode 100644 test/twig/attacker/backdoor diff --git a/test/twig/TwigTest.php b/test/twig/TwigTest.php index 1457b30f6..f47bc10be 100644 --- a/test/twig/TwigTest.php +++ b/test/twig/TwigTest.php @@ -1,33 +1,55 @@ render($sFileName.'.twig'); - $this->assertSame($sHtml, $expected); + // Creating sandbox twig env. to load and test the custom form template + $oTwig = new Twig_Environment(new Twig_Loader_Array([$sId => $sFileName])); + + // Manually registering filters and functions as we didn't find how to do it automatically + $aFilters = $oAppExtension->getFilters(); + foreach ($aFilters as $oFilter) + { + $oTwig->addFilter($oFilter); + } + $aFunctions = $oAppExtension->getFunctions(); + foreach ($aFunctions as $oFunction) + { + $oTwig->addFunction($oFunction); + } + + $sHtml = $oTwig->render($sId, ['AttackerURL' => 'file://'.__DIR__.'/attacker']); + + $this->assertEquals($sExpected, $sHtml); } - public static function testTemplateProvider() + public static function TemplateProvider() { $aReturn = array(); $aReturn['filter_system'] = [ - 'sFileName' => 'test.html', - 'expected' =>file_get_contents(dirname(__FILE__).'/test.html'), + 'sFileName' => file_get_contents(__DIR__.'/test.html.twig'), + 'expected' => file_get_contents(__DIR__.'/test.html'), ]; return $aReturn; diff --git a/test/twig/attacker/backdoor b/test/twig/attacker/backdoor new file mode 100644 index 000000000..f56435e7a --- /dev/null +++ b/test/twig/attacker/backdoor @@ -0,0 +1 @@ +!!! BACKDOOR !!! \ No newline at end of file diff --git a/test/twig/test.html b/test/twig/test.html index 06cd232d3..2406bbfd4 100644 --- a/test/twig/test.html +++ b/test/twig/test.html @@ -1,12 +1,33 @@
- User Name -
-
- ["id"] -
-
- ["touch+\/tmp\/test+"] -
-
- 40, 42 -
\ No newline at end of file + User Name +
+ ['id']|filter('system') +
+ ["id"] +
+ ['touch+/tmp/test+']|filter('system')|join(',') +
+ ["touch+\/tmp\/test+"] +
+ set sizes = [34, 36, 38, 40, 42] + sizes|filter(v => v > 38)|join(', ') +
+ 40, 42 +
+ app.request.server.all|join(',') +
+ self +
+ [0]|reduce('system','echo') +
+ ["echo"] +
+ ['echo']|map('system')|join +
+ ["echo"] +
+ ['echo',1]|sort('system')|join +
+ echo1 + POST /subscribe?0=cat+/etc/passwd HTTP/1.1 + email=""@attacker.tld \ No newline at end of file diff --git a/test/twig/test.html.twig b/test/twig/test.html.twig index b2ae9dd5a..5825249c3 100644 --- a/test/twig/test.html.twig +++ b/test/twig/test.html.twig @@ -1,13 +1,41 @@ -
- {{ 'UI:Login:UserNamePrompt'|dict_s }} -
-
- {{['id']|filter('system')}} -
-
- {{['touch+/tmp/test+']|filter('system')|join(',')}} -
-
-{% set sizes = [34, 36, 38, 40, 42] %} - {{ sizes|filter(v => v > 38)|join(', ') }} -
\ No newline at end of file +{% spaceless %} +
+ {{ 'UI:Login:UserNamePrompt'|dict_s }} +
+
+ ['id']|filter('system') +
+ {{ ['id']|filter('system') }} +
+ ['touch+/tmp/test+']|filter('system')|join(',') +
+ {{ ['touch+/tmp/test+']|filter('system')|join(',') }} +
+ set sizes = [34, 36, 38, 40, 42] + sizes|filter(v => v > 38)|join(', ') +
+ {% set sizes = [34, 36, 38, 40, 42] %} + {{ sizes|filter(v => v > 38)|join(', ') }} +
+ app.request.server.all|join(',') +
+ {{ app.request.server.all|join(',') }} {# needs syfony #} +
+ self +
+ {{ self }} {# ??? not sure #} +
+ [0]|reduce('system','echo') +
+ {{ [0]|reduce('system','echo') }} +
+ ['echo']|map('system')|join +
+ {{ ['echo']|map('system')|join }} +
+ ['echo',1]|sort('system')|join +
+ {{ ['echo',1]|sort('system')|join }} + POST /subscribe?0=cat+/etc/passwd HTTP/1.1 + email="{{ app.request.query.filter(0,0,1024,{'options':'system'}) }}"@attacker.tld +{% endspaceless %} \ No newline at end of file From 68d44fa9818f644238922f904704ba71e513e769 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 16 Nov 2022 09:32:47 +0100 Subject: [PATCH 2/4] =?UTF-8?q?N=C2=B05724=20-=20code=20hardening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/ajax.render.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/ajax.render.php b/pages/ajax.render.php index f08fea0fa..17a666c0a 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -1352,7 +1352,7 @@ EOF $aParams = utils::ReadParam('params', '', false, 'raw_data'); $sDashletClass = $aParams['attr_dashlet_class']; $sDashletType = $aParams['attr_dashlet_type']; - $sDashletId = $aParams['attr_dashlet_id']; + $sDashletId = utils::HtmlEntities($aParams['attr_dashlet_id']); $aUpdatedProperties = $aParams['updated']; // Code of the changed properties as an array: 'attr_xxx', 'attr_xxy', etc... $aPreviousValues = $aParams['previous_values']; // hash array: 'attr_xxx' => 'old_value' if (is_subclass_of($sDashletClass, 'Dashlet')) From 2f8f0b658c513c002dc07ef35629e7d567d06ac1 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 16 Nov 2022 09:40:19 +0100 Subject: [PATCH 3/4] =?UTF-8?q?N=C2=B05722=20-=20code=20hardening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webservices/export-v2.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/webservices/export-v2.php b/webservices/export-v2.php index 688a3aefc..aa77c31ad 100644 --- a/webservices/export-v2.php +++ b/webservices/export-v2.php @@ -14,7 +14,6 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockF use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\TextArea; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; if (!defined('__DIR__')) { @@ -27,19 +26,15 @@ require_once(APPROOT.'/core/bulkexport.class.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); - - const EXIT_CODE_ERROR = -1; const EXIT_CODE_FATAL = -2; - - function ReportErrorAndExit($sErrorMessage) { if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Export"); - $oP->p('ERROR: '.$sErrorMessage); + $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage)); $oP->output(); exit(EXIT_CODE_ERROR); } @@ -47,7 +42,7 @@ function ReportErrorAndExit($sErrorMessage) { $oP = new WebPage("iTop - Export"); $oP->add_xframe_options(); - $oP->p('ERROR: '.$sErrorMessage); + $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage)); $oP->output(); exit(EXIT_CODE_ERROR); } From 282d47aed4860f6425a119c85d4daab49296f3a3 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 16 Nov 2022 13:51:38 +0100 Subject: [PATCH 4/4] =?UTF-8?q?N=C2=B05727=20-=20REST=20API/get=5Frelated:?= =?UTF-8?q?=20Seemingly=20wrong=20results,=20when=20using=20[impacts,=20up?= =?UTF-8?q?]=20with=20[redundancy:=20true]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dbobjectset.class.php | 3 ++- core/dbsearch.class.php | 2 +- core/restservices.class.inc.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index 57c2da786..6e70cbc3b 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -141,7 +141,7 @@ class DBObjectSet implements iDBObjectSetIterator { $sRet = ''; $this->Rewind(); - $sRet .= "Set (".$this->m_oFilter->ToOQL().")
\n"; + $sRet .= "Set (".$this->m_oFilter->ToOQL(true).")
\n"; $sRet .= "Query:
".$this->m_oFilter->MakeSelectQuery().")
\n"; $sRet .= $this->Count()." records
\n"; @@ -154,6 +154,7 @@ class DBObjectSet implements iDBObjectSetIterator } $sRet .= "\n"; } + $this->Rewind(); return $sRet; } diff --git a/core/dbsearch.class.php b/core/dbsearch.class.php index e0eb85cbe..fa1c99929 100644 --- a/core/dbsearch.class.php +++ b/core/dbsearch.class.php @@ -1717,6 +1717,6 @@ abstract class DBSearch */ public function __toString() { - return $this->ToOQL(); + return $this->ToOQL(true); } } diff --git a/core/restservices.class.inc.php b/core/restservices.class.inc.php index 518e8a8cd..2a6c1c830 100644 --- a/core/restservices.class.inc.php +++ b/core/restservices.class.inc.php @@ -574,7 +574,7 @@ class CoreServices implements iRestServiceProvider $oObject = $oElement->GetProperty('object'); if ($oObject) { - if ($bEnableRedundancy) + if ($bEnableRedundancy && $sDirection == 'down') { // Add only the "reached" objects if ($oElement->GetProperty('is_reached'))