Steuerkram/tests/67_BelegXmlXsltXssTest.php

76 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
/**
* Test 67: Stored XSS über XSLT in hochgeladenen XML-Belegen ist geschlossen
* (BelegController::serveFile liefert XML-Belege als Download statt inline aus).
*/
class BelegXmlXsltXssTest extends TestCase
{
protected string $testName = 'Test 67: XML-Belege werden als Attachment statt inline ausgeliefert';
public function run(): void
{
$belegNr = \Steuerkram\Models\Beleg::generateBelegNr();
$year = date('Y');
$relDir = "{$year}/{$belegNr}";
$absDir = \Steuerkram\Config::get('upload_dir') . "/{$relDir}";
if (!is_dir($absDir)) {
mkdir($absDir, 0755, true);
}
$storagePath = "{$relDir}/original.xml";
$absPath = \Steuerkram\Config::get('upload_dir') . "/{$storagePath}";
$xmlPayload = <<<'XML'
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="#stylesheet"?>
<doc>
<xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html><body><script>alert(document.domain)</script></body></html>
</xsl:template>
</xsl:stylesheet>
</doc>
XML;
file_put_contents($absPath, $xmlPayload);
$belegId = \Steuerkram\Models\Beleg::create([
'beleg_nr' => $belegNr,
'filename' => 'rechnung.xml',
'storage_path' => $storagePath,
'mime_type' => 'text/xml',
'file_hash' => hash('sha256', $xmlPayload),
'beleg_datum' => date('Y-m-d'),
'rechnungs_nr' => null,
'betrag_brutto' => 100.00,
'belegtyp' => 'Eingangsrechnung',
'notes' => 'XSLT-XSS-Test',
]);
$controller = new \Steuerkram\Controllers\BelegController();
ob_start();
$controller->serveFile(['id' => (string)$belegId]);
ob_get_clean();
$headers = headers_list();
$disposition = '';
foreach ($headers as $header) {
if (stripos($header, 'Content-Disposition:') === 0) {
$disposition = $header;
}
}
$this->assertNotEmpty(
$disposition,
'Es wurde ein Content-Disposition-Header gesetzt'
);
$this->assert(
stripos($disposition, 'attachment') !== false,
"XML-Beleg wird als attachment statt inline ausgeliefert (erhalten: {$disposition})"
);
@unlink($absPath);
}
}