mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
+340
@@ -0,0 +1,340 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { generateKeyPairSync } = require('node:crypto');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
createApprovalRequest,
|
||||
decideApprovalRequest,
|
||||
} = require('@qinglong/runtime-core/approved-action');
|
||||
const {
|
||||
createPluginPackagePublisherTrustSnapshot,
|
||||
} = require('@qinglong/runtime-core/plugin-package-publisher-trust');
|
||||
const {
|
||||
createPluginPackagePublisherTrustTransitionProposal,
|
||||
} = require('@qinglong/runtime-core/plugin-package-publisher-trust-transition-proposal');
|
||||
const {
|
||||
ClusterPluginPackageManagementTransportRequestError,
|
||||
createClusterPluginPackageManagementTransport,
|
||||
} = require('@qinglong/cluster-admin/plugin-package-management-transport');
|
||||
|
||||
const NOW = 1_000;
|
||||
const OWNER = Object.freeze({ type: 'user', id: 'cluster-owner' });
|
||||
const REVIEWER = Object.freeze({ type: 'user', id: 'cluster-reviewer' });
|
||||
const FENCE = Object.freeze({ projectVersion: 4, bindingVersion: 7 });
|
||||
|
||||
function principal() {
|
||||
return {
|
||||
subject: REVIEWER,
|
||||
authenticationId: 'reviewer-session',
|
||||
authenticatedAtMs: NOW - 100,
|
||||
expiresAtMs: NOW + 1_000,
|
||||
assurance: 'multi_factor',
|
||||
};
|
||||
}
|
||||
|
||||
function authentication() {
|
||||
return {
|
||||
async authenticate() {
|
||||
return principal();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function definition(keyId) {
|
||||
const { publicKey } = generateKeyPairSync('ed25519');
|
||||
return {
|
||||
publisher: 'publisher-a.example',
|
||||
keyId,
|
||||
publicKeyPem: publicKey.export({ type: 'spki', format: 'pem' }),
|
||||
notBeforeMs: 1,
|
||||
notAfterMs: 20_000,
|
||||
};
|
||||
}
|
||||
|
||||
const oldDefinition = definition('key-old');
|
||||
const newDefinition = definition('key-new');
|
||||
const currentSnapshot = createPluginPackagePublisherTrustSnapshot([
|
||||
oldDefinition,
|
||||
]);
|
||||
const materialSnapshot = createPluginPackagePublisherTrustSnapshot([
|
||||
oldDefinition,
|
||||
newDefinition,
|
||||
]);
|
||||
|
||||
function transition(createdAtMs = NOW) {
|
||||
return createPluginPackagePublisherTrustTransitionProposal({
|
||||
actionRef: 'publisher-overlap:publisher-a.example:key-new',
|
||||
authorityProjectId: 'cluster-trust-authority',
|
||||
trustAuthorityId: 'cluster',
|
||||
trustGeneration: 4,
|
||||
mode: 'overlap_add',
|
||||
trustSnapshot: currentSnapshot,
|
||||
materialSnapshot,
|
||||
publisher: 'publisher-a.example',
|
||||
keyId: 'key-new',
|
||||
proposedBy: OWNER,
|
||||
proposerAssurance: 'multi_factor',
|
||||
proposalFence: FENCE,
|
||||
createdAtMs,
|
||||
}).proposal;
|
||||
}
|
||||
|
||||
function approval(candidate = transition(), overrides = {}) {
|
||||
return createApprovalRequest({
|
||||
id: 'approval-publisher-overlap',
|
||||
projectId: candidate.projectId,
|
||||
action: {
|
||||
permission: candidate.permission,
|
||||
actionType: candidate.actionType,
|
||||
actionRef: candidate.actionRef,
|
||||
actionDigest: candidate.actionDigest,
|
||||
previewDigest: candidate.previewDigest,
|
||||
},
|
||||
risk: 'critical',
|
||||
decisionMode: 'separation_of_duty',
|
||||
requestedBy: OWNER,
|
||||
requestedAtMs: candidate.createdAtMs,
|
||||
expiresAtMs: candidate.createdAtMs + 10_000,
|
||||
requestFence: FENCE,
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
function command(operation, request = {}) {
|
||||
const common = {
|
||||
actionRef: 'publisher-overlap:publisher-a.example:key-new',
|
||||
approvalRequestId: 'approval-publisher-overlap',
|
||||
};
|
||||
if (operation.endsWith('.propose')) {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
operation,
|
||||
request: {
|
||||
...common,
|
||||
proposalAuditEventId: 'proposal-overlap-audit',
|
||||
approvalAuditEventId: 'approval-overlap-audit',
|
||||
mode: 'overlap_add',
|
||||
publisher: 'publisher-a.example',
|
||||
keyId: 'key-new',
|
||||
...request,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (operation.endsWith('.decide')) {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
operation,
|
||||
request: {
|
||||
...common,
|
||||
expectedVersion: 1,
|
||||
decisionId: 'decision-publisher-overlap',
|
||||
auditEventId: 'decision-overlap-audit',
|
||||
decision: 'approved',
|
||||
reasonCode: 'reviewed',
|
||||
...request,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
operation,
|
||||
request: {
|
||||
...common,
|
||||
inspectionId: 'inspection-publisher-overlap',
|
||||
...request,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function services(state = { proposal: null, approvalRequest: null }) {
|
||||
const calls = {
|
||||
decide: [],
|
||||
proposeTransition: [],
|
||||
inspectTransition: [],
|
||||
inspectTransitionAuthorized: [],
|
||||
};
|
||||
const installService = {
|
||||
async propose() {
|
||||
throw new Error('install proposal must not run');
|
||||
},
|
||||
async decide(request) {
|
||||
calls.decide.push(request);
|
||||
return {
|
||||
status: 'decided',
|
||||
request: decideApprovalRequest(state.approvalRequest, {
|
||||
expectedVersion: request.expectedVersion,
|
||||
decisionId: request.decisionId,
|
||||
decision: request.decision,
|
||||
reasonCode: request.reasonCode,
|
||||
principal: request.principal,
|
||||
decidedAtMs: request.decidedAtMs,
|
||||
authorizationFence: FENCE,
|
||||
}),
|
||||
};
|
||||
},
|
||||
async inspect() {
|
||||
return { proposal: null, approvalRequest: null };
|
||||
},
|
||||
async inspectAuthorized() {
|
||||
return { proposal: null, approvalRequest: null };
|
||||
},
|
||||
async inspectInstallationAuthorized() {
|
||||
return null;
|
||||
},
|
||||
async listInstallationsAuthorized() {
|
||||
return { items: [], truncated: false };
|
||||
},
|
||||
};
|
||||
const publisherTrust = {
|
||||
async propose() {
|
||||
throw new Error('revocation proposal must not run');
|
||||
},
|
||||
async inspect() {
|
||||
return { proposal: null, approvalRequest: null };
|
||||
},
|
||||
async inspectAuthorized() {
|
||||
return { proposal: null, approvalRequest: null };
|
||||
},
|
||||
async proposeTransition(request) {
|
||||
calls.proposeTransition.push(request);
|
||||
const candidate = transition(request.requestedAtMs);
|
||||
return {
|
||||
proposalStatus: 'created',
|
||||
approvalStatus: 'created',
|
||||
proposal: candidate,
|
||||
approvalRequest: approval(candidate),
|
||||
};
|
||||
},
|
||||
async inspectTransition(actionRef, approvalRequestId) {
|
||||
calls.inspectTransition.push({ actionRef, approvalRequestId });
|
||||
return state;
|
||||
},
|
||||
async inspectTransitionAuthorized(request) {
|
||||
calls.inspectTransitionAuthorized.push(request);
|
||||
return state;
|
||||
},
|
||||
};
|
||||
return { calls, installService, publisherTrust };
|
||||
}
|
||||
|
||||
test('routes derived-only trust overlap proposal without key material', async () => {
|
||||
const fixture = services();
|
||||
const transport = createClusterPluginPackageManagementTransport({
|
||||
service: fixture.installService,
|
||||
publisherTrust: fixture.publisherTrust,
|
||||
now: () => NOW,
|
||||
});
|
||||
const proposed = command('plugin-package.publisher-trust-transition.propose');
|
||||
const result = await transport.execute(proposed, authentication());
|
||||
|
||||
assert.equal(result.operation, proposed.operation);
|
||||
assert.equal(fixture.calls.proposeTransition.length, 1);
|
||||
assert.deepEqual(Object.keys(proposed.request).sort(), [
|
||||
'actionRef',
|
||||
'approvalAuditEventId',
|
||||
'approvalRequestId',
|
||||
'keyId',
|
||||
'mode',
|
||||
'proposalAuditEventId',
|
||||
'publisher',
|
||||
]);
|
||||
assert.equal(JSON.stringify(proposed).includes('PUBLIC KEY'), false);
|
||||
assert.equal(JSON.stringify(result).includes('PUBLIC KEY'), false);
|
||||
assert.deepEqual(Object.keys(result.proposal).sort(), [
|
||||
'actionDigest',
|
||||
'actionRef',
|
||||
'createdAtMs',
|
||||
'currentTrustDigest',
|
||||
'keyId',
|
||||
'mode',
|
||||
'previewDigest',
|
||||
'previousTrustDigest',
|
||||
'projectId',
|
||||
'proposalDigest',
|
||||
'publisher',
|
||||
'trustAuthorityId',
|
||||
'trustGeneration',
|
||||
]);
|
||||
assert.deepEqual(fixture.calls.proposeTransition[0].principal, principal());
|
||||
assert.equal(fixture.calls.proposeTransition[0].requestedAtMs, NOW);
|
||||
});
|
||||
|
||||
test('requires exact separation-of-duty authority before transition decision', async () => {
|
||||
const candidate = transition();
|
||||
const pending = approval(candidate);
|
||||
const fixture = services({
|
||||
proposal: candidate,
|
||||
approvalRequest: pending,
|
||||
});
|
||||
const transport = createClusterPluginPackageManagementTransport({
|
||||
service: fixture.installService,
|
||||
publisherTrust: fixture.publisherTrust,
|
||||
now: () => NOW,
|
||||
});
|
||||
const result = await transport.execute(
|
||||
command('plugin-package.publisher-trust-transition.decide'),
|
||||
authentication(),
|
||||
);
|
||||
assert.equal(result.status, 'decided');
|
||||
assert.equal(result.approval.decisionMode, 'separation_of_duty');
|
||||
assert.equal(fixture.calls.decide.length, 1);
|
||||
assert.deepEqual(fixture.calls.decide[0].principal, principal());
|
||||
|
||||
const invalid = services({
|
||||
proposal: candidate,
|
||||
approvalRequest: approval(candidate, {
|
||||
decisionMode: 'human_confirmation',
|
||||
}),
|
||||
});
|
||||
const invalidTransport = createClusterPluginPackageManagementTransport({
|
||||
service: invalid.installService,
|
||||
publisherTrust: invalid.publisherTrust,
|
||||
now: () => NOW,
|
||||
});
|
||||
await assert.rejects(
|
||||
invalidTransport.execute(
|
||||
command('plugin-package.publisher-trust-transition.decide'),
|
||||
authentication(),
|
||||
),
|
||||
ClusterPluginPackageManagementTransportRequestError,
|
||||
);
|
||||
assert.deepEqual(invalid.calls.decide, []);
|
||||
});
|
||||
|
||||
test('uses scoped authorized inspection and rejects client key material', async () => {
|
||||
const candidate = transition();
|
||||
const state = {
|
||||
proposal: candidate,
|
||||
approvalRequest: approval(candidate),
|
||||
};
|
||||
const fixture = services(state);
|
||||
const transport = createClusterPluginPackageManagementTransport({
|
||||
service: fixture.installService,
|
||||
publisherTrust: fixture.publisherTrust,
|
||||
now: () => NOW,
|
||||
});
|
||||
const result = await transport.execute(
|
||||
command('plugin-package.publisher-trust-transition.inspect'),
|
||||
authentication(),
|
||||
);
|
||||
assert.equal(
|
||||
result.operation,
|
||||
'plugin-package.publisher-trust-transition.inspect',
|
||||
);
|
||||
assert.deepEqual(fixture.calls.inspectTransition, []);
|
||||
assert.equal(fixture.calls.inspectTransitionAuthorized.length, 1);
|
||||
assert.deepEqual(
|
||||
fixture.calls.inspectTransitionAuthorized[0].principal,
|
||||
principal(),
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
transport.execute(
|
||||
command('plugin-package.publisher-trust-transition.propose', {
|
||||
publicKeyPem: 'client-controlled',
|
||||
}),
|
||||
authentication(),
|
||||
),
|
||||
ClusterPluginPackageManagementTransportRequestError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user