Files
app/extension/tests/core.test.mjs
T

73 lines
4.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {readFile, readdir} from 'node:fs/promises';
import {pageTarget, metadata, publicSource, request, DEFAULT_SCOPES, BASE} from '../core.mjs';
const target = pageTarget('https://wp.phly.cloud/wp-admin/');
const response = (data, status = 200, type = 'application/json') => new Response(JSON.stringify(data), {status, headers: {'content-type': type}});
test('HTTPS origin only; no path/query exposed as origin', () => {
assert.equal(target.origin, 'https://wp.phly.cloud');
assert.deepEqual(pageTarget('https://wp.phly.cloud/private?token=example#fragment'), {origin: 'https://wp.phly.cloud'});
for (const url of ['http://example.org', 'chrome://extensions', 'file:///tmp/a', 'https://user:pass@example.org']) assert.throws(() => pageTarget(url));
});
test('My vault metadata does not pretend to attenuate provider authority', () => {
const meta = metadata({origin: target.origin, name: 'Site session', scopes: DEFAULT_SCOPES, minutes: 60}, 0);
assert.equal(meta.vault_kind, 'user');
assert.equal(meta.expires, undefined);
assert.deepEqual(meta.scopes, ['content:read', 'content:draft-write']);
assert.equal(meta.credential_type, 'api-token');
assert.throws(() => metadata({...meta, origin: target.origin, scopes: ['admin'], minutes: 60}));
});
test('public source projection retains id/name only, rejects repository context', () => {
assert.deepEqual(publicSource({id: 'ABC123', name: 'Site', vault_kind: 'user', credential: 'SENSITIVE_FIXTURE'}), {id: 'ABC123', name: 'Site'});
assert.throws(() => publicSource({id: 'ABC123', name: 'Site', vault_kind: 'repository'}));
});
test('exact create route, browser authority and safe request settings', async () => {
await request('create', {vault_kind: 'user', credential: 'SENSITIVE_FIXTURE'}, async (url, options) => {
assert.equal(url, `${BASE}/create`);
assert.equal(options.credentials, 'include');
assert.equal(options.redirect, 'error');
assert.equal(options.cache, 'no-store');
assert.equal(options.method, 'POST');
assert.equal(JSON.parse(options.body).vault_kind, 'user');
return response({id: 'ABC123', name: 'Site', vault_kind: 'user'});
});
for (const path of ['juice/create', 'juice/revoke']) await request(path, {vault_kind: 'user'}, async url => {
assert.equal(url, `${BASE}/${path}`); return response({ok: true});
});
});
test('missing Juice, rejected auth, redirects/HTML, echoed secrets and timeouts are safe', async () => {
for (const [status, expected] of [[404, /Juice unavailable/], [503, /Juice unavailable/], [401, /Sign in/], [403, /Sign in/]]) {
await assert.rejects(request('juice/create', {}, async () => response({error: 'SENSITIVE_FIXTURE'}, status)), expected);
}
await assert.rejects(request('list', undefined, async () => response('SENSITIVE_FIXTURE', 200, 'text/html')), /expected JSON/);
await assert.rejects(request('create', {}, async () => response({error: 'SENSITIVE_FIXTURE'})), /did not confirm/);
await assert.rejects(request('create', {}, async () => { throw new Error('SENSITIVE_FIXTURE'); }), /unreachable or timed out/);
let called = false;
await assert.rejects(request('create', {credential: '\\'.repeat(16384)}, async () => { called = true; }), /24 KiB/);
assert.equal(called, false);
});
test('legacy token module still has no persistence or injection; MV3 exposure is bounded', async () => {
const root = new URL('../', import.meta.url);
const manifest = JSON.parse(await readFile(new URL('manifest.json', root), 'utf8'));
assert.equal(manifest.manifest_version, 3);
assert.deepEqual(manifest.permissions, ['activeTab', 'scripting', 'storage']);
assert.deepEqual(manifest.host_permissions, ['https://teahub.xyz/*']);
assert.deepEqual(manifest.optional_host_permissions, ['https://persimmons.app/*']);
assert.equal(manifest.content_scripts, undefined);
assert.deepEqual(manifest.background, {service_worker:'connection.mjs',type:'module'});
assert.equal(manifest.web_accessible_resources, undefined);
assert.deepEqual(manifest.externally_connectable, {matches:['https://persimmons.app/*']});
for (const name of await readdir(root)) {
if (!/\.(mjs|html)$/.test(name)) continue;
const source = await readFile(new URL(name, root), 'utf8');
assert.doesNotMatch(source, /console\.|chrome\.cookies|localStorage|sessionStorage|indexedDB|innerHTML|eval\(/);
if (['core.mjs', 'popup.mjs'].includes(name)) assert.doesNotMatch(source, /chrome\.scripting|chrome\.storage/);
}
const html = await readFile(new URL('popup.html', root), 'utf8');
assert.match(html, /human-popup\.mjs/);
assert.doesNotMatch(html, /juice-panel|value="content:read"|value="content:draft-write"/);
for (const scope of ['manage:users', 'billing', 'destructive']) assert.doesNotMatch(html, new RegExp(`value="${scope}" checked`));
});