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:
+330
@@ -0,0 +1,330 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { generateKeyPairSync } = require('node:crypto');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
approvalRequestDigest,
|
||||
createApprovalRequest,
|
||||
decideApprovalRequest,
|
||||
} = require('@qinglong/runtime-core/approved-action');
|
||||
const {
|
||||
createPluginPackagePublisherTrustSnapshot,
|
||||
} = require('@qinglong/runtime-core/plugin-package-publisher-trust');
|
||||
const {
|
||||
createPluginPackagePublisherTrustTransitionProposal,
|
||||
PluginPackagePublisherTrustTransitionConflictError,
|
||||
} = require('@qinglong/runtime-core/plugin-package-publisher-trust-transition-proposal');
|
||||
const {
|
||||
PostgresPluginPackagePublisherTrustTransitionProposalRepository,
|
||||
} = require('../dist/plugin-package/publisher/pluginPackagePublisherTrustTransitionProposalRepository');
|
||||
|
||||
const SUBJECT = Object.freeze({ type: 'user', id: 'usr_owner' });
|
||||
const FENCE = Object.freeze({ projectVersion: 4, bindingVersion: 7 });
|
||||
|
||||
function transition() {
|
||||
const oldPair = generateKeyPairSync('ed25519');
|
||||
const newPair = generateKeyPairSync('ed25519');
|
||||
const oldDefinition = {
|
||||
publisher: 'publisher-a.example',
|
||||
keyId: 'key-old',
|
||||
publicKeyPem: oldPair.publicKey.export({
|
||||
type: 'spki',
|
||||
format: 'pem',
|
||||
}),
|
||||
notBeforeMs: 1,
|
||||
notAfterMs: 10_000,
|
||||
};
|
||||
const newDefinition = {
|
||||
publisher: 'publisher-a.example',
|
||||
keyId: 'key-new',
|
||||
publicKeyPem: newPair.publicKey.export({
|
||||
type: 'spki',
|
||||
format: 'pem',
|
||||
}),
|
||||
notBeforeMs: 1,
|
||||
notAfterMs: 20_000,
|
||||
};
|
||||
const currentSnapshot =
|
||||
createPluginPackagePublisherTrustSnapshot([oldDefinition]);
|
||||
const materialSnapshot =
|
||||
createPluginPackagePublisherTrustSnapshot([
|
||||
oldDefinition,
|
||||
newDefinition,
|
||||
]);
|
||||
const created =
|
||||
createPluginPackagePublisherTrustTransitionProposal({
|
||||
actionRef: 'publisher-overlap:publisher-a.example:key-new',
|
||||
authorityProjectId: 'cluster-trust-authority',
|
||||
trustAuthorityId: 'cluster',
|
||||
trustGeneration: 1,
|
||||
mode: 'overlap_add',
|
||||
trustSnapshot: currentSnapshot,
|
||||
materialSnapshot,
|
||||
publisher: 'publisher-a.example',
|
||||
keyId: 'key-new',
|
||||
proposedBy: SUBJECT,
|
||||
proposerAssurance: 'multi_factor',
|
||||
proposalFence: FENCE,
|
||||
createdAtMs: 100,
|
||||
});
|
||||
return {
|
||||
...created,
|
||||
currentSnapshot,
|
||||
};
|
||||
}
|
||||
|
||||
function audit(proposal, overrides = {}) {
|
||||
return {
|
||||
eventId: '33000000-0000-4000-8000-000000000001',
|
||||
requestId: proposal.actionRef,
|
||||
operationId: 'plugin_package.publisher_trust_transition.propose',
|
||||
projectId: proposal.projectId,
|
||||
subject: proposal.proposedBy,
|
||||
authenticationId: 'auth-owner',
|
||||
outcome: 'allowed',
|
||||
reasons: ['publisher_trust_transition_proposal'],
|
||||
fence: proposal.proposalFence,
|
||||
occurredAtMs: proposal.createdAtMs,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function fixture(value, trustOverrides = {}) {
|
||||
const snapshots = new Map([
|
||||
[value.currentSnapshot.snapshotDigest, value.currentSnapshot],
|
||||
]);
|
||||
let storedProposal = null;
|
||||
let storedAudit = null;
|
||||
let signerLocks = 0;
|
||||
const query = async (text, values = []) => {
|
||||
if (text.includes('pg_advisory_xact_lock(hashtextextended')) {
|
||||
signerLocks += 1;
|
||||
assert.deepEqual(values, [
|
||||
JSON.stringify([
|
||||
value.proposal.actionInput.publisher,
|
||||
value.proposal.actionInput.keyId,
|
||||
]),
|
||||
774635229,
|
||||
]);
|
||||
return { rows: [{}], rowCount: 1 };
|
||||
}
|
||||
if (
|
||||
text.includes(
|
||||
'FROM "ql3"."plugin_package_publisher_trust_transition_proposals"',
|
||||
)
|
||||
) {
|
||||
return {
|
||||
rows: storedProposal
|
||||
? [
|
||||
{
|
||||
proposalJson: storedProposal,
|
||||
proposalDigest: storedProposal.proposalDigest,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
if (
|
||||
text.includes(
|
||||
'FROM "ql3"."plugin_package_publisher_trust_heads" AS head',
|
||||
)
|
||||
) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
generation:
|
||||
trustOverrides.generation ??
|
||||
value.proposal.actionInput.trustGeneration,
|
||||
effectiveTrustDigest:
|
||||
trustOverrides.effectiveTrustDigest ??
|
||||
value.proposal.actionInput.previousTrustDigest,
|
||||
snapshotJson: value.currentSnapshot,
|
||||
snapshotDigest: value.currentSnapshot.snapshotDigest,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (
|
||||
text.includes(
|
||||
'INSERT INTO "ql3"."plugin_package_publisher_trust_snapshots"',
|
||||
)
|
||||
) {
|
||||
const existed = snapshots.has(values[0]);
|
||||
if (!existed) snapshots.set(values[0], JSON.parse(values[4]));
|
||||
return { rows: [], rowCount: existed ? 0 : 1 };
|
||||
}
|
||||
if (
|
||||
text.includes(
|
||||
'FROM "ql3"."plugin_package_publisher_trust_snapshots"',
|
||||
)
|
||||
) {
|
||||
const snapshot = snapshots.get(values[0]);
|
||||
return {
|
||||
rows: snapshot
|
||||
? [
|
||||
{
|
||||
snapshotJson: snapshot,
|
||||
snapshotDigest: snapshot.snapshotDigest,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
if (text.includes('lock_approval_policy_fence')) {
|
||||
return { rows: [{ matches: true }] };
|
||||
}
|
||||
if (
|
||||
text.includes(
|
||||
'"ql3"."plugin_package_publisher_trust_transition_proposals" (',
|
||||
)
|
||||
) {
|
||||
storedProposal = JSON.parse(values[19]);
|
||||
return { rows: [], rowCount: 1 };
|
||||
}
|
||||
if (text.includes('INSERT INTO "ql3"."security_audit_events"')) {
|
||||
storedAudit = audit(value.proposal);
|
||||
return { rows: [], rowCount: 1 };
|
||||
}
|
||||
if (text.includes('FROM "ql3"."security_audit_events"')) {
|
||||
return {
|
||||
rows: storedAudit
|
||||
? [
|
||||
{
|
||||
eventId: storedAudit.eventId,
|
||||
requestId: storedAudit.requestId,
|
||||
operationId: storedAudit.operationId,
|
||||
projectId: storedAudit.projectId,
|
||||
subjectType: storedAudit.subject.type,
|
||||
subjectId: storedAudit.subject.id,
|
||||
authenticationId: storedAudit.authenticationId,
|
||||
outcome: storedAudit.outcome,
|
||||
reasons: storedAudit.reasons,
|
||||
projectVersion: storedAudit.fence.projectVersion,
|
||||
bindingVersion: storedAudit.fence.bindingVersion,
|
||||
occurredAtMs: storedAudit.occurredAtMs,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
return { rows: [], rowCount: 0 };
|
||||
};
|
||||
const client = { query, release() {} };
|
||||
const repository =
|
||||
new PostgresPluginPackagePublisherTrustTransitionProposalRepository({
|
||||
query,
|
||||
async connect() {
|
||||
return client;
|
||||
},
|
||||
});
|
||||
repository.signerLocks = () => signerLocks;
|
||||
repository.snapshots = () => snapshots;
|
||||
return repository;
|
||||
}
|
||||
|
||||
test('persists candidate material and exactly replays an overlap proposal', async () => {
|
||||
const value = transition();
|
||||
const repository = fixture(value);
|
||||
const command = {
|
||||
proposal: value.proposal,
|
||||
candidateSnapshot: value.candidateSnapshot,
|
||||
audit: audit(value.proposal),
|
||||
};
|
||||
const created = await repository.createProposal(command);
|
||||
assert.equal(created.status, 'created');
|
||||
assert.equal(repository.signerLocks(), 1);
|
||||
assert.deepEqual(
|
||||
repository.snapshots().get(value.candidateSnapshot.snapshotDigest),
|
||||
value.candidateSnapshot,
|
||||
);
|
||||
|
||||
const replay = await repository.createProposal(command);
|
||||
assert.equal(replay.status, 'existing');
|
||||
assert.equal(repository.signerLocks(), 1);
|
||||
assert.deepEqual(
|
||||
await repository.findProposalByActionRef(value.proposal.actionRef),
|
||||
value.proposal,
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects stale trust generations and client-shaped candidate drift', async () => {
|
||||
const value = transition();
|
||||
await assert.rejects(
|
||||
fixture(value, { generation: 2 }).createProposal({
|
||||
proposal: value.proposal,
|
||||
candidateSnapshot: value.candidateSnapshot,
|
||||
audit: audit(value.proposal),
|
||||
}),
|
||||
PluginPackagePublisherTrustTransitionConflictError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
fixture(value).createProposal({
|
||||
proposal: value.proposal,
|
||||
candidateSnapshot: value.currentSnapshot,
|
||||
audit: audit(value.proposal),
|
||||
}),
|
||||
PluginPackagePublisherTrustTransitionConflictError,
|
||||
);
|
||||
});
|
||||
|
||||
test('lists only separation-of-duty approvals from the durable JSON record', async () => {
|
||||
const value = transition();
|
||||
const pending = createApprovalRequest({
|
||||
id: 'approval-trust-transition-list',
|
||||
projectId: value.proposal.projectId,
|
||||
action: {
|
||||
permission: value.proposal.permission,
|
||||
actionType: value.proposal.actionType,
|
||||
actionRef: value.proposal.actionRef,
|
||||
actionDigest: value.proposal.actionDigest,
|
||||
previewDigest: value.proposal.previewDigest,
|
||||
},
|
||||
risk: 'critical',
|
||||
decisionMode: 'separation_of_duty',
|
||||
requestedBy: SUBJECT,
|
||||
requestedAtMs: 100,
|
||||
expiresAtMs: 10_000,
|
||||
requestFence: FENCE,
|
||||
});
|
||||
const approved = decideApprovalRequest(pending, {
|
||||
expectedVersion: 1,
|
||||
decisionId: 'decision-trust-transition-list',
|
||||
decision: 'approved',
|
||||
reasonCode: 'reviewed',
|
||||
principal: {
|
||||
subject: { type: 'user', id: 'usr_reviewer' },
|
||||
authenticationId: 'auth-reviewer',
|
||||
authenticatedAtMs: 100,
|
||||
expiresAtMs: 10_000,
|
||||
assurance: 'multi_factor',
|
||||
},
|
||||
decidedAtMs: 101,
|
||||
authorizationFence: FENCE,
|
||||
});
|
||||
let inspectedSql = '';
|
||||
const repository =
|
||||
new PostgresPluginPackagePublisherTrustTransitionProposalRepository({
|
||||
async query(text) {
|
||||
inspectedSql = text;
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
requestJson: approved,
|
||||
requestDigest: approvalRequestDigest(approved),
|
||||
},
|
||||
],
|
||||
rowCount: 1,
|
||||
};
|
||||
},
|
||||
async connect() {
|
||||
throw new Error('listApprovedRequests must not open a transaction');
|
||||
},
|
||||
});
|
||||
assert.deepEqual(await repository.listApprovedRequests(4), [approved]);
|
||||
assert.match(
|
||||
inspectedSql,
|
||||
/request\.request_json ->> 'decisionMode'/,
|
||||
);
|
||||
assert.doesNotMatch(inspectedSql, /request\.decision_mode/);
|
||||
});
|
||||
Reference in New Issue
Block a user