Files
qinglong/packages/ql3-cluster-control/test/tool-result-projected-keyring.test.cjs

194 lines
5.6 KiB
JavaScript

const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const { afterEach, test } = require('node:test');
const {
CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA,
ClusterToolResultProjectedKeyring,
ClusterToolResultProjectedKeyringUnavailableError,
canonicalClusterToolResultKeyringManifest,
createClusterToolResultProjectedKeyring,
} = require('../dist/trusted-tool/key-management/toolResultProjectedKeyring.js');
const {
toolResultKeyMaterialProof,
} = require('@qinglong/runtime-core/tool-result-key-catalog');
const roots = [];
afterEach(async () => {
await Promise.all(
roots
.splice(0)
.map((root) => fs.rm(root, { recursive: true, force: true })),
);
});
function manifest(keys) {
return Object.freeze({
schema: CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA,
keys: Object.freeze(keys),
});
}
async function tempRoot() {
const root = await fs.mkdtemp(
path.join(os.tmpdir(), 'ql3-tool-result-keyring-'),
);
roots.push(root);
return root;
}
async function publish(root, generationName, bytes, mode = 0o440) {
const generation = path.join(root, generationName);
await fs.mkdir(generation, { mode: 0o750 });
const target = path.join(generation, 'keyring.json');
await fs.writeFile(target, bytes, { mode });
await fs.chmod(target, mode);
const next = path.join(root, '..data-next');
await fs.symlink(generationName, next);
await fs.rename(next, path.join(root, '..data'));
try {
await fs.symlink('..data/keyring.json', path.join(root, 'keyring.json'));
} catch (error) {
if (error.code !== 'EEXIST') throw error;
}
}
test('projected Tool result keyring follows atomic rotation without choosing the active catalog key', async () => {
const root = await tempRoot();
const keyOneBytes = Buffer.alloc(32, 0x11);
const keyTwoBytes = Buffer.alloc(32, 0x22);
const keyOne = keyOneBytes.toString('base64url');
const keyTwo = keyTwoBytes.toString('base64url');
await publish(
root,
'..2026_08_14_01',
canonicalClusterToolResultKeyringManifest(
manifest({ 'result-key-one': keyOne }),
),
);
const provider = await createClusterToolResultProjectedKeyring({
rootDirectory: root,
});
assert.equal('active' in provider, false);
const firstSummary = await provider.verify();
assert.deepEqual(firstSummary.keyIds, ['result-key-one']);
assert.equal(
firstSummary.materialProofs['result-key-one'],
toolResultKeyMaterialProof('result-key-one', keyOneBytes),
);
const first = await provider.resolve('result-key-one');
assert.ok(first);
assert.deepEqual(Buffer.from(first.key), keyOneBytes);
first.key.fill(0);
await publish(
root,
'..2026_08_14_02',
canonicalClusterToolResultKeyringManifest(
manifest({
'result-key-two': keyTwo,
'result-key-one': keyOne,
}),
),
);
const second = await provider.resolve('result-key-two');
assert.ok(second);
assert.deepEqual(Buffer.from(second.key), keyTwoBytes);
second.key.fill(0);
assert.equal(await provider.resolve('result-key-missing'), null);
});
test('projected Tool result keyring rejects noncanonical, writable and escaped material', async () => {
const encoded = Buffer.alloc(32, 0x33).toString('base64url');
const value = manifest({ 'result-key-one': encoded });
const noncanonicalRoot = await tempRoot();
await publish(
noncanonicalRoot,
'..2026_08_14_01',
Buffer.from(`${JSON.stringify(value, null, 2)}\n`, 'utf8'),
);
await assert.rejects(
() =>
createClusterToolResultProjectedKeyring({
rootDirectory: noncanonicalRoot,
}),
ClusterToolResultProjectedKeyringUnavailableError,
);
const writableRoot = await tempRoot();
await publish(
writableRoot,
'..2026_08_14_01',
canonicalClusterToolResultKeyringManifest(value),
0o640,
);
await assert.rejects(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: writableRoot,
}).verify(),
ClusterToolResultProjectedKeyringUnavailableError,
);
const escapedRoot = await tempRoot();
const externalRoot = await tempRoot();
const external = path.join(externalRoot, 'keyring.json');
await fs.writeFile(
external,
canonicalClusterToolResultKeyringManifest(value),
{ mode: 0o440 },
);
await fs.symlink(external, path.join(escapedRoot, 'keyring.json'));
await assert.rejects(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: escapedRoot,
}).verify(),
ClusterToolResultProjectedKeyringUnavailableError,
);
});
test('projected Tool result keyring rejects invalid material and ambiguous paths', async () => {
const invalidRoot = await tempRoot();
await publish(
invalidRoot,
'..2026_08_14_01',
Buffer.from(
`${JSON.stringify(
manifest({ 'result-key-one': Buffer.alloc(31).toString('base64url') }),
)}\n`,
'utf8',
),
);
await assert.rejects(
() =>
createClusterToolResultProjectedKeyring({ rootDirectory: invalidRoot }),
ClusterToolResultProjectedKeyringUnavailableError,
);
const root = await tempRoot();
const rootLink = `${root}-link`;
roots.push(rootLink);
await fs.symlink(root, rootLink);
await assert.rejects(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: rootLink,
}).verify(),
ClusterToolResultProjectedKeyringUnavailableError,
);
assert.throws(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: root,
dataFileName: '../keyring.json',
}),
ClusterToolResultProjectedKeyringUnavailableError,
);
});