mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
234 lines
6.9 KiB
JavaScript
234 lines
6.9 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { generateKeyPairSync } = require('node:crypto');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
consumeApprovalRequest,
|
|
createApprovalRequest,
|
|
decideApprovalRequest,
|
|
} = require('@qinglong/runtime-core/approved-action');
|
|
const {
|
|
createApprovedActionExecution,
|
|
claimApprovedActionExecution,
|
|
startApprovedActionExecution,
|
|
} = require('@qinglong/runtime-core/approved-action-execution');
|
|
const {
|
|
createPluginPackagePublisherRevocationProposal,
|
|
} = require('@qinglong/runtime-core/plugin-package-publisher-revocation-proposal');
|
|
const {
|
|
createPluginPackagePublisherTrustSnapshot,
|
|
} = require('@qinglong/runtime-core/plugin-package-publisher-trust');
|
|
const {
|
|
ClusterPluginPackagePublisherRevocationApprovedActionHandler,
|
|
} = require('../dist/plugin-package/publisher/pluginPackagePublisherRevocationApprovedAction');
|
|
|
|
const REQUESTER = Object.freeze({ type: 'user', id: 'usr_owner' });
|
|
const REVIEWER = Object.freeze({ type: 'user', id: 'usr_security' });
|
|
const SYSTEM = Object.freeze({ type: 'system', id: 'cluster_package_executor' });
|
|
const FENCE = Object.freeze({ projectVersion: 4, bindingVersion: 7 });
|
|
|
|
function proposal() {
|
|
const { publicKey } = generateKeyPairSync('ed25519');
|
|
return createPluginPackagePublisherRevocationProposal({
|
|
actionRef: 'publisher-revoke:publisher-a.example:key-a',
|
|
authorityProjectId: 'cluster-trust-authority',
|
|
trustAuthorityId: 'cluster',
|
|
trustGeneration: 1,
|
|
trustSnapshot: createPluginPackagePublisherTrustSnapshot([
|
|
{
|
|
publisher: 'publisher-a.example',
|
|
keyId: 'key-a',
|
|
publicKeyPem: publicKey.export({ type: 'spki', format: 'pem' }),
|
|
notBeforeMs: 1,
|
|
notAfterMs: 10_000,
|
|
},
|
|
]),
|
|
publisher: 'publisher-a.example',
|
|
keyId: 'key-a',
|
|
authorizationMode: 'dual_control',
|
|
reasonCode: 'suspected_key_compromise',
|
|
proposedBy: REQUESTER,
|
|
proposerAssurance: 'multi_factor',
|
|
proposalFence: FENCE,
|
|
createdAtMs: 5,
|
|
});
|
|
}
|
|
|
|
function dispatch(candidate) {
|
|
const action = {
|
|
permission: candidate.permission,
|
|
actionType: candidate.actionType,
|
|
actionRef: candidate.actionRef,
|
|
actionDigest: candidate.actionDigest,
|
|
previewDigest: candidate.previewDigest,
|
|
};
|
|
const pending = createApprovalRequest({
|
|
id: 'approval-publisher-revoke',
|
|
projectId: candidate.projectId,
|
|
action,
|
|
risk: 'critical',
|
|
decisionMode: 'separation_of_duty',
|
|
requestedBy: REQUESTER,
|
|
requestedAtMs: 10,
|
|
expiresAtMs: 1_000,
|
|
requestFence: FENCE,
|
|
});
|
|
const approved = decideApprovalRequest(pending, {
|
|
expectedVersion: 1,
|
|
decisionId: 'decision-publisher-revoke',
|
|
decision: 'approved',
|
|
reasonCode: 'reviewed',
|
|
principal: {
|
|
subject: REVIEWER,
|
|
authenticationId: 'auth-reviewer',
|
|
authenticatedAtMs: 15,
|
|
expiresAtMs: 500,
|
|
assurance: 'multi_factor',
|
|
},
|
|
decidedAtMs: 20,
|
|
authorizationFence: FENCE,
|
|
});
|
|
return consumeApprovalRequest(approved, {
|
|
expectedVersion: 2,
|
|
consumptionId: 'consume-publisher-revoke',
|
|
dispatchId: 'dispatch-publisher-revoke',
|
|
action,
|
|
requestedBy: REQUESTER,
|
|
consumedBy: SYSTEM,
|
|
consumedAtMs: 30,
|
|
authorizationFence: FENCE,
|
|
}).dispatch;
|
|
}
|
|
|
|
function execution(approvedDispatch) {
|
|
const baseline = createApprovedActionExecution(approvedDispatch, 5);
|
|
const claimed = claimApprovedActionExecution(baseline, {
|
|
owner: 'publisher-executor',
|
|
leaseToken: 'lease-publisher-revoke',
|
|
nowMs: 31,
|
|
leaseDurationMs: 1_000,
|
|
});
|
|
assert.equal(claimed.status, 'leased');
|
|
return startApprovedActionExecution(
|
|
{ dispatch: approvedDispatch, execution: claimed },
|
|
{
|
|
dispatchId: approvedDispatch.id,
|
|
approvalRequestId: approvedDispatch.approvalRequestId,
|
|
actionDigest: approvedDispatch.action.actionDigest,
|
|
owner: 'publisher-executor',
|
|
leaseToken: 'lease-publisher-revoke',
|
|
expectedVersion: claimed.version,
|
|
startedAtMs: 40,
|
|
},
|
|
);
|
|
}
|
|
|
|
test('inspects authority and executes the exact approved revocation receipt', async () => {
|
|
const candidate = proposal();
|
|
const approvedDispatch = dispatch(candidate);
|
|
const started = execution(approvedDispatch);
|
|
const receipts = [];
|
|
const handler =
|
|
new ClusterPluginPackagePublisherRevocationApprovedActionHandler(
|
|
{
|
|
async findProposalByActionRef() {
|
|
return candidate;
|
|
},
|
|
async createProposal() {
|
|
throw new Error('must not create');
|
|
},
|
|
},
|
|
{
|
|
async run(receipt) {
|
|
receipts.push(receipt);
|
|
return {
|
|
safeToAdmit: true,
|
|
receiptDigest: receipt.receiptDigest,
|
|
impactDigest: 'a'.repeat(64),
|
|
};
|
|
},
|
|
},
|
|
);
|
|
assert.deepEqual(await handler.inspect(approvedDispatch), {
|
|
status: 'ready',
|
|
actionDigest: candidate.actionDigest,
|
|
});
|
|
const result = await handler.execute({
|
|
dispatch: approvedDispatch,
|
|
execution: started,
|
|
idempotencyKey: 'publisher-revoke-attempt',
|
|
fence: {
|
|
owner: started.leaseOwner,
|
|
leaseToken: started.leaseToken,
|
|
version: started.version,
|
|
},
|
|
});
|
|
assert.deepEqual(result, {
|
|
outcome: 'succeeded',
|
|
resultCode: 'publisher_revocation_converged',
|
|
resultDigest: 'a'.repeat(64),
|
|
});
|
|
assert.equal(receipts.length, 1);
|
|
assert.equal(receipts[0].mutationId, approvedDispatch.id);
|
|
assert.equal(receipts[0].revokedAtMs, started.startedAtMs);
|
|
});
|
|
|
|
test('blocks missing proposals and incomplete quarantine convergence', async () => {
|
|
const candidate = proposal();
|
|
const approvedDispatch = dispatch(candidate);
|
|
const missing =
|
|
new ClusterPluginPackagePublisherRevocationApprovedActionHandler(
|
|
{
|
|
async findProposalByActionRef() {
|
|
return null;
|
|
},
|
|
async createProposal() {
|
|
throw new Error('must not create');
|
|
},
|
|
},
|
|
{ async run() { throw new Error('must not run'); } },
|
|
);
|
|
assert.deepEqual(await missing.inspect(approvedDispatch), {
|
|
status: 'blocked',
|
|
resultCode: 'publisher_revocation_proposal_missing',
|
|
});
|
|
|
|
const started = execution(approvedDispatch);
|
|
const incomplete =
|
|
new ClusterPluginPackagePublisherRevocationApprovedActionHandler(
|
|
{
|
|
async findProposalByActionRef() {
|
|
return candidate;
|
|
},
|
|
async createProposal() {
|
|
throw new Error('must not create');
|
|
},
|
|
},
|
|
{
|
|
async run(receipt) {
|
|
return {
|
|
safeToAdmit: false,
|
|
receiptDigest: receipt.receiptDigest,
|
|
impactDigest: 'b'.repeat(64),
|
|
};
|
|
},
|
|
},
|
|
);
|
|
assert.deepEqual(
|
|
await incomplete.execute({
|
|
dispatch: approvedDispatch,
|
|
execution: started,
|
|
idempotencyKey: 'publisher-revoke-attempt',
|
|
fence: {
|
|
owner: started.leaseOwner,
|
|
leaseToken: started.leaseToken,
|
|
version: started.version,
|
|
},
|
|
}),
|
|
{
|
|
outcome: 'indeterminate',
|
|
resultCode: 'publisher_revocation_convergence_incomplete',
|
|
},
|
|
);
|
|
});
|