Consolidate Persimmons source
This commit is contained in:
1 parent
534961db9b
commit
5fc3cd5795
1 file changed
+56
@@ -0,0 +1,56 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {createHash} from 'node:crypto';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import {initialize, unlock, encryptItem, decryptItem, isHttpsOrigin} from '../human-crypto.mjs';
|
||||
import {EXTENSION_ID} from '../identity.mjs';
|
||||
import {vaultRequest} from '../human-api.mjs';
|
||||
|
||||
test('deterministic unpacked ID agrees with the exact server Origin', async () => {
|
||||
const manifest = JSON.parse(await readFile(new URL('../manifest.json', import.meta.url)));
|
||||
const derived = createHash('sha256').update(Buffer.from(manifest.key, 'base64')).digest('hex').slice(0, 32)
|
||||
.replace(/[0-9a-f]/g, c => String.fromCharCode(97 + parseInt(c, 16)));
|
||||
assert.equal(derived, EXTENSION_ID);
|
||||
const server = await readFile(new URL('../../tea_mcp/human_vault.py', import.meta.url), 'utf8');
|
||||
assert.ok(server.includes(`EXTENSION_ORIGIN = "chrome-extension://${derived}"`));
|
||||
});
|
||||
|
||||
test('exact HTTPS origins are data while extension origin remains the caller boundary', () => {
|
||||
for (const origin of ['https://example.com','https://accounts.example.com','https://example.com:8443']) assert.equal(isHttpsOrigin(origin),true);
|
||||
for (const origin of ['http://example.com','https://example.com/path','https://user@example.com','not-a-url']) assert.equal(isHttpsOrigin(origin),false);
|
||||
});
|
||||
|
||||
test('local wrapping and authenticated ciphertext work for ordinary HTTPS sites', async () => {
|
||||
const passphrase = 'disposable test passphrase';
|
||||
const owner = 'fixture';
|
||||
const record = await initialize(passphrase, owner);
|
||||
assert.ok(!JSON.stringify(record).includes(passphrase));
|
||||
const key = await unlock(passphrase, owner, record);
|
||||
assert.equal(key.extractable, false);
|
||||
await assert.rejects(crypto.subtle.exportKey('raw', key));
|
||||
await assert.rejects(unlock('wrong passphrase', owner, record), /Unlock failed/);
|
||||
const account = {username: 'DISPOSABLE_USER_SENTINEL', password: 'DISPOSABLE_PASSWORD_SENTINEL'};
|
||||
const item = await encryptItem(key, owner, 'https://example.com', account);
|
||||
assert.deepEqual(await decryptItem(key, owner, item), account);
|
||||
assert.equal(item.username, account.username);
|
||||
assert.ok(!JSON.stringify(item).includes(account.password));
|
||||
const updated = await encryptItem(key, owner, item.origin, account, item.id);
|
||||
assert.notEqual(updated.iv, item.iv);
|
||||
assert.notEqual(updated.ciphertext, item.ciphertext);
|
||||
for (const bad of [{...item,id:'0'.repeat(32)},{...item,origin:'https://other.example'},{...item,v:2}])
|
||||
await assert.rejects(decryptItem(key, owner, bad), /failed authentication/);
|
||||
});
|
||||
|
||||
test('vault HTTP has no query or keys; session failures never echo bodies', async () => {
|
||||
await vaultRequest('GET', 'fixture', '1'.repeat(32), undefined, async (url, options) => {
|
||||
assert.equal(url, 'https://teahub.xyz/tea/site/chat/persimmons/human-vault/items/' + '1'.repeat(32) + '/read');
|
||||
assert.equal(options.method, 'POST');
|
||||
assert.equal(options.credentials, 'include');
|
||||
assert.equal(options.headers['X-Vault-Owner'], 'fixture');
|
||||
return new Response(JSON.stringify({owner: 'fixture'}), {headers: {'Content-Type': 'application/json'}});
|
||||
});
|
||||
for (const status of [401,403,500]) await assert.rejects(
|
||||
vaultRequest('GET', undefined, undefined, undefined, async()=>new Response('PRIVATE_SENTINEL',{status})),
|
||||
/Vault unavailable/
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user