const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const { test } = require('node:test'); const { PluginPackageManagementLiveEvidenceCollectionError, buildLiveEvidenceReport, collectDatabaseSnapshot, collectKubernetesSnapshot, collectOidcSnapshot, parseArguments, policyIsBounded, validateExercise, } = require('../../scripts/ql3-plugin-package-management-live-evidence-collect.cjs'); const { validatePluginPackageManagementLiveEvidence, } = require('../../scripts/ql3-plugin-package-management-live-evidence-audit.cjs'); function sha(character) { return `sha256:${character.repeat(64)}`; } function conditionReady() { return [{ type: 'Ready', status: 'True' }]; } function node(name, uid, role) { return { metadata: { name, uid, labels: { 'kubernetes.io/arch': 'arm64', ...(role === 'control-plane' ? { 'node-role.kubernetes.io/control-plane': '' } : {}), }, }, spec: {}, status: { conditions: conditionReady() }, }; } function pod(name, uid, nodeName, imageId, containerName = 'management') { return { metadata: { name, uid }, spec: { nodeName, serviceAccountName: 'ql3-plugin-package-management', automountServiceAccountToken: false, }, status: { conditions: conditionReady(), containerStatuses: [ { name: containerName, ready: true, imageID: `containerd://${imageId}`, }, ], }, }; } function validExercise() { return { schemaVersion: 1, fixture: 'qinglong/plugin-package-management-live-exercise@v1', observedAt: '2026-07-25T12:00:00.000Z', identity: { issuer: 'https://login.example.com/', audience: 'qinglong3-package-management', requesterSubject: 'tenant-a/requester-42', reviewerSubject: 'tenant-a/reviewer-17', requesterAssurance: 'multi_factor', reviewerAssurance: 'hardware', keysetGenerations: [7, 8, 9], }, ceremony: { proposalAuditEventId: '123e4567-e89b-42d3-a456-426614174000', approvalAuditEventId: '123e4567-e89b-42d3-a456-426614174001', decisionAuditEventId: '123e4567-e89b-42d3-a456-426614174002', proposeStatus: 200, proposeOperation: 'plugin-package.propose', selfDecisionStatus: 403, selfDecisionError: 'forbidden', reviewerDecisionStatus: 200, reviewerDecisionOperation: 'plugin-package.decide', inspectionStatus: 200, inspectionOperation: 'plugin-package.inspect', }, isolation: { labelledClientOutcome: 'tls13_connected', unlabelledClientOutcome: 'timeout', wrongPortOutcome: 'timeout', kubernetesApiEgressOutcome: 'timeout', publicInternetEgressOutcome: 'timeout', postgresEgressOutcome: 'postgres_ready', }, rotation: { overlapOldStatus: 200, newStatus: 200, revokedOldStatus: 401, revokedOldError: 'authentication_required', previousTlsSerial: '01:A2:03', currentTlsSerial: '09:B8:07', previousTlsSecretResourceVersion: '48192', currentTlsSecretResourceVersion: '49201', readinessSamples: ['before', 'overlap', 'revoked'].map((phase) => ({ phase, replicas: 2, readyReplicas: 2, unavailableReplicas: 0, tlsProtocol: 'TLSv1.3', })), }, }; } function migrationIds() { return Array.from({ length: 25 }, (_, index) => index === 24 ? 'pg-0025-plugin-package-materialized-revisions' : `pg-${String(index + 1).padStart(4, '0')}-migration`, ); } function databaseSnapshot() { return { currentRole: 'ql3_package_manager', serverVersionNumber: 180004, migrationIds: migrationIds(), controlCoreCapability: 24, tableCount: 38, auditRows: 3, ledger: { generation: 9, issuer: 'https://login.example.com/', audience: 'qinglong3-package-management', revokedKeyCount: 2, }, }; } function kubernetesSnapshot() { return { kubernetesVersion: 'v1.34.2', architecture: 'arm64', managementImageId: sha('1'), postgresImageId: sha('2'), cniName: 'cilium', cniVersion: '1.17.1', controlPlaneNodes: 3, workerNodes: 2, replicas: 2, readyReplicas: 2, podIdentitySha256: [sha('3'), sha('4')], nodeIdentitySha256: [sha('5'), sha('6')], serviceAccount: 'ql3-plugin-package-management', automountServiceAccountToken: false, boundedNetworkPolicy: true, managerSecretReadDenied: true, managerExecutorMutationDenied: true, }; } function kubectlFixture() { const nodes = [ node('cp-a', 'node-cp-a', 'control-plane'), node('cp-b', 'node-cp-b', 'control-plane'), node('cp-c', 'node-cp-c', 'control-plane'), node('worker-a', 'node-worker-a', 'worker'), node('worker-b', 'node-worker-b', 'worker'), ]; const managementImage = sha('1'); const postgresImage = sha('2'); const values = new Map([ [ 'version -o json', { clientVersion: {}, serverVersion: { gitVersion: 'v1.34.2' } }, ], ['get nodes -o json', { items: nodes }], [ '-n qinglong3-system get deployment ql3-plugin-package-management -o json', { spec: { replicas: 2 }, status: { readyReplicas: 2, unavailableReplicas: 0 }, }, ], [ '-n qinglong3-system get service ql3-plugin-package-management -o json', { spec: { ports: [{ port: 8443, protocol: 'TCP' }] }, }, ], [ '-n qinglong3-system get serviceaccount ql3-plugin-package-management -o json', { metadata: { name: 'ql3-plugin-package-management' }, automountServiceAccountToken: false, }, ], [ '-n qinglong3-system get networkpolicy ql3-plugin-package-management -o json', { spec: { podSelector: { matchLabels: { 'app.kubernetes.io/name': 'ql3-plugin-package-management', 'app.kubernetes.io/component': 'plugin-package-management', }, }, policyTypes: ['Ingress', 'Egress'], ingress: [ { from: [ { podSelector: { matchLabels: { 'qinglong.io/plugin-package-management-client': 'true', }, }, }, ], ports: [{ port: 8443, protocol: 'TCP' }], }, ], egress: [ { to: [ { namespaceSelector: { matchLabels: { 'kubernetes.io/metadata.name': 'kube-system', }, }, podSelector: { matchLabels: { 'k8s-app': 'kube-dns' } }, }, ], ports: [ { port: 53, protocol: 'UDP' }, { port: 53, protocol: 'TCP' }, ], }, { to: [ { podSelector: { matchLabels: { 'cnpg.io/cluster': 'ql3-postgres' }, }, }, ], ports: [{ port: 5432, protocol: 'TCP' }], }, ], }, }, ], [ '-n qinglong3-system get pods -l app.kubernetes.io/name=ql3-plugin-package-management,app.kubernetes.io/component=plugin-package-management -o json', { items: [ pod('management-a', 'pod-a', 'worker-a', managementImage), pod('management-b', 'pod-b', 'worker-b', managementImage), ], }, ], [ '-n kube-system get daemonset cilium -o json', { spec: { template: { spec: { containers: [ { name: 'cilium-agent', image: 'quay.io/cilium/cilium:v1.17.1', }, ], }, }, }, status: { desiredNumberScheduled: 5, numberReady: 5 }, }, ], [ '-n qinglong3-system get pods -l cnpg.io/cluster=ql3-postgres -o json', { items: [ pod('postgres-a', 'pg-a', 'worker-a', postgresImage, 'postgres'), pod('postgres-b', 'pg-b', 'worker-b', postgresImage, 'postgres'), pod('postgres-c', 'pg-c', 'worker-a', postgresImage, 'postgres'), ], }, ], ]); return (args) => { const command = args.join(' '); if (command.startsWith('auth can-i ')) { return { status: 0, stdout: 'no\n', stderr: '' }; } const value = values.get(command); assert.ok(value, `unexpected kubectl command: ${command}`); return { status: 0, stdout: JSON.stringify(value), stderr: '' }; }; } test('collects immutable Kubernetes topology, images, CNI and denied authority', () => { const result = collectKubernetesSnapshot( { cniDaemonSet: { namespace: 'kube-system', name: 'cilium' }, cniContainer: 'cilium-agent', cniName: 'cilium', }, kubectlFixture(), ); assert.equal(result.kubernetesVersion, 'v1.34.2'); assert.equal(result.controlPlaneNodes, 3); assert.equal(result.workerNodes, 2); assert.equal(result.managementImageId, sha('1')); assert.equal(result.postgresImageId, sha('2')); assert.equal(result.cniVersion, '1.17.1'); assert.equal(result.managerSecretReadDenied, true); assert.equal(result.managerExecutorMutationDenied, true); assert.equal(new Set(result.podIdentitySha256).size, 2); assert.equal(new Set(result.nodeIdentitySha256).size, 2); }); test('rejects empty selectors and any live NetworkPolicy widening', () => { assert.equal( policyIsBounded({ spec: { podSelector: { matchLabels: { 'app.kubernetes.io/name': 'ql3-plugin-package-management', 'app.kubernetes.io/component': 'plugin-package-management', }, }, policyTypes: ['Ingress', 'Egress'], ingress: [ { from: [{ podSelector: {} }], ports: [{ port: 8443, protocol: 'TCP' }], }, ], egress: [ { to: [{}], ports: [{ port: 443, protocol: 'TCP' }], }, ], }, }), false, ); }); test('queries PostgreSQL through the manager role and binds audit plus ledger', () => { const exercise = validateExercise( validExercise(), Date.parse('2026-07-25T12:01:00.000Z'), ); let invocation; const result = collectDatabaseSnapshot( exercise, { pgService: 'ql3_evidence' }, (args) => { invocation = args; return { status: 0, stdout: JSON.stringify(databaseSnapshot()), stderr: '', }; }, ); assert.equal(result.currentRole, 'ql3_package_manager'); assert.equal(result.auditRows, 3); assert.ok(invocation.includes('--dbname=service=ql3_evidence')); const sql = invocation.find((value) => value.startsWith('--command=')); assert.match(sql, /plugin_package_identity_keyset_ledger/); assert.doesNotMatch(sql, /password|postgres(?:ql)?:\/\//i); }); test('fetches and hashes the exercised external OIDC discovery and JWKS', async () => { const calls = []; const documents = new Map([ [ 'https://login.example.com/.well-known/openid-configuration', JSON.stringify({ issuer: 'https://login.example.com/', jwks_uri: 'https://login.example.com/keys', }), ], [ 'https://login.example.com/keys', JSON.stringify({ keys: [{ kid: 'production-key-1', kty: 'OKP' }] }), ], ]); const result = await collectOidcSnapshot( validExercise().identity, async (url, options) => { calls.push({ url, options }); const body = Buffer.from(documents.get(url)); return { status: 200, async arrayBuffer() { return body; }, }; }, ); assert.equal(calls.length, 2); assert.ok(result.discoveryDocumentSha256.startsWith('sha256:')); assert.ok(result.jwksSha256.startsWith('sha256:')); assert.notEqual(result.discoveryDocumentSha256, result.jwksSha256); }); test('rejects OIDC SSRF targets and oversized streamed metadata before accepting evidence', async () => { let calls = 0; await assert.rejects( collectOidcSnapshot( { ...validExercise().identity, issuer: 'https://127.0.0.1/', }, async () => { calls += 1; }, ), PluginPackageManagementLiveEvidenceCollectionError, ); assert.equal(calls, 0); await assert.rejects( collectOidcSnapshot(validExercise().identity, async () => ({ status: 200, headers: { get: () => null }, body: { async *[Symbol.asyncIterator]() { yield Buffer.alloc(1024 * 1024); yield Buffer.from('x'); }, }, })), /exceeds 1 MiB/, ); }); test('derives a compatible report without leaking raw subjects, event IDs or TLS identities', () => { const exercise = validateExercise( validExercise(), Date.parse('2026-07-25T12:01:00.000Z'), ); const report = buildLiveEvidenceReport({ kubernetes: kubernetesSnapshot(), database: databaseSnapshot(), oidc: { discoveryDocumentSha256: sha('7'), jwksSha256: sha('8'), }, exercise, observedAt: '2026-07-25T12:02:00.000Z', }); assert.equal( validatePluginPackageManagementLiveEvidence(report).compatible, true, ); const serialized = JSON.stringify(report); assert.doesNotMatch(serialized, /tenant-a\/requester-42/); assert.doesNotMatch(serialized, /123e4567-e89b-42d3-a456-426614174000/); assert.doesNotMatch(serialized, /01:A2:03/); assert.doesNotMatch(serialized, /48192/); }); test('rejects claimed gates and non-private collection inputs', () => { const claimed = validExercise(); claimed.gates = { passed: true }; assert.throws( () => validateExercise(claimed, Date.parse('2026-07-25T12:01:00.000Z')), PluginPackageManagementLiveEvidenceCollectionError, ); const directory = fs.mkdtempSync( path.join(os.tmpdir(), 'ql3-live-evidence-collector-'), ); try { const files = ['kubeconfig', 'exercise.json', 'pg-service.conf']; for (const name of files) { fs.writeFileSync( path.join(directory, name), name === 'exercise.json' ? '{}' : 'private', { mode: 0o600 }, ); } fs.chmodSync(path.join(directory, 'exercise.json'), 0o640); assert.throws( () => parseArguments([ `--kubeconfig=${path.join(directory, 'kubeconfig')}`, '--context=production', '--cni-daemonset=kube-system/cilium', '--cni-container=cilium-agent', '--cni-name=cilium', `--exercise=${path.join(directory, 'exercise.json')}`, `--pg-service-file=${path.join(directory, 'pg-service.conf')}`, '--pg-service=ql3_evidence', `--output=${path.join(directory, 'report.json')}`, ]), PluginPackageManagementLiveEvidenceCollectionError, ); } finally { fs.rmSync(directory, { recursive: true, force: true }); } });