Files
qinglong/scripts/ql3-barman-cloud-supply-chain-audit.cjs

172 lines
5.6 KiB
JavaScript

#!/usr/bin/env node
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const LOCK_DIRECTORY = 'deploy/kubernetes/ql3-cluster/operators/barman-cloud';
const RELEASE_MANIFEST =
'https://github.com/cloudnative-pg/plugin-barman-cloud/releases/download/v0.13.0/manifest.yaml';
const RELEASE_MANIFEST_SHA256 =
'd2e71e7b06822448f1a421f05781846cfdb9cc621e7ef32eef5e20c5133213b0';
const RELEASE_BLOCKERS = Object.freeze([
'live-object-store-backup-wal-latest-restore-pitr-evidence',
]);
const CONTROLLER = Object.freeze({
manifestReference: 'ghcr.io/cloudnative-pg/plugin-barman-cloud:v0.13.0',
image:
'ghcr.io/cloudnative-pg/plugin-barman-cloud:v0.13.0@sha256:71589dbac582333442812b07b31f7ea4d00324a8358aac7ca507dabf9f4b6c96',
platforms: Object.freeze({
'linux/amd64':
'sha256:417449fe4f6f0a56acdeb30e4131930815f2b46b9afeb808059b57aa8b4c2ef5',
'linux/arm64':
'sha256:de612e3ad8633a198b91ffbea53848407424155daf2183d656490d843a83b100',
}),
});
const SIDECAR = Object.freeze({
manifestReference:
'ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar:v0.13.0',
image:
'ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar:v0.13.0@sha256:990361af3319f9e23aafa0f6d7981f99bf1f69b4e6a85cf1bc7d71d6f09bb288',
platforms: Object.freeze({
'linux/amd64':
'sha256:15cb1a01e7c5235eedac2061cab8208e5f7c39dbda292f9c2d4ddaa0c1f211e6',
'linux/arm64':
'sha256:f53e168e341661cd76334215ead9dfd69f06117685d3232206192cf25218da71',
}),
});
function finding(code, detail) {
return Object.freeze({ code, detail });
}
function exactJson(actual, expected) {
return JSON.stringify(actual) === JSON.stringify(expected);
}
function auditBarmanCloudSupplyChain(options = {}) {
const root = path.resolve(options.root ?? path.join(__dirname, '..'));
const readFile = options.readFile ?? fs.readFileSync;
const readDirectory = options.readDirectory ?? fs.readdirSync;
const findings = [];
try {
const directory = path.join(root, LOCK_DIRECTORY);
const entries = [...readDirectory(directory)].sort();
if (!exactJson(entries, ['plugin-lock.json'])) {
findings.push(
finding(
'QL3_BARMAN_INSTALLER_UNREVIEWED',
'the candidate lock directory must not contain an install manifest before the certificate authority and image rewrite are reviewed',
),
);
}
const lock = JSON.parse(
readFile(path.join(directory, 'plugin-lock.json'), 'utf8'),
);
if (
lock?.schemaVersion !== 1 ||
lock?.observedAt !== '2026-08-03' ||
lock?.plugin?.name !== 'barman-cloud.cloudnative-pg.io' ||
lock?.plugin?.version !== '0.13.0' ||
lock?.compatibility?.cloudNativePg !== '>=1.26.0' ||
lock?.compatibility?.reviewedCloudNativePg !== '1.30.0'
) {
findings.push(
finding(
'QL3_BARMAN_LOCK_ENVELOPE',
'the plugin candidate must stay bound to the reviewed Barman and CloudNativePG versions',
),
);
}
if (
lock?.plugin?.releaseManifest !== RELEASE_MANIFEST ||
lock?.plugin?.releaseManifestSha256 !== RELEASE_MANIFEST_SHA256
) {
findings.push(
finding(
'QL3_BARMAN_RELEASE_ASSET',
'the official release manifest URL and observed SHA-256 must be exact',
),
);
}
if (!exactJson(lock?.plugin?.controller, CONTROLLER)) {
findings.push(
finding(
'QL3_BARMAN_CONTROLLER_IMAGE',
'the controller index and native amd64/arm64 manifests must remain digest locked',
),
);
}
if (!exactJson(lock?.plugin?.sidecar, SIDECAR)) {
findings.push(
finding(
'QL3_BARMAN_SIDECAR_IMAGE',
'the PostgreSQL sidecar index and native amd64/arm64 manifests must remain digest locked',
),
);
}
if (
lock?.certificateAuthority?.mode !== 'cert-manager' ||
lock?.certificateAuthority?.version !== '1.20.3' ||
lock?.certificateAuthority?.selectionLock !==
'deploy/kubernetes/ql3-cluster/operators/cert-manager/selection-lock.json' ||
lock?.certificateAuthority?.releaseManifestUses !==
'cert-manager.io/v1' ||
!exactJson(lock?.certificateAuthority?.requiredSecrets, [
'barman-cloud-client-tls',
'barman-cloud-server-tls',
]) ||
lock?.certificateAuthority?.status !== 'supply-chain-verified'
) {
findings.push(
finding(
'QL3_BARMAN_CERTIFICATE_GATE',
'the certificate authority dependency must remain supply-chain locked while live readiness fails closed independently',
),
);
}
if (
lock?.releaseReady !== false ||
!exactJson(lock?.releaseBlockers, RELEASE_BLOCKERS)
) {
findings.push(
finding(
'QL3_BARMAN_PREMATURE_RELEASE',
'the candidate lock cannot become release-ready before certificate supply-chain and live restore evidence exist',
),
);
}
} catch (error) {
findings.push(
finding(
'QL3_BARMAN_SUPPLY_CHAIN_AUDIT_UNAVAILABLE',
error instanceof Error ? error.message : 'unknown audit failure',
),
);
}
return Object.freeze({
schemaVersion: 1,
pluginVersion: '0.13.0',
controller: CONTROLLER.image,
sidecar: SIDECAR.image,
releaseReady: false,
releaseBlockers: RELEASE_BLOCKERS,
findings: Object.freeze(findings),
compatible: findings.length === 0,
});
}
if (require.main === module) {
const report = auditBarmanCloudSupplyChain();
process.stdout.write(`${JSON.stringify(report)}\n`);
if (!report.compatible) process.exitCode = 1;
}
module.exports = {
auditBarmanCloudSupplyChain,
};