mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 02:27:44 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { createHash, generateKeyPairSync, sign } = require('node:crypto');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
PluginPackagePromptOutputExternalCustodyUntrustedError,
|
||||
createPluginPackagePromptOutputExternalCustodyReceipt,
|
||||
normalizePluginPackagePromptOutputExternalCustodyReceipt,
|
||||
verifyPluginPackagePromptOutputExternalCustodyReceipt,
|
||||
verifyPluginPackagePromptOutputRecoveredMaterial,
|
||||
verifyPluginPackagePromptOutputWrappedBackup,
|
||||
} = require('../dist/prompt-output/custody/pluginPackagePromptOutputExternalCustody.js');
|
||||
const {
|
||||
createPluginPackagePromptOutputArtifact,
|
||||
} = require('../dist/prompt-output/pluginPackagePromptOutputArtifact.js');
|
||||
const {
|
||||
pluginPackagePromptOutputKeyRotationMaterialProof,
|
||||
} = require('../dist/prompt-output/key-management/pluginPackagePromptOutputKeyRotation.js');
|
||||
|
||||
const MATERIAL = Buffer.alloc(32, 0x5a);
|
||||
const WRAPPED = Buffer.from('kms-wrapped-prompt-output-key-material');
|
||||
const CATALOG_DIGEST = '1'.repeat(64);
|
||||
const WRAPPING_KEY_REF_DIGEST = '2'.repeat(64);
|
||||
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
|
||||
|
||||
function receipt(overrides = {}) {
|
||||
return createPluginPackagePromptOutputExternalCustodyReceipt(
|
||||
{
|
||||
custodyId: 'custody-001',
|
||||
keyId: 'prompt-key-001',
|
||||
materialProof: pluginPackagePromptOutputKeyRotationMaterialProof(
|
||||
'prompt-key-001',
|
||||
MATERIAL,
|
||||
),
|
||||
sourceGeneration: 4,
|
||||
sourceCatalogDigest: CATALOG_DIGEST,
|
||||
wrappingProvider: 'vault-transit',
|
||||
wrappingKeyRefDigest: WRAPPING_KEY_REF_DIGEST,
|
||||
wrappedMaterialDigest: createHash('sha256').update(WRAPPED).digest('hex'),
|
||||
wrappedMaterialBytes: WRAPPED.length,
|
||||
createdAtMs: 1_700_000_000_000,
|
||||
...overrides,
|
||||
},
|
||||
{
|
||||
publicKey,
|
||||
sign: (digest) => sign(null, digest, privateKey),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function artifact(overrides = {}) {
|
||||
return createPluginPackagePromptOutputArtifact(
|
||||
{
|
||||
projectId: 'project-a',
|
||||
runId: 'run-a',
|
||||
stepRunId: 'step-a',
|
||||
invocationId: 'invocation-a',
|
||||
requestedBy: { type: 'user', id: 'user-a' },
|
||||
result: {
|
||||
provider: 'openai-compatible',
|
||||
model: 'bounded-model',
|
||||
text: 'private recovered answer',
|
||||
finishReason: 'stop',
|
||||
usage: {
|
||||
inputTokens: 5,
|
||||
outputTokens: 3,
|
||||
totalTokens: 8,
|
||||
costMicros: 11,
|
||||
},
|
||||
},
|
||||
retentionPolicy: {
|
||||
revision: 'retention-v1',
|
||||
retentionMs: 86_400_000,
|
||||
},
|
||||
keyId: 'prompt-key-001',
|
||||
key: Buffer.from(MATERIAL),
|
||||
sealedAtMs: 1_700_000_000_000,
|
||||
...overrides,
|
||||
},
|
||||
() => Buffer.alloc(12, 0x33),
|
||||
);
|
||||
}
|
||||
|
||||
test('binds a signed content-free custody receipt to exact wrapped bytes', () => {
|
||||
const value = receipt();
|
||||
assert.deepEqual(
|
||||
normalizePluginPackagePromptOutputExternalCustodyReceipt(value),
|
||||
value,
|
||||
);
|
||||
assert.deepEqual(
|
||||
verifyPluginPackagePromptOutputExternalCustodyReceipt(value, publicKey),
|
||||
value,
|
||||
);
|
||||
const verified = verifyPluginPackagePromptOutputWrappedBackup(
|
||||
value,
|
||||
publicKey,
|
||||
WRAPPED,
|
||||
);
|
||||
assert.equal(verified.custodyId, 'custody-001');
|
||||
assert.equal(verified.wrappedMaterialBytes, WRAPPED.length);
|
||||
const serialized = JSON.stringify(value);
|
||||
assert.equal(serialized.includes(MATERIAL.toString('base64url')), false);
|
||||
assert.equal(serialized.includes(WRAPPED.toString('base64url')), false);
|
||||
assert.equal(serialized.includes('kms-wrapped-prompt'), false);
|
||||
});
|
||||
|
||||
test('verifies recovered material against durable fact and opens an Artifact', () => {
|
||||
const custody = receipt();
|
||||
const encrypted = artifact();
|
||||
const proof = verifyPluginPackagePromptOutputRecoveredMaterial({
|
||||
recoveryId: 'recovery-001',
|
||||
requestId: 'request-001',
|
||||
receipt: custody,
|
||||
trustedPublicKey: publicKey,
|
||||
durableKeyFact: {
|
||||
keyId: custody.keyId,
|
||||
materialProof: custody.materialProof,
|
||||
catalogDigest: custody.sourceCatalogDigest,
|
||||
},
|
||||
material: MATERIAL,
|
||||
artifact: encrypted,
|
||||
verifiedAtMs: 1_700_000_001_000,
|
||||
});
|
||||
assert.equal(proof.artifactId, encrypted.artifactId);
|
||||
assert.equal(proof.artifactDigest, encrypted.artifactDigest);
|
||||
assert.equal(proof.contentDigest, encrypted.contentDigest);
|
||||
assert.equal(proof.keyId, custody.keyId);
|
||||
assert.equal(proof.proofDigest.length, 64);
|
||||
const serialized = JSON.stringify(proof);
|
||||
assert.equal(serialized.includes('private recovered answer'), false);
|
||||
assert.equal(serialized.includes(MATERIAL.toString('base64url')), false);
|
||||
});
|
||||
|
||||
test('rejects untrusted receipt, wrapped bytes, durable fact and material drift', () => {
|
||||
const custody = receipt();
|
||||
const otherKeys = generateKeyPairSync('ed25519');
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputExternalCustodyReceipt(
|
||||
custody,
|
||||
otherKeys.publicKey,
|
||||
),
|
||||
PluginPackagePromptOutputExternalCustodyUntrustedError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputWrappedBackup(
|
||||
custody,
|
||||
publicKey,
|
||||
Buffer.from('different wrapped material'),
|
||||
),
|
||||
PluginPackagePromptOutputExternalCustodyUntrustedError,
|
||||
);
|
||||
for (const candidate of [
|
||||
{
|
||||
durableKeyFact: {
|
||||
keyId: custody.keyId,
|
||||
materialProof: '3'.repeat(64),
|
||||
catalogDigest: custody.sourceCatalogDigest,
|
||||
},
|
||||
material: MATERIAL,
|
||||
},
|
||||
{
|
||||
durableKeyFact: {
|
||||
keyId: custody.keyId,
|
||||
materialProof: custody.materialProof,
|
||||
catalogDigest: custody.sourceCatalogDigest,
|
||||
},
|
||||
material: Buffer.alloc(32, 0x5b),
|
||||
},
|
||||
]) {
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputRecoveredMaterial({
|
||||
recoveryId: 'recovery-001',
|
||||
requestId: 'request-001',
|
||||
receipt: custody,
|
||||
trustedPublicKey: publicKey,
|
||||
durableKeyFact: candidate.durableKeyFact,
|
||||
material: candidate.material,
|
||||
artifact: artifact(),
|
||||
verifiedAtMs: 1_700_000_001_000,
|
||||
}),
|
||||
PluginPackagePromptOutputExternalCustodyUntrustedError,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects a custody receipt with a tampered signature', () => {
|
||||
const custody = receipt();
|
||||
const signature = Buffer.from(custody.signature, 'base64url');
|
||||
signature[0] ^= 0x01;
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputExternalCustodyReceipt(
|
||||
{
|
||||
...custody,
|
||||
signature: signature.toString('base64url'),
|
||||
},
|
||||
publicKey,
|
||||
),
|
||||
PluginPackagePromptOutputExternalCustodyUntrustedError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user