feat(ql3): persist package secret approval plans

This commit is contained in:
whyour
2026-08-13 12:43:43 +08:00
parent e43fd607aa
commit 3a58e1508e
16 changed files with 1335 additions and 94 deletions
@@ -0,0 +1,310 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
PLUGIN_PACKAGE_API_VERSION,
PLUGIN_PACKAGE_KIND,
planPluginPackageInstall,
} = require('@qinglong/runtime-core/plugin-package');
const {
createPluginPackageInstall,
createPluginPackageLock,
pluginPackageActivationIntentDigest,
pluginPackageInstallActionDigest,
pluginPackageInstallPlanDigest,
transitionPluginPackageInstall,
} = require('@qinglong/runtime-core/plugin-package-install');
const {
createPluginPackageInstallProposal,
} = require('@qinglong/runtime-core/plugin-package-proposal');
const {
createPluginPackageResourceGeneration,
} = require('@qinglong/runtime-core/plugin-package-resource-generation');
const {
createPluginPackageSecretBindingPlan,
} = require('@qinglong/runtime-core/plugin-package-secret-binding-plan');
const {
PluginPackageSecretBindingApprovalPlanConflictError,
PluginPackageSecretBindingApprovalPlanUnavailableError,
createPluginPackageSecretBindingApprovalPlan,
} = require('@qinglong/runtime-core/plugin-package-secret-binding-approval-plan');
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
const {
PostgresPluginPackageSecretBindingApprovalPlanReader,
PostgresPluginPackageSecretBindingApprovalPlanRepository,
} = require('../dist/plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository');
function fixture() {
const manifest = {
apiVersion: PLUGIN_PACKAGE_API_VERSION,
kind: PLUGIN_PACKAGE_KIND,
metadata: {
name: 'example-monitor',
displayName: 'Example Monitor',
version: '1.0.0',
description: 'PostgreSQL Secret binding plan fixture',
license: 'Apache-2.0',
},
spec: {
compatibility: {
qinglong: '>=3.0.0-0 <4.0.0',
architectures: ['arm64'],
deploymentProfiles: ['cluster-control'],
},
runtimes: [],
resources: {
memory: { recommended: '32Mi' },
disk: { install: '4Mi', working: '8Mi' },
},
permissions: {
network: { allowedHosts: [] },
secrets: [{ name: 'TOKEN', required: true }],
tools: ['secret.use'],
},
contents: { tasks: [], workflows: [], prompts: [], tools: [] },
},
};
const environment = {
qinglongVersion: '3.0.0-alpha.0',
architecture: 'arm64',
deploymentProfile: 'cluster-control',
runtimes: [],
availableMemoryBytes: 128 * 1024 * 1024,
availableDiskBytes: 256 * 1024 * 1024,
};
const installPlan = planPluginPackageInstall(manifest, environment);
const actionInput = {
lockId: 'lock-secret-binding-1',
projectId: 'project-1',
manifest,
plan: installPlan,
environment,
source: {
kind: 'offline',
locator: `offline:sha256:${'a'.repeat(64)}`,
artifactDigest: 'a'.repeat(64),
artifactBytes: 2048,
contentDigest: 'b'.repeat(64),
},
architecture: 'arm64',
deploymentProfile: 'cluster-control',
targetGeneration: 1,
};
const proposal = createPluginPackageInstallProposal({
actionRef: 'proposal:secret-binding-install-v1',
actionInput,
proposedBy: { type: 'user', id: 'cluster-owner' },
proposalFence: { projectVersion: 1, bindingVersion: 1 },
createdAtMs: 90,
});
const lock = createPluginPackageLock({
...actionInput,
approval: {
requestId: 'approval-secret-binding-install-v1',
requestVersion: 1,
dispatchId: 'dispatch-secret-binding-install-v1',
actionDigest: pluginPackageInstallActionDigest(actionInput),
previewDigest: pluginPackageInstallPlanDigest(installPlan),
approvedBy: { type: 'user', id: 'install-reviewer' },
approvedAtMs: 100,
expiresAtMs: 1_000,
fence: { projectVersion: 1, bindingVersion: 1 },
},
createdAtMs: 200,
});
const queued = createPluginPackageInstall(lock, {
installationId: 'install-secret-binding-1',
mutationId: 'mutation-secret-binding-create',
occurredAtMs: 201,
});
const staged = transitionPluginPackageInstall(lock, queued, {
type: 'stage_completed',
mutationId: 'mutation-secret-binding-stage',
occurredAtMs: 202,
stageRef: 'stage-secret-binding-1',
artifactDigest: lock.source.artifactDigest,
manifestDigest: lock.manifestDigest,
contentDigest: lock.source.contentDigest,
evidenceDigest: 'c'.repeat(64),
});
const activating = transitionPluginPackageInstall(lock, staged, {
type: 'activation_started',
mutationId: 'mutation-secret-binding-activate',
occurredAtMs: 203,
});
const record = transitionPluginPackageInstall(lock, activating, {
type: 'activation_committed',
mutationId: 'mutation-secret-binding-commit',
occurredAtMs: 204,
activationRef: 'activation-secret-binding-1',
intentDigest: pluginPackageActivationIntentDigest(lock, activating),
generation: 1,
contentDigest: lock.source.contentDigest,
});
const generation = createPluginPackageResourceGeneration({
installationId: record.installationId,
projectId: record.projectId,
packageName: record.packageName,
lockDigest: record.lockDigest,
generation: record.targetGeneration,
previousActiveLockDigest: record.previousActiveLockDigest,
contentDigest: lock.source.contentDigest,
contents: manifest.spec.contents,
});
const bindingPlan = createPluginPackageSecretBindingPlan({
generation,
manifest,
assignments: [
{
name: 'TOKEN',
secretRef: createSecretRef({
projectId: 'project-1',
name: 'runtime-token',
version: 2,
}),
},
],
plannedAtMs: 300,
});
const approvalPlan = createPluginPackageSecretBindingApprovalPlan({
actionRef: 'secret-binding:example-monitor-v1',
bindingPlan,
requestedBy: { type: 'user', id: 'cluster-owner' },
expiresAtMs: 900,
});
return { approvalPlan, lock, proposal, record };
}
test('reads and creates one exact approval plan only through the narrow database function', async () => {
const { approvalPlan } = fixture();
const calls = [];
const repository =
new PostgresPluginPackageSecretBindingApprovalPlanRepository({
async query(text, parameters) {
calls.push({ text, parameters });
if (
text.includes('create_plugin_package_secret_binding_approval_plan')
) {
return { rows: [{ status: 'created' }] };
}
return { rows: [{ planJson: approvalPlan }] };
},
});
const result = await repository.create(approvalPlan);
assert.deepEqual(result, { status: 'created', plan: approvalPlan });
assert.equal(calls.length, 2);
assert.match(
calls[0].text,
/create_plugin_package_secret_binding_approval_plan/,
);
assert.deepEqual(calls[0].parameters, [JSON.stringify(approvalPlan)]);
assert.doesNotMatch(calls[0].text, /INSERT INTO/);
assert.match(calls[1].text, /WHERE action_ref = \$1/);
});
test('loads one current unbound generation snapshot and rejects provenance drift', async () => {
const { lock, proposal, record } = fixture();
const pool = {
async query(text, parameters) {
assert.match(text, /plugin_package_secret_binding_planning_snapshot/);
assert.deepEqual(parameters, ['project-1', 'example-monitor']);
return {
rows: [
{
recordJson: record,
lockJson: lock,
proposalJson: proposal,
observedAtMs: '500',
},
],
};
},
};
const reader = new PostgresPluginPackageSecretBindingApprovalPlanReader(pool);
assert.deepEqual(
await reader.loadPlanningSnapshot('project-1', 'example-monitor'),
{ record, lock, proposal, observedAtMs: 500 },
);
const drifted = new PostgresPluginPackageSecretBindingApprovalPlanReader({
async query() {
return {
rows: [
{
recordJson: record,
lockJson: lock,
proposalJson: { ...proposal, previewDigest: 'f'.repeat(64) },
observedAtMs: 500,
},
],
};
},
});
await assert.rejects(
drifted.loadPlanningSnapshot('project-1', 'example-monitor'),
PluginPackageSecretBindingApprovalPlanUnavailableError,
);
});
test('returns absence and fails closed on replay drift or storage conflict', async () => {
const { approvalPlan } = fixture();
const reader = new PostgresPluginPackageSecretBindingApprovalPlanReader({
async query() {
return { rows: [] };
},
});
assert.equal(await reader.findByActionRef(approvalPlan.actionRef), null);
const otherPlan = createPluginPackageSecretBindingApprovalPlan({
actionRef: approvalPlan.actionRef,
bindingPlan: approvalPlan.bindingPlan,
requestedBy: { type: 'user', id: 'another-owner' },
expiresAtMs: approvalPlan.expiresAtMs,
});
const drifted = new PostgresPluginPackageSecretBindingApprovalPlanRepository({
async query(text) {
return text.includes('create_plugin_package_secret_binding_approval_plan')
? { rows: [{ status: 'existing' }] }
: { rows: [{ planJson: otherPlan }] };
},
});
await assert.rejects(
drifted.create(approvalPlan),
PluginPackageSecretBindingApprovalPlanConflictError,
);
const conflict = new PostgresPluginPackageSecretBindingApprovalPlanRepository(
{
async query() {
const error = new Error('unique violation');
error.code = '23505';
throw error;
},
},
);
await assert.rejects(
conflict.create(approvalPlan),
PluginPackageSecretBindingApprovalPlanConflictError,
);
});
test('exports plan creation only to the Package manager and readback to the executor', () => {
const manager = require('@qinglong/cluster-postgres/package-manager');
const executor = require('@qinglong/cluster-postgres/package-executor');
assert.equal(
manager.PostgresPluginPackageSecretBindingApprovalPlanRepository,
PostgresPluginPackageSecretBindingApprovalPlanRepository,
);
assert.equal(
executor.PostgresPluginPackageSecretBindingApprovalPlanRepository,
undefined,
);
assert.equal(
executor.PostgresPluginPackageSecretBindingApprovalPlanReader,
PostgresPluginPackageSecretBindingApprovalPlanReader,
);
});
@@ -108,7 +108,10 @@ const {
} = require('@qinglong/runtime-core/plugin-package-admission');
const {
pluginPackageInstallActionDigest,
pluginPackageActivationIntentDigest,
pluginPackageInstallCommit,
pluginPackageInstallPlanDigest,
transitionPluginPackageInstall,
} = require('@qinglong/runtime-core/plugin-package-install');
const {
createPluginPackageInstallProposal,
@@ -183,6 +186,17 @@ const {
const {
createPluginPackageResourceGenerationFromReferences,
} = require('@qinglong/runtime-core/plugin-package-resource-generation');
const {
createPluginPackageSecretBindingPlan,
} = require('@qinglong/runtime-core/plugin-package-secret-binding-plan');
const {
createPluginPackageSecretBindingApprovalPlan,
} = require('@qinglong/runtime-core/plugin-package-secret-binding-approval-plan');
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
const {
PostgresPluginPackageSecretBindingApprovalPlanReader,
PostgresPluginPackageSecretBindingApprovalPlanRepository,
} = require('../dist/plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository');
const {
createProjectToolDefinitionSnapshot,
projectToolDefinitionRegistry,
@@ -291,8 +305,8 @@ function admissionPackageAction() {
},
permissions: {
network: { allowedHosts: [] },
secrets: [],
tools: [],
secrets: [{ name: 'TOKEN', required: true }],
tools: ['secret.use'],
},
contents: { tasks: [], workflows: [], prompts: [], tools: [] },
},
@@ -882,14 +896,16 @@ if (!migrationConnectionString) {
GROUP BY run.id, attempt.id`,
[accepted.runId],
);
assert.deepEqual(durable.rows, [{
status: 'queued',
version: 2,
eventSequence: 2,
trigger_type: 'task_start',
attemptStatus: 'claimed',
eventCount: 2,
}]);
assert.deepEqual(durable.rows, [
{
status: 'queued',
version: 2,
eventSequence: 2,
trigger_type: 'task_start',
attemptStatus: 'claimed',
eventCount: 2,
},
]);
} finally {
if (runtimeDatabase && runtimeDatabase !== migrationDatabase) {
await runtimeDatabase.close();
@@ -2087,12 +2103,14 @@ if (!migrationConnectionString) {
runtimeDatabase.pool,
);
assert.deepEqual(await taskAttempts.listCandidates({ limit: 8 }), {
candidates: [{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
readyAtMs: plan.plannedAtMs,
planDigest: plan.planDigest,
}],
candidates: [
{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
readyAtMs: plan.plannedAtMs,
planDigest: plan.planDigest,
},
],
truncated: false,
});
const taskAttempt = await taskAttempts.admit(
@@ -2167,29 +2185,34 @@ if (!migrationConnectionString) {
GROUP BY run.id, attempt.id, step.id`,
[plan.runId, taskAttempt.receipt.attemptId],
);
assert.deepEqual(recovered.rows, [{
runStatus: 'running',
runVersion: 6,
eventSequence: 6,
attemptStatus: 'lost',
stepStatus: 'ready',
stepVersion: 2,
stepAttemptCount: 0,
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.lost',
'step.ready',
],
}]);
assert.deepEqual(recovered.rows, [
{
runStatus: 'running',
runVersion: 6,
eventSequence: 6,
attemptStatus: 'lost',
stepStatus: 'ready',
stepVersion: 2,
stepAttemptCount: 0,
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.lost',
'step.ready',
],
},
]);
const requeued = await taskAttempts.listCandidates({ limit: 8 });
assert.deepEqual(
requeued.candidates.map(
({ runId, stepRunId }) => ({ runId, stepRunId }),
),
[{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
}],
requeued.candidates.map(({ runId, stepRunId }) => ({
runId,
stepRunId,
})),
[
{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
},
],
);
const secondTaskAttempt = await taskAttempts.admit(
plan.runId,
@@ -2242,20 +2265,22 @@ if (!migrationConnectionString) {
WHERE run.id = $1`,
[plan.runId],
);
assert.deepEqual(cancelled.rows, [{
runStatus: 'cancelled',
runVersion: 11,
eventSequence: 11,
attemptStatuses: ['cancelled', 'lost'],
stepStatuses: ['cancelled'],
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.cancelled',
'step.cancelled',
'step.cancelled',
'workflow.cancelled',
],
}]);
assert.deepEqual(cancelled.rows, [
{
runStatus: 'cancelled',
runVersion: 11,
eventSequence: 11,
attemptStatuses: ['cancelled', 'lost'],
stepStatuses: ['cancelled'],
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.cancelled',
'step.cancelled',
'step.cancelled',
'workflow.cancelled',
],
},
]);
} finally {
await runtimeDatabase.close();
}
@@ -3607,10 +3632,181 @@ if (!migrationConnectionString) {
await repository.findAdmissionReceipt(consumed.dispatch.id),
admitted.receipt,
);
const staged = transitionPluginPackageInstall(lock, admitted.record, {
type: 'stage_completed',
mutationId: 'stage-package-secret-binding-integration-1',
occurredAtMs: admittedAtMs + 1,
stageRef: `stage:${lock.lockDigest}`,
artifactDigest: lock.source.artifactDigest,
manifestDigest: lock.manifestDigest,
contentDigest: lock.source.contentDigest,
evidenceDigest: 'd'.repeat(64),
});
const stageProvenance = createPluginPackagePublisherProvenance({
projectId: staged.projectId,
packageName: staged.packageName,
installationId: staged.installationId,
lockDigest: staged.lockDigest,
artifactDigest: staged.stageReceipt.artifactDigest,
manifestDigest: staged.stageReceipt.manifestDigest,
contentDigest: staged.stageReceipt.contentDigest,
stageEvidenceDigest: staged.stageReceipt.evidenceDigest,
signature: {
publisher: 'integration.qinglong.dev',
keyId: 'integration-key-1',
signatureDigest: 'e'.repeat(64),
keyNotBeforeMs: 0,
keyNotAfterMs: admittedAtMs + 60_000,
verifiedAtMs: admittedAtMs,
},
});
await executorDatabase.pool.query(
`INSERT INTO "ql3"."plugin_package_publisher_provenance" (
installation_id, project_id, package_name, lock_digest,
artifact_digest, manifest_digest, content_digest,
stage_evidence_digest, publisher, key_id, signature_digest,
key_not_before_ms, key_not_after_ms, verified_at_ms,
provenance_digest, provenance_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11,
$12, $13, $14, $15, $16::jsonb
)`,
[
stageProvenance.installationId,
stageProvenance.projectId,
stageProvenance.packageName,
stageProvenance.lockDigest,
stageProvenance.artifactDigest,
stageProvenance.manifestDigest,
stageProvenance.contentDigest,
stageProvenance.stageEvidenceDigest,
stageProvenance.publisher,
stageProvenance.keyId,
stageProvenance.signatureDigest,
stageProvenance.keyNotBeforeMs,
stageProvenance.keyNotAfterMs,
stageProvenance.verifiedAtMs,
stageProvenance.provenanceDigest,
JSON.stringify(stageProvenance),
],
);
await repository.commit(
pluginPackageInstallCommit(admitted.record, staged),
);
const activating = transitionPluginPackageInstall(lock, staged, {
type: 'activation_started',
mutationId: 'activate-package-secret-binding-integration-1',
occurredAtMs: admittedAtMs + 2,
});
await repository.commit(pluginPackageInstallCommit(staged, activating));
const active = transitionPluginPackageInstall(lock, activating, {
type: 'activation_committed',
mutationId: 'commit-package-secret-binding-integration-1',
occurredAtMs: admittedAtMs + 3,
activationRef: `active:${lock.lockDigest}`,
intentDigest: pluginPackageActivationIntentDigest(lock, activating),
generation: lock.targetGeneration,
contentDigest: lock.source.contentDigest,
});
await repository.commit(pluginPackageInstallCommit(activating, active));
} finally {
await executorDatabase.close();
}
const bindingManagerDatabase = await open('package-manager');
let approvalPlan;
try {
const plans =
new PostgresPluginPackageSecretBindingApprovalPlanRepository(
bindingManagerDatabase.pool,
);
const snapshot = await plans.loadPlanningSnapshot(
'default',
'postgres-monitor',
);
assert.ok(snapshot);
const generation = createPluginPackageResourceGenerationFromReferences({
installationId: snapshot.record.installationId,
projectId: snapshot.record.projectId,
packageName: snapshot.record.packageName,
lockDigest: snapshot.record.lockDigest,
generation: snapshot.record.targetGeneration,
previousActiveLockDigest: snapshot.record.previousActiveLockDigest,
contentDigest: snapshot.lock.source.contentDigest,
resources: [],
});
const bindingPlan = createPluginPackageSecretBindingPlan({
generation,
manifest: snapshot.proposal.actionInput.manifest,
assignments: [
{
name: 'TOKEN',
secretRef: createSecretRef({
projectId: 'default',
name: 'postgres-monitor-token',
version: 1,
}),
},
],
plannedAtMs: snapshot.observedAtMs,
});
approvalPlan = createPluginPackageSecretBindingApprovalPlan({
actionRef: 'secret-binding:postgres-monitor-v1',
bindingPlan,
requestedBy: requester,
expiresAtMs: snapshot.observedAtMs + 60_000,
});
assert.equal((await plans.create(approvalPlan)).status, 'created');
assert.equal((await plans.create(approvalPlan)).status, 'existing');
await assert.rejects(
bindingManagerDatabase.pool.query(
`INSERT INTO "ql3"."plugin_package_secret_binding_approval_plans" (
action_ref
) VALUES ('forbidden-direct-manager-insert')`,
),
(error) => error?.code === '42501',
);
} finally {
await bindingManagerDatabase.close();
}
const bindingExecutorDatabase = await open('package-executor');
try {
assert.deepEqual(
await new PostgresPluginPackageSecretBindingApprovalPlanReader(
bindingExecutorDatabase.pool,
).findByActionRef(approvalPlan.actionRef),
approvalPlan,
);
await assert.rejects(
bindingExecutorDatabase.pool.query(
`SELECT "ql3"."create_plugin_package_secret_binding_approval_plan"(
$1::jsonb
)`,
[JSON.stringify(approvalPlan)],
),
(error) => error?.code === '42501',
);
} finally {
await bindingExecutorDatabase.close();
}
const runtimeDatabase = await open('runtime');
try {
await assert.rejects(
runtimeDatabase.pool.query(
`SELECT *
FROM "ql3"."plugin_package_secret_binding_planning_snapshot"(
'default', 'postgres-monitor'
)`,
),
(error) => error?.code === '42501',
);
} finally {
await runtimeDatabase.close();
}
const verificationDatabase = await open('migration');
try {
const facts = await verificationDatabase.pool.query(
@@ -3623,13 +3819,17 @@ if (!migrationConnectionString) {
FROM "ql3"."plugin_package_admission_receipts") AS receipts,
(SELECT count(*)::integer
FROM "ql3"."security_audit_events"
WHERE operation_id = 'plugin_package.admit') AS audits`,
WHERE operation_id = 'plugin_package.admit') AS audits,
(SELECT count(*)::integer
FROM "ql3"."plugin_package_secret_binding_approval_plans")
AS "approvalPlans"`,
);
assert.deepEqual(facts.rows[0], {
installs: 1,
mutations: 1,
mutations: 4,
receipts: 1,
audits: 1,
approvalPlans: 1,
});
} finally {
await verificationDatabase.close();
@@ -4718,10 +4918,9 @@ if (!migrationConnectionString) {
new PostgresTaskDefinitionAdministrationRepository(
automationDatabase.pool,
);
const triggerMutations =
new PostgresTriggerAdministrationRepository(
automationDatabase.pool,
);
const triggerMutations = new PostgresTriggerAdministrationRepository(
automationDatabase.pool,
);
const taskBase = {
projectId,
taskId: 'automation-task',
@@ -111,6 +111,7 @@ test('defines the immutable PostgreSQL capability and Run core stream', async ()
'pg-0058-plugin-package-automation-disposition-events',
'pg-0059-plugin-package-secret-bindings',
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
],
);
for (const migration of postgresqlMainMigrationStream.migrations) {
@@ -163,6 +164,7 @@ test('keeps local-only and legacy tables out of the cluster baseline', async ()
'plugin_package_install_mutations',
'plugin_package_materialized_revisions',
'plugin_package_secret_bindings',
'plugin_package_secret_binding_approval_plans',
'plugin_package_lifecycle_events',
'plugin_package_lifecycle_heads',
'plugin_package_lifecycle_receipts',
@@ -547,6 +549,11 @@ test('freezes every published PostgreSQL migration checksum', () => {
checksum:
'28284ca860b39ff9de5b2aa1a2a60ef2c463fd6a72798d237040174272b64b1e',
},
{
id: 'pg-0061-plugin-package-secret-binding-approval-plans',
checksum:
'c995b7846ae8a57d3abb4b5523961e81aeba890e7405a030bcb505dfc6be3d25',
},
];
assert.deepEqual(
postgresqlMainMigrationStream.migrations.map(({ id, checksum }) => ({
@@ -2030,3 +2037,49 @@ test('advances capability v59 with fail-closed Package Secret materialization',
assert.match(sql, /contract_version = 58/);
assert.match(sql, /migration_id = 'pg-0059-plugin-package-secret-bindings'/);
});
test('advances capability v60 with least-privilege Package Secret binding approval plans', async () => {
const migration = migrationById(
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const statements = [];
await migration.up({
async query(statement) {
statements.push(statement);
return { rows: [] };
},
});
const sql = statements.join('\n');
assert.match(
sql,
/CREATE TABLE "ql3"\."plugin_package_secret_binding_approval_plans"/,
);
assert.match(
sql,
/CREATE FUNCTION "ql3"\."plugin_package_secret_binding_planning_snapshot"\([\s\S]+p_project_id varchar,[\s\S]+p_package_name varchar[\s\S]+\)/,
);
assert.match(
sql,
/CREATE FUNCTION "ql3"\."create_plugin_package_secret_binding_approval_plan"\([\s\S]+p_plan_json jsonb[\s\S]+\)/,
);
assert.match(sql, /SECURITY DEFINER/);
assert.match(
sql,
/GRANT SELECT ON "ql3"\."plugin_package_secret_binding_approval_plans" TO ql3_package_manager/,
);
assert.match(
sql,
/GRANT SELECT ON "ql3"\."plugin_package_secret_binding_approval_plans" TO ql3_package_executor/,
);
assert.doesNotMatch(
sql,
/GRANT (?:INSERT|UPDATE|DELETE)[^;]+plugin_package_secret_binding_approval_plans[^;]+ql3_package_manager/,
);
assert.match(sql, /contract_version = 60/);
assert.match(sql, /"plugin_package_secret_binding_approval_plan":1/);
assert.match(sql, /contract_version = 59/);
assert.match(
sql,
/migration_id = 'pg-0060-plugin-package-secret-materialization-guard'/,
);
});
@@ -75,6 +75,7 @@ function validPrivileges() {
plugin_package_install_mutations: [false, false, false, false],
plugin_package_materialized_revisions: [false, false, false, false],
plugin_package_secret_bindings: [false, false, false, false],
plugin_package_secret_binding_approval_plans: [false, false, false, false],
project_tool_definition_snapshots: [false, false, false, false],
project_tool_definition_snapshot_sources: [false, false, false, false],
plugin_package_quarantine_events: [false, false, false, false],
@@ -194,6 +195,7 @@ function validAdminPrivileges() {
plugin_package_install_mutations: [false, false, false, false],
plugin_package_materialized_revisions: [false, false, false, false],
plugin_package_secret_bindings: [false, false, false, false],
plugin_package_secret_binding_approval_plans: [false, false, false, false],
project_tool_definition_snapshots: [false, false, false, false],
project_tool_definition_snapshot_sources: [false, false, false, false],
plugin_package_quarantine_events: [false, false, false, false],
@@ -287,6 +289,7 @@ function packagePrivileges(kind) {
'plugin_package_publisher_trust_transition_proposals',
'plugin_package_publisher_trust_transition_receipts',
'plugin_package_lifecycle_plans',
'plugin_package_secret_binding_approval_plans',
'plugin_package_automation_publications',
'plugin_package_automation_publication_heads',
...(manager
@@ -667,7 +670,13 @@ function queryable(overrides = {}) {
contract.functions.map(({ name: functionName }) => ({
functionName,
executeAllowed:
overrides.functionMode === 'manager'
overrides.functionMode === 'package-manager'
? [
'create_plugin_package_secret_binding_approval_plan',
'lock_approval_policy_fence',
'plugin_package_secret_binding_planning_snapshot',
].includes(functionName)
: overrides.functionMode === 'manager'
? functionName === 'lock_approval_policy_fence'
: overrides.functionMode === 'run-manager'
? functionName === 'lock_run_management_policy_fence'
@@ -754,7 +763,7 @@ test('accepts the exact PostgreSQL control schema and least-privilege runtime ro
serverMajor: 16,
currentUser: 'ql3_runtime',
contractName: 'control-core',
contractVersion: 59,
contractVersion: 60,
migrationIds: [
'pg-0001-schema-capability',
'pg-0002-run-core',
@@ -816,6 +825,7 @@ test('accepts the exact PostgreSQL control schema and least-privilege runtime ro
'pg-0058-plugin-package-automation-disposition-events',
'pg-0059-plugin-package-secret-bindings',
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
],
});
});
@@ -846,10 +856,10 @@ test('accepts the exact schema and isolated least-privilege admin role', async (
}),
);
assert.equal(report.currentUser, 'ql3_admin');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
});
@@ -862,10 +872,10 @@ test('accepts the isolated least-privilege automation manager role', async () =>
}),
);
assert.equal(report.currentUser, 'ql3_automation_manager');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const widened = automationManagerPrivileges();
@@ -894,10 +904,10 @@ test('accepts the isolated least-privilege human Approval manager role', async (
}),
);
assert.equal(report.currentUser, 'ql3_approval_manager');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const widened = approvalManagerPrivileges();
@@ -928,10 +938,10 @@ test('accepts the isolated least-privilege Run manager role', async () => {
}),
);
assert.equal(report.currentUser, 'ql3_run_manager');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const widened = runManagerPrivileges();
@@ -983,7 +993,7 @@ test('accepts isolated Package manager and executor roles', async () => {
queryable({
currentUser: 'ql3_package_manager',
privileges: packagePrivileges('manager'),
functionMode: 'manager',
functionMode: 'package-manager',
}),
);
assert.equal(manager.currentUser, 'ql3_package_manager');
@@ -1063,10 +1073,10 @@ test('accepts the exact schema and isolated Worker ingress role', async () => {
}),
);
assert.equal(report.currentUser, 'ql3_worker_ingress');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
});