assert(file_exists($path), 'scripts/analyse_nextcloud.sh existiert'); $this->assert(is_executable($path), 'scripts/analyse_nextcloud.sh ist ausführbar'); exec('bash -n ' . escapeshellarg($path) . ' 2>&1', $output, $exitCode); $this->assertEquals(0, $exitCode, 'bash-Syntax ist gültig (' . implode(' ', $output) . ')'); $content = (string)file_get_contents($path); // Rein lesend: keine schreibenden WebDAV-Methoden, kein Importlauf. foreach (['MOVE', 'DELETE', 'PUT', 'MKCOL'] as $method) { $this->assert( !str_contains($content, "'" . $method . "'") && !str_contains($content, '"' . $method . '"'), 'Skript verwendet keine schreibende WebDAV-Methode ' . $method ); } // Verweise auf NextcloudImportService im Kommentar sind erwünscht (die cURL-Optionen // müssen dort synchron gehalten werden) – ausgeführt werden darf der Import nicht. $this->assert( !str_contains($content, 'nextcloud-import.php') && !str_contains($content, 'new NextcloudImportService'), 'Skript startet keinen Importlauf' ); // Das Passwort dient ausschließlich als cURL-Zugangsdatum. In Ausgaben darf // es nur als Länge (strlen) auftauchen, nie im Klartext. foreach (explode("\n", $content) as $index => $line) { if (!str_contains($line, '$pass') || !preg_match('/\b(echo|print|printf|sprintf|print_r|var_export)\b/', $line)) { continue; } $this->assert( !str_contains(str_replace('strlen($pass)', '', $line), '$pass'), 'Passwort erscheint in Ausgabe-Zeile ' . ($index + 1) . ' höchstens als Länge' ); } // Die Analyse muss den echten SSRF-/Pinning-Pfad verwenden, sonst prüft sie // etwas anderes als der Importlauf. $this->assert( str_contains($content, 'SsrfGuard::resolvePinned'), 'Skript prüft das IP-Pinning über SsrfGuard::resolvePinned()' ); $this->assert( str_contains($content, 'PROPFIND'), 'Skript unternimmt einen PROPFIND-Verbindungsversuch' ); } }