Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions scripts/acquisition-deployment-evidence-audit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
writeFileSync,
} from "node:fs";
import { dirname, join } from "node:path";
import { TextDecoder } from "node:util";
import { evaluateAcquisitionDeploymentEvidence } from "./lib/acquisition-deployment-evidence.mjs";

const MAX_EVIDENCE_BYTES = 16 * 1024 * 1024;
Expand All @@ -34,7 +35,7 @@ function bounded(value, maximum = 4_000) {
return compact.length <= maximum ? compact : `${compact.slice(0, maximum)}…`;
}

function readRegularText(path, label) {
function readRegularBytes(path, label) {
if (!existsSync(path)) {
throw new Error(`${label} is missing: ${path}`);
}
Expand All @@ -43,20 +44,31 @@ function readRegularText(path, label) {
if (lstat.isSymbolicLink() || !stat.isFile() || stat.size <= 0 || stat.size > MAX_EVIDENCE_BYTES) {
throw new Error(`${label} must be a non-empty regular file no larger than ${MAX_EVIDENCE_BYTES} bytes`);
}
return readFileSync(path, "utf8");
return readFileSync(path);
}

function decodeUtf8(bytes, label) {
try {
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
} catch {
throw new Error(`${label} contains invalid UTF-8`);
}
}

function readJson(path, label) {
const text = readRegularText(path, label);
const bytes = readRegularBytes(path, label);
const text = decodeUtf8(bytes, label);
try {
return { text, value: JSON.parse(text) };
return { bytes, text, value: JSON.parse(text) };
} catch (error) {
throw new Error(`${label} is invalid JSON: ${bounded(error?.message || error)}`);
}
}

function readBundle(path) {
const text = readRegularText(path, "deployment attestation bundle");
const label = "deployment attestation bundle";
const bytes = readRegularBytes(path, label);
const text = decodeUtf8(bytes, label);
try {
return JSON.parse(text);
} catch {
Expand All @@ -80,8 +92,8 @@ function readBundle(path) {
}
}

function sha256(text) {
return createHash("sha256").update(text).digest("hex");
function sha256(bytes) {
return createHash("sha256").update(bytes).digest("hex");
}

function writeAudit(report) {
Expand Down Expand Up @@ -123,7 +135,7 @@ function evaluateSelectedRelease() {
const evaluation = evaluateAcquisitionDeploymentEvidence({
expectedTag: releaseUnderDiligenceTag,
deploymentEvidence: deployment.value,
deploymentEvidenceSha256: sha256(deployment.text),
deploymentEvidenceSha256: sha256(deployment.bytes),
governanceEvidence: governance.value,
attestationBundle,
verificationReceipt: receipt.value,
Expand Down
196 changes: 196 additions & 0 deletions test/acquisition-deployment-evidence-input-integrity.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { createHash } from "node:crypto";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { describe, expect, it } from "vitest";

const repository = "ContextualWisdomLab/noema";
const releaseTag = "v0.1.0";
const commitSha = "a".repeat(40);
const predicateType = "https://contextualwisdomlab.org/attestations/noema-deployment/v1";

function deploymentEvidence() {
return {
schemaVersion: 1,
generatedAt: "2026-08-04T00:00:00.000Z",
source: {
repository,
releaseTag,
releaseRef: `refs/tags/${releaseTag}`,
releaseUrl: `https://github.com/${repository}/releases/tag/${releaseTag}`,
version: "0.1.0",
commitSha,
releaseEvidenceSha256: "1".repeat(64),
},
deployment: {
environment: "production",
workerName: "noema",
workerVersionId: "worker-version-one",
deploymentId: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
deployedAt: "2026-08-04T00:00:01.000Z",
deploymentCreatedAt: "2026-08-04T00:00:02.000Z",
trafficPercentage: 100,
targets: ["https://noema.example.workers.dev"],
workflowRunUrl: `https://github.com/${repository}/actions/runs/123`,
},
rollback: {
previousDeploymentId: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
previousWorkerVersionId: "worker-version-zero",
},
validation: {
immutableRelease: true,
strictKpi: true,
smokePassed: true,
kpiExecutedAt: "2026-08-03T23:59:50.000Z",
smokeTimestamp: "2026-08-04T00:00:04.000Z",
kpiEvidenceSha256: "2".repeat(64),
smokeEvidenceSha256: "3".repeat(64),
},
};
}

function governanceEvidence() {
return {
schema_version: 1,
repository,
environment: "production",
status: "PASS",
reviewer_count: 1,
reviewers: [{ type: "Team", id: 42, identifier: "production-approvers" }],
checks: [
{ name: "required reviewers rule exists", pass: true, detail: "rule_count=1" },
{ name: "deployment initiator cannot self-approve", pass: true, detail: "prevent_self_review=true" },
{ name: "only protected branches may deploy", pass: true, detail: "protected_branches=true" },
],
failures: [],
};
}

function attestationBundle() {
return {
mediaType: "application/vnd.dev.sigstore.bundle.v0.3+json",
verificationMaterial: { tlogEntries: [{}] },
dsseEnvelope: { payload: "ZXZpZGVuY2U=", signatures: [{ sig: "c2ln" }] },
};
}

function sha256(value: Buffer | string) {
return createHash("sha256").update(value).digest("hex");
}

function writeInputs(root: string) {
const deploymentPath = join(root, "deployment-evidence.json");
const governancePath = join(root, "production-environment-governance.json");
const bundlePath = join(root, "deployment-evidence.sigstore.json");
const receiptPath = join(root, "deployment-attestation-verification.json");

const deploymentBytes = Buffer.from(`${JSON.stringify(deploymentEvidence(), null, 2)}\n`, "utf8");
writeFileSync(deploymentPath, deploymentBytes);
writeFileSync(governancePath, `${JSON.stringify(governanceEvidence(), null, 2)}\n`);
writeFileSync(bundlePath, `${JSON.stringify(attestationBundle())}\n`);
writeFileSync(receiptPath, `${JSON.stringify({
schemaVersion: 1,
verified: true,
repository,
releaseTag,
commitSha,
deploymentEvidenceSha256: sha256(deploymentBytes),
signerWorkflow: `${repository}/.github/workflows/cd.yml`,
predicateType,
oidcIssuer: "https://token.actions.githubusercontent.com",
denySelfHostedRunners: true,
workflowRunUrl: `https://github.com/${repository}/actions/runs/123`,
}, null, 2)}\n`);

return { deploymentPath, governancePath, bundlePath, receiptPath };
}

function runAudit(root: string, paths: ReturnType<typeof writeInputs>) {
return spawnSync(process.execPath, ["scripts/acquisition-deployment-evidence-audit.mjs"], {
cwd: process.cwd(),
encoding: "utf8",
env: {
...process.env,
NOEMA_RELEASE_UNDER_DILIGENCE_TAG: releaseTag,
NOEMA_ACQUISITION_AUDIT_OUTPUT_DIR: root,
NOEMA_DEPLOYMENT_EVIDENCE_PATH: paths.deploymentPath,
NOEMA_DEPLOYMENT_ATTESTATION_PATH: paths.bundlePath,
NOEMA_DEPLOYMENT_ATTESTATION_VERIFICATION_PATH: paths.receiptPath,
NOEMA_PRODUCTION_ENVIRONMENT_GOVERNANCE_PATH: paths.governancePath,
},
});
}

function malformedJsonBytes(value: object) {
const text = JSON.stringify({ ...value, note: "MALFORMED_SENTINEL" }, null, 2);
const [prefix, suffix] = text.split("MALFORMED_SENTINEL");
return Buffer.concat([
Buffer.from(prefix, "utf8"),
Buffer.from([0xff]),
Buffer.from(`${suffix}\n`, "utf8"),
]);
}

describe("acquisition deployment evidence byte integrity", () => {
it("keeps the valid exact-byte fixture passing", () => {
const root = mkdtempSync(join(tmpdir(), "noema-acquisition-deployment-bytes-"));
try {
const paths = writeInputs(root);
const result = runAudit(root, paths);

expect(result.status).toBe(0);
expect(result.stdout).toContain("acquisition-deployment-evidence-audit: PASS");
} finally {
rmSync(root, { recursive: true, force: true });
}
});

it("rejects malformed UTF-8 deployment evidence even when the receipt matches replacement-decoded text", () => {
const root = mkdtempSync(join(tmpdir(), "noema-acquisition-deployment-bytes-"));
try {
const paths = writeInputs(root);
const malformed = malformedJsonBytes(deploymentEvidence());
writeFileSync(paths.deploymentPath, malformed);

const replacementDecodedDigest = sha256(malformed.toString("utf8"));
writeFileSync(paths.receiptPath, `${JSON.stringify({
schemaVersion: 1,
verified: true,
repository,
releaseTag,
commitSha,
deploymentEvidenceSha256: replacementDecodedDigest,
signerWorkflow: `${repository}/.github/workflows/cd.yml`,
predicateType,
oidcIssuer: "https://token.actions.githubusercontent.com",
denySelfHostedRunners: true,
workflowRunUrl: `https://github.com/${repository}/actions/runs/123`,
}, null, 2)}\n`);

const result = runAudit(root, paths);

expect(result.status).toBe(1);
expect(result.stdout).toContain("deployment_evidence_collection_failed");
expect(result.stdout).toContain("invalid UTF-8");
} finally {
rmSync(root, { recursive: true, force: true });
}
});

it("rejects malformed UTF-8 in the retained attestation bundle", () => {
const root = mkdtempSync(join(tmpdir(), "noema-acquisition-deployment-bytes-"));
try {
const paths = writeInputs(root);
writeFileSync(paths.bundlePath, malformedJsonBytes(attestationBundle()));

const result = runAudit(root, paths);

expect(result.status).toBe(1);
expect(result.stdout).toContain("deployment_evidence_collection_failed");
expect(result.stdout).toContain("invalid UTF-8");
} finally {
rmSync(root, { recursive: true, force: true });
}
});
});
Loading