76 lines
2.5 KiB
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);
|
|
}
|
|
}
|