import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; const read = (path) => readFile(new URL(`../${path}`, import.meta.url), 'utf8'); test('Roundcube SSO accepts the launch ticket from POST only', async () => { const plugin = await read('docker/roundcube/plugins/mailhub_sso/mailhub_sso.php'); assert.match(plugin, /REQUEST_METHOD[^\n]+POST/); assert.match(plugin, /get_input_string\('mailhub_ticket',\s*rcube_utils::INPUT_POST\)/); assert.doesNotMatch(plugin, /\$_GET\s*\[/); assert.doesNotMatch(plugin, /\$_REQUEST\s*\[/); assert.match(plugin, /'task'\]\s*=\s*'login'/); assert.match(plugin, /'action'\]\s*=\s*'login'/); }); test('Roundcube SSO keeps sensitive values out of output and error logging', async () => { const plugin = await read('docker/roundcube/plugins/mailhub_sso/mailhub_sso.php'); assert.doesNotMatch(plugin, /\b(?:echo|print|print_r|var_dump|error_log)\b/); assert.doesNotMatch(plugin, /CURLOPT_VERBOSE\s*=>\s*true/); assert.match(plugin, /CURLOPT_FOLLOWLOCATION\s*=>\s*false/); assert.match(plugin, /Authorization: Bearer /); assert.match(plugin, /'credential'\s*=>\s*\$credential/); assert.match(plugin, /add_hook\('session_destroy'/); assert.match(plugin, /mailhub_sso\.ssofailed/); assert.match(plugin, /array_fill_keys\(array_keys\(\$args\),\s*null\)/); assert.match(plugin, /\$redirect\['_task'\]\s*=\s*'mail'/); assert.match(plugin, /\$redirect\['_mbox'\]\s*=\s*'INBOX'/); assert.doesNotMatch(plugin, /X-MailHub-SSO-Diagnostic/i); }); test('Compose and examples use a dedicated file-backed Webmail SSO secret', async () => { const [compose, env, pluginConfig, docs] = await Promise.all([ read('docker-compose.yml'), read('.env.example'), read('docker/roundcube/plugins/mailhub_sso/config.inc.php.dist'), read('docs/webmail-sso.md'), ]); assert.match(compose, /WEBMAIL_SSO_SECRET_FILE:\s*\/data\/secrets\/webmail_sso_secret/); assert.doesNotMatch(compose, /webmail_sso_secret:\s*\n\s+file:/); assert.doesNotMatch(compose, /^\s{2}roundcube:/m); assert.match(env, /^WEBMAIL_SSO_URL=\s*$/m); assert.match(env, /^WEBMAIL_SSO_READER_GID=1000$/m); assert.match(env, /WEBMAIL_SSO_TICKET_TTL_SECONDS=60/); assert.match(env, /WEBMAIL_SSO_CREDENTIAL_TTL_SECONDS=43200/); assert.match(pluginConfig, /mailhub_sso_secret_file'\]\s*=\s*'\/run\/secrets\/webmail_sso_secret'/); assert.match(pluginConfig, /mailhub_sso_audience'\]\s*=\s*'https:\/\//); assert.match(docs, /不运行 Roundcube/); assert.match(docs, /WEBMAIL_SSO_READER_GID=33/); assert.match(docs, /PHP 工作进程的实际主组 GID/); assert.doesNotMatch(docs, /group_add:/); assert.match(docs, /in\.ss5\.xyz:host-gateway/); });