'use strict'; const assert = require('node:assert/strict'); const crypto = require('node:crypto'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const test = require('node:test'); const { auditClusterOciLayout, } = require('../../scripts/ql3-cluster-oci-layout-audit.cjs'); const { mergeNativeLayouts, nativeEvidenceRecord, } = require('../../scripts/ql3-image-release-bundle.cjs'); const { createClusterImageSbom, } = require('../../scripts/ql3-cluster-image-sbom.cjs'); const { readReleaseIdentity, } = require('../../scripts/lib/ql3-release-identity.cjs'); const root = path.resolve(__dirname, '../..'); const version = readReleaseIdentity(root).version; const revision = 'fixture-revision'; function createFixture(t, options = {}) { const image = options.image || 'control'; const isControl = image === 'control' || image === 'control-ai'; const isControlAi = image === 'control-ai'; const isLocal = image === 'local'; const isLocalOperator = image === 'local-operator'; const isWorker = image === 'worker'; const layoutRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-oci-layout-')); t.after(() => fs.rmSync(layoutRoot, { recursive: true, force: true })); const blobDirectory = path.join(layoutRoot, 'blobs', 'sha256'); fs.mkdirSync(blobDirectory, { recursive: true }); fs.writeFileSync( path.join(layoutRoot, 'oci-layout'), JSON.stringify({ imageLayoutVersion: '1.0.0' }), ); function blob(value, mediaType) { const content = Buffer.isBuffer(value) ? value : Buffer.from(JSON.stringify(value)); const digest = `sha256:${crypto .createHash('sha256') .update(content) .digest('hex')}`; fs.writeFileSync( path.join(blobDirectory, digest.slice('sha256:'.length)), content, ); return { mediaType, digest, size: content.length }; } const expectedComponents = createClusterImageSbom({ root, image, }).components; const applicationPackages = expectedComponents.map((component) => ({ name: component.name, versionInfo: component.version, sourceInfo: `acquired package info from installed node module manifest file: /opt/qinglong/node_modules/${component.name}/package.json`, externalRefs: [ { referenceCategory: 'PACKAGE-MANAGER', referenceType: 'purl', referenceLocator: component.purl, }, ], })); if (options.addDevelopmentPackage) { applicationPackages.push({ name: '@types/node', versionInfo: '24.13.3', sourceInfo: 'acquired package info from installed node module manifest file: /opt/qinglong/node_modules/@types/node/package.json', externalRefs: [ { referenceCategory: 'PACKAGE-MANAGER', referenceType: 'purl', referenceLocator: 'pkg:npm/%40types/node@24.13.3', }, ], }); } const descriptors = []; const imageDescriptors = []; for (const architecture of options.onlyArchitecture ? [options.onlyArchitecture] : options.omitArm64 ? ['amd64'] : ['amd64', 'arm64']) { const config = blob( { architecture, os: 'linux', config: { User: options.rootArm64 && architecture === 'arm64' ? '0:0' : isWorker ? '65532:65532' : isLocal || isLocalOperator ? '65532:65532' : '10001:10001', ...(isControl ? { ExposedPorts: { '5800/tcp': {} } } : {}), Env: [ 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 'NODE_VERSION=24.18.0', 'YARN_VERSION=1.22.22', 'NODE_ENV=production', ], Entrypoint: [ 'node', isWorker ? '/opt/qinglong/node_modules/@qinglong/worker-runtime/dist/process/workerProcessCli.js' : isLocal ? '/opt/qinglong/node_modules/@qinglong/local-application/dist/cli.js' : isLocalOperator ? '/opt/qinglong/node_modules/@qinglong/local-owner-cli/dist/product-cli/cli.js' : isControl ? isControlAi ? '/opt/qinglong/node_modules/@qinglong/cluster-control/dist/aiCli.js' : '/opt/qinglong/node_modules/@qinglong/cluster-control/dist/cli.js' : '/opt/qinglong/node_modules/@qinglong/cluster-admin/dist/product-cli/cli.js', ], WorkingDir: '/opt/qinglong', Labels: { ...(isWorker ? { 'io.qinglong.profile': 'worker', 'io.qinglong.worker.capacity-profiles': 'edge,node', } : {}), ...(isLocal ? { 'io.qinglong.ai': 'excluded', 'io.qinglong.local.application-config': '2,3,4', 'io.qinglong.local.compose-selection': '1', 'io.qinglong.local.sqlite-contract-max': '52', 'io.qinglong.local.sqlite-contract-min': '51', 'io.qinglong.local.sqlite-write-contract': '52', 'io.qinglong.profile': 'edge,standalone', } : {}), ...(isLocalOperator ? { 'io.qinglong.authority': 'local-owner-management', 'io.qinglong.lifecycle': 'short-lived', 'io.qinglong.network': 'none-by-default', } : {}), 'org.opencontainers.image.description': isLocal ? 'QingLong 3.0 AI-excluded Edge and Standalone runtime' : isLocalOperator ? 'QingLong 3.0 short-lived Local management authority' : isWorker ? 'QingLong 3.0 headless Remote Worker runtime' : isControl ? isControlAi ? 'Optional QingLong 3.0 AI-enabled cluster control plane' : 'QingLong 3.0 PostgreSQL-backed cluster control plane' : 'QingLong 3.0 cluster operations and bounded Copilot surfaces', 'org.opencontainers.image.licenses': 'Apache-2.0', 'org.opencontainers.image.revision': revision, 'org.opencontainers.image.source': 'https://github.com/whyour/qinglong', 'org.opencontainers.image.title': isLocal ? 'QingLong 3.0 Local Application' : isLocalOperator ? 'QingLong 3.0 Local Operator' : isWorker ? 'QingLong 3.0 Worker' : isControl ? isControlAi ? 'QingLong 3.0 Cluster Control AI' : 'QingLong 3.0 Cluster Control' : 'QingLong 3.0 Cluster Admin', ...(isLocal || isLocalOperator || isWorker || isControl || image === 'admin' ? { 'org.opencontainers.image.version': version, } : {}), }, }, rootfs: { type: 'layers', diff_ids: [ `sha256:${(architecture === 'amd64' ? 'a' : 'b').repeat(64)}`, ], }, }, 'application/vnd.oci.image.config.v1+json', ); const layer = blob( Buffer.from(`fixture-${architecture}`), 'application/vnd.oci.image.layer.v1.tar+gzip', ); const manifest = blob( { schemaVersion: 2, mediaType: 'application/vnd.oci.image.manifest.v1+json', config, layers: [layer], }, 'application/vnd.oci.image.manifest.v1+json', ); const descriptor = { ...manifest, platform: { architecture, os: 'linux' }, }; imageDescriptors.push(descriptor); descriptors.push(descriptor); } for (const imageDescriptor of imageDescriptors) { const spdx = blob( { _type: 'https://in-toto.io/Statement/v1', subject: [], predicateType: 'https://spdx.dev/Document', predicate: { spdxVersion: 'SPDX-2.3', packages: applicationPackages, }, }, 'application/vnd.in-toto+json', ); spdx.annotations = { 'in-toto.io/predicate-type': 'https://spdx.dev/Document', }; const provenance = blob( { _type: 'https://in-toto.io/Statement/v1', subject: [], predicateType: 'https://slsa.dev/provenance/v1', predicate: { buildDefinition: { buildType: 'https://github.com/moby/buildkit/blob/master/docs/attestations/slsa-definitions.md', externalParameters: { request: { frontend: 'dockerfile.v0', args: { 'build-arg:SOURCE_REVISION': revision, }, }, }, }, runDetails: { builder: { id: '' } }, }, }, 'application/vnd.in-toto+json', ); provenance.annotations = { 'in-toto.io/predicate-type': 'https://slsa.dev/provenance/v1', }; const attestationLayers = options.omitProvenance ? [spdx] : [spdx, provenance]; const attestationConfig = options.modernAttestationConfig ? blob({}, 'application/vnd.oci.empty.v1+json') : blob( { architecture: 'unknown', os: 'unknown', config: {}, rootfs: { type: 'layers', diff_ids: attestationLayers.map((layer) => layer.digest), }, }, 'application/vnd.oci.image.config.v1+json', ); const attestationManifest = blob( { schemaVersion: 2, mediaType: 'application/vnd.oci.image.manifest.v1+json', config: attestationConfig, layers: attestationLayers, }, 'application/vnd.oci.image.manifest.v1+json', ); descriptors.push({ ...attestationManifest, annotations: { 'vnd.docker.reference.digest': options.unboundAttestation ? `sha256:${'f'.repeat(64)}` : imageDescriptor.digest, 'vnd.docker.reference.type': 'attestation-manifest', }, platform: { architecture: 'unknown', os: 'unknown' }, }); } if (!options.onlyArchitecture) { while (descriptors.length < 4) { descriptors.push({ ...descriptors[descriptors.length - 1], digest: `sha256:${'0'.repeat(64)}`, }); } } const imageIndex = blob( { schemaVersion: 2, mediaType: 'application/vnd.oci.image.index.v1+json', manifests: descriptors, }, 'application/vnd.oci.image.index.v1+json', ); fs.writeFileSync( path.join(layoutRoot, 'index.json'), JSON.stringify({ schemaVersion: 2, mediaType: 'application/vnd.oci.image.index.v1+json', manifests: [imageIndex], }), ); return layoutRoot; } test('accepts two exact images with bound SBOM and provenance', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t), expectedRevision: revision, }); assert.equal(report.platforms.length, 2); assert.deepEqual( report.platforms.map((entry) => entry.platform), ['linux/amd64', 'linux/arm64'], ); assert.deepEqual( report.platforms.map((entry) => entry.spdxApplicationPackages), [46, 46], ); }); test('accepts OCI 1.1 empty attestation configs emitted by current BuildKit', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { modernAttestationConfig: true }), expectedRevision: revision, }); assert.equal(report.platforms.length, 2); }); test('accepts the independent cluster-admin image and attestation closure', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { image: 'admin' }), expectedRevision: revision, image: 'admin', }); assert.equal(report.image, 'admin'); assert.deepEqual( report.platforms.map((entry) => entry.spdxApplicationPackages), [91, 91], ); }); test('accepts the optional Cluster AI image and attestation closure', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { image: 'control-ai' }), expectedRevision: revision, image: 'control-ai', }); assert.equal(report.image, 'control-ai'); assert.deepEqual( report.platforms.map((entry) => entry.spdxApplicationPackages), [47, 47], ); }); test('accepts the AI-excluded local image and attestation closure', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { image: 'local' }), expectedRevision: revision, image: 'local', }); assert.equal(report.image, 'local'); assert.equal(report.maximumPlatformBytes, 128 * 1024 * 1024); assert.deepEqual( report.platforms.map((entry) => entry.spdxApplicationPackages), [10, 10], ); }); test('accepts the short-lived Local operator image and attestation closure', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { image: 'local-operator' }), expectedRevision: revision, image: 'local-operator', }); assert.equal(report.image, 'local-operator'); assert.equal(report.maximumPlatformBytes, 128 * 1024 * 1024); assert.deepEqual( report.platforms.map((entry) => entry.spdxApplicationPackages), [9, 9], ); }); test('accepts the headless Worker image and attestation closure', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { image: 'worker' }), expectedRevision: revision, image: 'worker', }); assert.equal(report.image, 'worker'); assert.deepEqual( report.platforms.map((platform) => platform.platform), ['linux/amd64', 'linux/arm64'], ); }); test('rejects cluster-control config presented as cluster-admin evidence', (t) => { assert.throws( () => auditClusterOciLayout({ root, layoutRoot: createFixture(t), expectedRevision: revision, image: 'admin', }), /SPDX application closure differs|image config differs/, ); }); test('rejects an absent arm64 image', (t) => { assert.throws( () => auditClusterOciLayout({ root, layoutRoot: createFixture(t, { omitArm64: true }), expectedRevision: revision, }), /platform set/, ); }); test('rejects a root runtime config on either architecture', (t) => { assert.throws( () => auditClusterOciLayout({ root, layoutRoot: createFixture(t, { rootArm64: true }), expectedRevision: revision, }), /image config differs/, ); }); test('rejects an attestation that is not bound to its image digest', (t) => { assert.throws( () => auditClusterOciLayout({ root, layoutRoot: createFixture(t, { unboundAttestation: true }), expectedRevision: revision, }), /one bound attestation/, ); }); test('rejects an incomplete predicate set', (t) => { assert.throws( () => auditClusterOciLayout({ root, layoutRoot: createFixture(t, { omitProvenance: true }), expectedRevision: revision, }), /exactly SBOM and provenance/, ); }); test('rejects a development package in the image SBOM closure', (t) => { assert.throws( () => auditClusterOciLayout({ root, layoutRoot: createFixture(t, { addDevelopmentPackage: true }), expectedRevision: revision, }), /SPDX application closure differs/, ); }); test('accepts one native layout with one bound attestation', (t) => { const report = auditClusterOciLayout({ root, layoutRoot: createFixture(t, { onlyArchitecture: 'arm64' }), expectedRevision: revision, expectedPlatforms: ['linux/arm64'], }); assert.deepEqual( report.platforms.map((entry) => entry.platform), ['linux/arm64'], ); }); test('merges two scanned native layouts into the exact audited multiarch digest', (t) => { const amd64Layout = createFixture(t, { onlyArchitecture: 'amd64' }); const arm64Layout = createFixture(t, { onlyArchitecture: 'arm64' }); const outputParent = fs.realpathSync( fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-release-bundle-')), ); t.after(() => fs.rmSync(outputParent, { recursive: true, force: true })); const amd64Evidence = path.join(outputParent, 'amd64-evidence.json'); const arm64Evidence = path.join(outputParent, 'arm64-evidence.json'); fs.writeFileSync( amd64Evidence, JSON.stringify( nativeEvidenceRecord({ root, layoutRoot: amd64Layout, expectedRevision: revision, image: 'control', platform: 'linux/amd64', }), ), ); fs.writeFileSync( arm64Evidence, JSON.stringify( nativeEvidenceRecord({ root, layoutRoot: arm64Layout, expectedRevision: revision, image: 'control', platform: 'linux/arm64', }), ), ); const outputRoot = path.join(outputParent, 'merged'); const predicatePath = path.join(outputParent, 'predicate.json'); const reportPath = path.join(outputParent, 'report.json'); const report = mergeNativeLayouts({ root, image: 'control', expectedRevision: revision, amd64Layout, amd64Evidence, arm64Layout, arm64Evidence, outputRoot, predicatePath, reportPath, }); assert.match(report.rootIndexDigest, /^sha256:[0-9a-f]{64}$/); assert.equal( JSON.parse(fs.readFileSync(predicatePath, 'utf8')).subjectDigest, report.rootIndexDigest, ); assert.equal( auditClusterOciLayout({ root, layoutRoot: outputRoot, expectedRevision: revision, image: 'control', }).rootIndexDigest, report.rootIndexDigest, ); }); test('rejects native evidence that does not exactly describe its OCI layout', (t) => { const amd64Layout = createFixture(t, { onlyArchitecture: 'amd64' }); const arm64Layout = createFixture(t, { onlyArchitecture: 'arm64' }); const outputParent = fs.realpathSync( fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-release-bundle-mismatch-')), ); t.after(() => fs.rmSync(outputParent, { recursive: true, force: true })); const amd64Evidence = path.join(outputParent, 'amd64-evidence.json'); const arm64Evidence = path.join(outputParent, 'arm64-evidence.json'); const amd64Record = nativeEvidenceRecord({ root, layoutRoot: amd64Layout, expectedRevision: revision, image: 'control', platform: 'linux/amd64', }); fs.writeFileSync( amd64Evidence, JSON.stringify({ ...amd64Record, sourceRevision: 'different' }), ); fs.writeFileSync( arm64Evidence, JSON.stringify( nativeEvidenceRecord({ root, layoutRoot: arm64Layout, expectedRevision: revision, image: 'control', platform: 'linux/arm64', }), ), ); assert.throws( () => mergeNativeLayouts({ root, image: 'control', expectedRevision: revision, amd64Layout, amd64Evidence, arm64Layout, arm64Evidence, outputRoot: path.join(outputParent, 'merged'), predicatePath: path.join(outputParent, 'predicate.json'), reportPath: path.join(outputParent, 'report.json'), }), /native vulnerability evidence differs for linux\/amd64/, ); });