Compare commits

...

8 Commits

Author SHA1 Message Date
jf-cbd
95f4747006 Fix test 2026-10-05 17:13:52 +02:00
jf-cbd
b09740be7a Reformat 2026-10-05 17:11:11 +02:00
jf-cbd
50e6423765 Remove code 2026-10-05 16:54:18 +02:00
jf-cbd
cb6a66ae44 Strengthen CSP and add unit test 2026-10-05 16:54:11 +02:00
jf-cbd
3ab74174b8 Refactor 2026-10-05 16:00:03 +02:00
jf-cbd
663741f8fa Refactor 2026-10-05 15:08:11 +02:00
jf-cbd
31c2522448 Remove filtering method 2026-10-05 15:05:42 +02:00
jf-cbd
e65082a57c Add CSP 2026-10-05 15:02:46 +02:00
4 changed files with 45 additions and 17 deletions

View File

@@ -768,7 +768,7 @@ try {
// N°4129 - Prevent XSS attacks & other script executions
if (utils::GetConfig()->Get('security.disable_inline_documents_sandbox') === false) {
$oPage->add_header('Content-Security-Policy: sandbox;');
$oPage->add_header('Content-Security-Policy: sandbox; default-src \'none\'');
}
ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'inline');

View File

@@ -346,12 +346,4 @@ class AjaxPage extends WebPage implements iTabbedPage
{
assert(false);
}
/**
* @inheritDoc
*/
public static function FilterXSS($sHTML)
{
return str_ireplace(['<script', '</script>'], ['<!-- <removed-script', '</removed-script> -->'], $sHTML);
}
}

View File

@@ -47,15 +47,9 @@ class DownloadPage extends AjaxPage
header($s_header);
}
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline')) {
// inline content != attachment && html => filter all scripts for malicious XSS scripts
$sContent = self::FilterXSS($this->sContent);
} else {
$sContent = $this->sContent;
}
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sContent;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sContent) / 1024).' Kb)');
echo $this->sContent;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($this->sContent) / 1024).' Kb)');
ExecutionKPI::ReportStats();
}
}

View File

@@ -4,6 +4,7 @@ namespace Combodo\iTop\Test\UnitTest\Pages;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use Dict;
use ormDocument;
use UserLocal;
use UserRequest;
@@ -132,6 +133,47 @@ class AjaxRenderTest extends ItopDataTestCase
return $this->AcquireLockAsUser(self::$sLogin, self::$iTicketId);
}
public function testDisplayDocumentHasSandboxWhenConfigurationDoesNotDisableIt(): void
{
$sLogin = uniqid('AjaxRenderTest');
$this->CreateContactlessUser($sLogin, self::$aURP_Profiles['Administrator'], self::AUTHENTICATION_PASSWORD);
$iDocumentId = $this->GivenObjectInDB('DocumentFile', [
'name' => 'AjaxRenderTest_'.uniqid(),
'org_id' => $this->getTestOrgId(),
'file' => new ormDocument('<svg xmlns="http://www.w3.org/2000/svg"></svg>', 'image/svg+xml', 'test.svg'),
]);
$this->oiTopConfig->Set('security.disable_inline_documents_sandbox', false);
$this->SaveItopConfFile();
$sResponseHeaders = $this->GetDocumentResponseHeaders($sLogin, $iDocumentId);
$this->assertStringContainsString(
'sandbox',
$sResponseHeaders
);
}
private function GetDocumentResponseHeaders(string $sLogin, int $iDocumentId): string
{
$sResponse = $this->CallItopUri(
'pages/ajax.render.php?'.http_build_query([
'operation' => 'display_document',
'class' => 'DocumentFile',
'id' => $iDocumentId,
'field' => 'file',
'auth_user' => $sLogin,
'auth_pwd' => self::AUTHENTICATION_PASSWORD,
]),
[],
[
CURLOPT_HTTPHEADER => ['X-Combodo-Ajax:1'],
CURLOPT_HEADER => true,
CURLOPT_POST => 0,
]
);
return substr($sResponse, 0, $this->aLastCurlGetInfo['header_size']);
}
// Helper method to create a user with a specific profile
private function CreateUserWithProfile(int $iProfileId): UserLocal
{