$cnf, 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA]); openssl_pkey_export($k, $priv, null, ['config' => $cnf]); $pub = openssl_pkey_get_details($k)['key']; $zipPath = tempnam(sys_get_temp_dir(), 'th') . '.zip'; $z = new ZipArchive(); $z->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE); $z->addFromString('theme.css', "/*\n * Author: Studio\n * Version: 1.2.0\n */\n:root { --primary: #0a5; }"); $z->close(); $sha = hash_file('sha256', $zipPath); openssl_sign(plugin_signature_message('theme-nordlicht', '1.2.0', $sha), $sigRaw, $priv, OPENSSL_ALGO_SHA256); $sig = base64_encode($sigRaw); $entry = ['slug' => 'nordlicht', 'name' => 'Nordlicht', 'version' => '1.2.0', 'description' => 'Hell und ruhig', 'author' => 'Studio', 'downloadUrl' => 'https://plugins.example.de/dl/nordlicht-1.2.0.zip', 'sha256' => $sha, 'signature' => $sig]; $served = ['themes' => [$entry, ['slug' => 'kaputt', 'version' => 'x', 'sha256' => 'y'], array_merge($entry, ['slug' => 'httpdl', 'downloadUrl' => 'http://x.de/a.zip'])]]; $body = file_get_contents($zipPath); $GLOBALS['deskhub_http_get'] = function (string $url, int $max) use (&$served, &$body): array { if (str_contains($url, 'catalog.php')) return [200, json_encode($served), null]; return [200, $body, null]; }; $check(!theme_catalog_fetch()['ok'], 'ohne Katalog-Adresse gibt es einen Hinweis'); set_setting('theme_catalog_url', 'https://plugins.example.de/catalog.php?type=themes'); $cat = theme_catalog_fetch(); $check($cat['ok'] && count($cat['themes']) === 1 && $cat['themes'][0]['slug'] === 'nordlicht', 'Katalog wird gelesen, ungültige Einträge werden verworfen'); $r = theme_catalog_install('nordlicht'); $check(!$r['ok'] && str_contains($r['error'], 'kein Herausgeber-Schlüssel'), 'Signatur ohne hinterlegten Schlüssel kann nicht geprüft werden'); $check(!theme_catalog_install('gibtesnicht')['ok'], 'Theme außerhalb des Katalogs kann nicht installiert werden'); set_setting('plugin_publisher_key', $pub); $r = theme_catalog_install('nordlicht'); $check($r['ok'] && $r['signed'] === true, 'mit Herausgeber-Schlüssel wird die Signatur geprüft'); $again = theme_catalog_install('nordlicht'); $check(!$again['ok'] && str_contains($again['error'], 'bereits'), 'ein bereits installiertes Theme lässt sich nicht noch einmal installieren'); $state = theme_catalog_state($served['themes'][0]); $check($state['installed'] && !$state['update'] && $state['version'] === '1.2.0', 'Katalog erkennt das Theme als installiert (gleicher Name, gleiche Version)'); $check(!theme_catalog_state(['name' => 'Anderes Theme', 'version' => '1.0.0'])['installed'], 'ein nicht installiertes Theme wird nicht als installiert gezeigt'); $check(theme_catalog_state(['name' => 'Nordlicht', 'version' => '1.3.0'])['update'] === true, 'eine neuere Katalogversion wird als Update erkannt'); $themes = list_custom_themes(); $check(count($themes) === 1 && $themes[0]['name'] === 'Nordlicht' && is_file($themesDir . '/' . $themes[0]['id'] . '/theme.css'), 'Theme liegt im Theme-Ordner und wird aufgelistet'); $check($themes && $themes[0]['theme_version'] === '1.2.0' && $themes[0]['author'] === 'Studio', 'Version und Autor stammen aus theme.css'); $check(get_setting('active_custom_theme', '') === '', 'das Theme wird nicht automatisch aktiviert'); foreach (list_custom_themes() as $__t) delete_custom_theme($__t['id']); $served['themes'][0]['signature'] = base64_encode(random_bytes(256)); $r = theme_catalog_install('nordlicht'); $check(!$r['ok'] && str_contains($r['error'], 'Signatur ist ungültig'), 'ungültige Signatur wird abgelehnt'); // Plugin-Signatur darf nicht als Theme-Signatur durchgehen openssl_sign(plugin_signature_message('nordlicht', '1.2.0', $sha), $pluginSig, $priv, OPENSSL_ALGO_SHA256); $served['themes'][0]['signature'] = base64_encode($pluginSig); $check(!theme_catalog_install('nordlicht')['ok'], 'eine Plugin-Signatur gilt nicht für ein Theme'); $served['themes'][0]['signature'] = $sig; $body = file_get_contents($zipPath) . 'x'; // Download wurde verändert $r = theme_catalog_install('nordlicht'); $check(!$r['ok'] && str_contains($r['error'], 'Prüfsumme'), 'veränderter Download wird abgelehnt'); $body = file_get_contents($zipPath); $served['themes'][0]['signature'] = ''; set_setting('plugin_require_signature', '1'); $r = theme_catalog_install('nordlicht'); $check(!$r['ok'] && str_contains($r['error'], 'müssen signiert sein'), '„Signatur erforderlich“ lehnt unsignierte Themes ab'); set_setting('plugin_require_signature', '0'); $served['themes'][0]['downloadUrl'] = 'https://192.168.1.5/x.zip'; $check(!theme_catalog_install('nordlicht')['ok'], 'Download von interner Adresse wird abgelehnt'); unset($GLOBALS['deskhub_http_get']); $rm = function (string $d) use (&$rm) { foreach (scandir($d) ?: [] as $e) { if ($e === '.' || $e === '..') continue; is_dir("$d/$e") ? $rm("$d/$e") : @unlink("$d/$e"); } @rmdir($d); }; $rm($themesDir); @unlink($zipPath); @unlink($cnf); if ($failures) { fwrite(STDERR, "Fehlgeschlagen:\n - " . implode("\n - ", $failures) . "\n"); exit(1); } echo "Theme catalog checks passed.\n";