Skip to content
Merged
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
138 changes: 129 additions & 9 deletions .github/scripts/check-workflow-policy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,19 @@ function requireExactStepScript(violations, file, job, name, expectedDigest, sub
);
}

// A line that begins with `#` is not necessarily a shell comment when the
// preceding line opened a quote. Hash raw text for scripts with multiline
// quoted programs so quote-context rewrites cannot disappear from the digest.
function requireExactRawStepScript(violations, file, job, name, expectedDigest, subject) {
const run = exactResolverRunText(stepRun(job, name));
const digest = createHash("sha256").update(run).digest("hex");
add(
violations,
run.length > 0 && digest === expectedDigest,
`${file} step ${name} must match the reviewed ${subject} script exactly`,
);
}

function stepIndex(job, name) {
return list(job?.steps).map(object).findIndex(step => step.name === name);
}
Expand Down Expand Up @@ -526,6 +539,23 @@ const marketplaceGuardDigest = "6380c916a1b3566b4b9d6545b63fbc9c7db12b54fb328b5c
// executable text as well as its reader-facing invariants.
const packagedPlatformCloseoutDigest =
"ce7a7f5aa99f5fcbc037d4c1f06de5d841e4a4d114208820592a84c41c797b1a";
// This workflow builds release archives on three operating systems and carries
// state between many shell steps through GITHUB_ENV and GITHUB_PATH. Pin its
// parsed executable structure so an unreviewed earlier step cannot replace an
// owner binary while leaving the locally digested finalizer unchanged.
const packagedPlatformWorkflowDigest =
"6cb226d673bdbe5679b9f7010aad18c2c2a956129b51cff69ba457ed91962132";
// Linux owns its compiler server inside Docker, while macOS and Windows own one
// in the host shell. Pin both executable programs so a swallowed stop or a
// dead-code copy cannot satisfy the ownership fragments below.
const packagedSccacheIdentityDigest =
"35f1976fd420c0ca6f2213c49ec3879dfa136d649529bbe3fe175f6b5ca633c6";
const packagedLinuxBuildDigest =
"fc02f682c294983989d5f63151f8af9b31febda2da6fa0933aa9b1f7c221b4aa";
const packagedCompileClockStopDigest =
"ef9f7ee4636c3466830447e2ed8a10c2030ca3949bca082652d9262848d258a5";
const packagedHostCompilerFinalizerDigest =
"b77d8bb12c2748bfe016ab65ccb2f4581356f3ccf1d666e747306caffd6c0c46";
const draftProofCommands = [
"cargo test --locked -p codestory-llama-sys --test native_staging",
"cargo test --locked -p codestory-llama-sys --test model_staging",
Expand Down Expand Up @@ -2733,6 +2763,12 @@ function validatePackagedProof(workflows, violations, graph) {
violations.push(`${file} must exist`);
return;
}
add(
violations,
createHash("sha256").update(JSON.stringify(workflow)).digest("hex")
=== packagedPlatformWorkflowDigest,
`${file} must match the reviewed canonical workflow structure`,
);
add(violations, trigger(workflow, "workflow_call") !== undefined, `${file} must be reusable`);
const refInput = object(at(workflow, "on", "workflow_call", "inputs", "ref"));
add(
Expand Down Expand Up @@ -2827,6 +2863,25 @@ function validatePackagedProof(workflows, violations, graph) {
&& object(sccacheSetup?.with).version === "${{ env.SCCACHE_VERSION }}",
`${file} must install the pinned sccache action and binary`,
);
const sccacheIdentity = namedStep(job, "Capture pinned sccache identity");
add(
violations,
sccacheIdentity?.id === "sccache-identity"
&& sccacheIdentity?.shell === "bash"
&& sccacheIdentity?.env === undefined
&& sccacheIdentity?.["continue-on-error"] === undefined
&& stepIndex(job, "Capture pinned sccache identity")
=== stepIndex(job, "Install pinned sccache") + 1,
`${file} must capture the pinned sccache identity immediately after installation`,
);
requireExactRawStepScript(
violations,
file,
job,
"Capture pinned sccache identity",
packagedSccacheIdentityDigest,
"pinned sccache identity capture",
);
requireStepRun(violations, file, job, "Configure short Windows Cargo target", [
'$workspaceTarget = Join-Path $env:GITHUB_WORKSPACE "target"',
'$runnerRoot = [System.IO.Path]::GetPathRoot($workspaceTarget)',
Expand Down Expand Up @@ -2986,24 +3041,90 @@ function validatePackagedProof(workflows, violations, graph) {
requireStepRun(violations, file, job, "Compile native workspace path regression on Windows", [
"cargo test --locked -p codestory-workspace repository_identity --no-run",
]);
const packageBuild = namedStep(job, "Build codestory-cli");
const linuxBuild = namedStep(job, "Build Linux x64 at the glibc 2.31 baseline");
const expectedSccacheIdentityEnv = {
SCCACHE_BINARY: "${{ steps.sccache-identity.outputs.path }}",
SCCACHE_SHA256: "${{ steps.sccache-identity.outputs.sha256 }}",
};
requireStepRun(violations, file, job, "Build Linux x64 at the glibc 2.31 baseline", [
'mkdir -p "$CARGO_HOME" "$SCCACHE_DIR"',
'test -x "$SCCACHE_BINARY"',
'test "$actual_sccache_sha256" = "$SCCACHE_SHA256"',
"RUSTC_WRAPPER=/sccache/sccache",
"SCCACHE_DIR=/sccache/cache",
"CMAKE_C_COMPILER_LAUNCHER=/sccache/sccache",
"CMAKE_CXX_COMPILER_LAUNCHER=/sccache/sccache",
"$SCCACHE_PATH:/sccache/sccache:ro",
"$SCCACHE_BINARY:/sccache/sccache:ro",
"$SCCACHE_DIR:/sccache/cache",
"/sccache/sccache --stop-server",
]);
add(
violations,
linuxBuild?.if === "matrix.asset_target == 'linux-x64'"
&& linuxBuild?.shell === "bash"
&& hasExactKeys(object(linuxBuild?.env), Object.keys(expectedSccacheIdentityEnv))
&& Object.entries(expectedSccacheIdentityEnv).every(
([key, value]) => object(linuxBuild?.env)[key] === value,
)
&& linuxBuild?.["continue-on-error"] === undefined,
`${file} Linux container must strictly report and stop its owned compiler server`,
);
requireExactRawStepScript(
violations,
file,
job,
"Build Linux x64 at the glibc 2.31 baseline",
packagedLinuxBuildDigest,
"Linux container build and compiler-server ownership",
);
const stopCompilationClock = namedStep(job, "Stop compilation clock");
add(
violations,
stopCompilationClock?.id === "compile-clock-stop"
&& stopCompilationClock?.shell === "bash"
&& stopCompilationClock?.env === undefined
&& stopCompilationClock?.["continue-on-error"] === undefined,
`${file} compiler clock stop must remain a strict telemetry-only boundary`,
);
requireExactRawStepScript(
violations,
file,
job,
"Stop compilation clock",
packagedCompileClockStopDigest,
"compiler clock stop",
);
const finalizeCompilerObjects = namedStep(job, "Finalize compiler objects");
add(
violations,
String(finalizeCompilerObjects?.if ?? "")
.includes("steps.linux-build.outcome == 'success'")
&& String(finalizeCompilerObjects?.if ?? "")
.includes("steps.package-build.outcome == 'success'"),
`${file} must stop the compiler server that performed each selected build`,
String(finalizeCompilerObjects?.if ?? "").trim()
=== "always() && steps.package-build.outcome == 'success'"
&& finalizeCompilerObjects?.shell === "bash"
&& hasExactKeys(
object(finalizeCompilerObjects?.env),
Object.keys(expectedSccacheIdentityEnv),
)
&& Object.entries(expectedSccacheIdentityEnv).every(
([key, value]) => object(finalizeCompilerObjects?.env)[key] === value,
)
&& finalizeCompilerObjects?.["continue-on-error"] === undefined
&& packageBuild?.if === "matrix.asset_target != 'linux-x64'",
`${file} host finalizer must strictly stop only the host package-build compiler server`,
);
requireExactRawStepScript(
violations,
file,
job,
"Finalize compiler objects",
packagedHostCompilerFinalizerDigest,
"host compiler-server finalizer",
);
add(
violations,
stepIndex(job, "Stop compilation clock")
=== stepIndex(job, "Build Linux x64 at the glibc 2.31 baseline") + 1
&& stepIndex(job, "Finalize compiler objects")
=== stepIndex(job, "Stop compilation clock") + 1,
`${file} compiler owner build, clock stop, and finalizer must remain adjacent`,
);
add(
violations,
Expand Down Expand Up @@ -3041,7 +3162,6 @@ function validatePackagedProof(workflows, violations, graph) {
`${file} Bullseye native build must preserve compiler contract ${fragment}`,
);
}
const packageBuild = namedStep(job, "Build codestory-cli");
add(
violations,
packageBuild?.env === undefined,
Expand Down
160 changes: 158 additions & 2 deletions .github/scripts/check-workflow-policy.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,9 @@ test("reusable compiler caches and proof modes reject hostile downgrades", async
draftStep(packagedJob(workflow), "Capture reusable build cache contract");

const mutations = [
["packaged workflow injects an earlier Node preload", packagedFile, workflow => {
workflow.env.NODE_OPTIONS = "--require ./fake-hash.cjs";
}, /packaged-platform-proof\.yml must match the reviewed canonical workflow structure/u],
["release workflow policy loses its full history", releaseFile, workflow => {
delete workflow.jobs["workflow-policy"].steps[0].with;
}, /workflow-policy must check out full history for the reuse-binding contracts/u],
Expand Down Expand Up @@ -2051,6 +2054,20 @@ test("reusable compiler caches and proof modes reject hostile downgrades", async
packagedIdentity(workflow).run = packagedIdentity(workflow).run
.replace("--identity qualification_driver=disabled", "--workload ignored");
}, /packaged-platform-proof\.yml must compute one complete reusable compiler compatibility contract/u],
["pinned sccache identity capture moves away from installation", packagedFile, workflow => {
moveNamedStepAfter(
packagedJob(workflow),
"Capture pinned sccache identity",
"Configure bounded compiler cache",
);
}, /must capture the pinned sccache identity immediately after installation/u],
["pinned sccache identity capture stops hashing the binary", packagedFile, workflow => {
const capture = draftStep(packagedJob(workflow), "Capture pinned sccache identity");
capture.run = capture.run.replace(
'createHash("sha256").update(readFileSync(process.argv[1])).digest("hex")',
'"unverified"',
);
}, /pinned sccache identity capture script exactly/u],
["source compiler cache waits for tests", sourceFile, workflow => {
moveNamedStepAfter(
sourceJob(workflow),
Expand Down Expand Up @@ -2173,10 +2190,149 @@ test("reusable compiler caches and proof modes reject hostile downgrades", async
},
});
}, /frozen Linux fresh-target qualification must not restore compiler output/u],
["Linux compiler cache exits with an active server", packagedFile, workflow => {
["Linux compiler cache omits server shutdown", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = build.run.replace("/sccache/sccache --stop-server", "true");
}, /step Build Linux x64 at the glibc 2\.31 baseline must run \/sccache\/sccache --stop-server/u],
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler cache makes statistics advisory", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = build.run.replace(
"/sccache/sccache --show-stats",
"/sccache/sccache --show-stats || true",
);
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler cache makes shutdown advisory", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = build.run.replace(
"/sccache/sccache --stop-server",
"/sccache/sccache --stop-server || true",
);
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler shutdown is parked in dead code", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = `if false; then\n${build.run}\nfi\n`;
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler shutdown hides behind an exact dead-code decoy", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = build.run.replace(
"/sccache/sccache --stop-server",
"/sccache/sccache --stop-server || true",
);
build.run += "\nif false; then\n /sccache/sccache --stop-server\nfi\n";
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler shutdown escapes through a stripped quote-context comment", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = build.run.replace(
" /sccache/sccache --show-stats",
" # '; exit 0; : '\n /sccache/sccache --show-stats",
);
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler shutdown is inverted", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = build.run.replace("docker run --rm", "! docker run --rm");
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler shutdown is bypassed by an early exit", packagedFile, workflow => {
const build = draftStep(packagedJob(workflow), "Build Linux x64 at the glibc 2.31 baseline");
build.run = `exit 0\n${build.run}`;
}, /Linux container build and compiler-server ownership script exactly/u],
["Linux compiler build shell absorbs failure", packagedFile, workflow => {
draftStep(
packagedJob(workflow),
"Build Linux x64 at the glibc 2.31 baseline",
).shell = "bash {0} || true";
}, /Linux container must strictly report and stop its owned compiler server/u],
["Linux compiler cache step becomes advisory", packagedFile, workflow => {
draftStep(
packagedJob(workflow),
"Build Linux x64 at the glibc 2.31 baseline",
)["continue-on-error"] = true;
}, /Linux container must strictly report and stop its owned compiler server/u],
["Linux compiler cache rebinds the pinned binary", packagedFile, workflow => {
draftStep(
packagedJob(workflow),
"Build Linux x64 at the glibc 2.31 baseline",
).env.SCCACHE_BINARY = "sccache";
}, /Linux container must strictly report and stop its owned compiler server/u],
["host compiler finalizer is restored on Linux", packagedFile, workflow => {
draftStep(packagedJob(workflow), "Finalize compiler objects").if =
"always() && ((matrix.asset_target == 'linux-x64' && steps.linux-build.outcome == 'success') || (matrix.asset_target != 'linux-x64' && steps.package-build.outcome == 'success'))";
}, /host finalizer must strictly stop only the host package-build compiler server/u],
["host package build becomes Linux-reachable", packagedFile, workflow => {
draftStep(packagedJob(workflow), "Build codestory-cli").if = "always()";
}, /host finalizer must strictly stop only the host package-build compiler server/u],
["clock stop prepends a fake compiler cache binary", packagedFile, workflow => {
const stop = draftStep(packagedJob(workflow), "Stop compilation clock");
stop.run += [
"",
'fake_dir="$RUNNER_TEMP/fake-sccache"',
'mkdir -p "$fake_dir"',
"printf '#!/usr/bin/env bash\\nexit 0\\n' > \"$fake_dir/sccache\"",
'chmod +x "$fake_dir/sccache"',
'echo "$fake_dir" >> "$GITHUB_PATH"',
].join("\n");
}, /compiler clock stop script exactly/u],
["clock stop shell absorbs failure", packagedFile, workflow => {
draftStep(packagedJob(workflow), "Stop compilation clock").shell = "bash {0} || true";
}, /compiler clock stop must remain a strict telemetry-only boundary/u],
["a prep step is inserted before compiler finalization", packagedFile, workflow => {
const steps = packagedJob(workflow).steps;
const finalizeIndex = steps.findIndex(step => step.name === "Finalize compiler objects");
steps.splice(finalizeIndex, 0, {
name: "Shadow compiler cache",
shell: "bash",
run: 'echo "$RUNNER_TEMP/fake-sccache" >> "$GITHUB_PATH"',
});
}, /compiler owner build, clock stop, and finalizer must remain adjacent/u],
["host compiler statistics become advisory", packagedFile, workflow => {
const finalize = draftStep(packagedJob(workflow), "Finalize compiler objects");
finalize.run = finalize.run.replace(
'"$SCCACHE_BINARY" --show-stats',
'"$SCCACHE_BINARY" --show-stats || true',
);
}, /host compiler-server finalizer script exactly/u],
["host compiler shutdown becomes advisory", packagedFile, workflow => {
const finalize = draftStep(packagedJob(workflow), "Finalize compiler objects");
finalize.run = finalize.run.replace(
'"$SCCACHE_BINARY" --stop-server',
'"$SCCACHE_BINARY" --stop-server || true',
);
}, /host compiler-server finalizer script exactly/u],
["host compiler shutdown hides behind exact dead-code decoys", packagedFile, workflow => {
const finalize = draftStep(packagedJob(workflow), "Finalize compiler objects");
finalize.run = [
"sccache --show-stats || true",
"sccache --stop-server || true",
"if false; then",
" sccache --show-stats",
" sccache --stop-server",
"fi",
].join("\n");
}, /host compiler-server finalizer script exactly/u],
["host compiler finalizer shell absorbs failure", packagedFile, workflow => {
draftStep(
packagedJob(workflow),
"Finalize compiler objects",
).shell = "bash {0} || true";
}, /host finalizer must strictly stop only the host package-build compiler server/u],
["host compiler finalizer step becomes advisory", packagedFile, workflow => {
draftStep(
packagedJob(workflow),
"Finalize compiler objects",
)["continue-on-error"] = true;
}, /host finalizer must strictly stop only the host package-build compiler server/u],
["host compiler finalizer rebinds the pinned binary", packagedFile, workflow => {
draftStep(
packagedJob(workflow),
"Finalize compiler objects",
).env.SCCACHE_BINARY = "sccache";
}, /host finalizer must strictly stop only the host package-build compiler server/u],
["host compiler finalizer resolves through PATH again", packagedFile, workflow => {
const finalize = draftStep(packagedJob(workflow), "Finalize compiler objects");
finalize.run = finalize.run.replace(
'"$SCCACHE_BINARY" --show-stats',
"sccache --show-stats",
);
}, /host compiler-server finalizer script exactly/u],
["package checkout accepts a fallback SHA", packagedFile, workflow => {
draftStep(packagedJob(workflow), "Checkout").with.ref = "${{ inputs.ref || github.sha }}";
}, /package jobs must checkout only the requested exact SHA/u],
Expand Down
Loading