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
161 changes: 148 additions & 13 deletions .github/scripts/check-workflow-policy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -993,11 +993,13 @@ const packagedPlatformWorkflowDigest =
// made advisory, parked in dead code, or followed by a payload substitution
// while leaving the expected tokens in place.
const packagedPlatformCoordinatorWorkflowDigest =
"29fdda15a93e6cf4526588bad2c746ef8e13afb58e74a7b2b44d9b1a656eb549";
"797fa9e2be359f83eacd45b78722829d1f277efd2e721de1c9bf8b590b73dc58";
const releaseSourceProofSentinelDigest =
"91ee8bc1a6a055e9297e81747c37d167b123d0a2e5dc60d5c6e2bdcfbef9c351";
const frozenCandidateQualityWorkflowDigest =
"92d0a7ab0e0df63dacd5cc3ef0b58500a6578036494c329aa35279048734f173";
const macosMetalWorkflowDigest =
"05b69d48238284b47b40c13bf15eb1f31370dea55bb77169553d41b46fda1a7f";
"55581330f6a035b84e1224dbd5469d812ab2fa444914157e22a39cccc64f4627";
const windowsVulkanWorkflowDigest =
"c2272dbf4c550ba4a21372e772a87f6df3307f5f4f709b216473f85958157ffe";
const linuxVulkanWorkflowDigest =
Expand Down Expand Up @@ -2890,9 +2892,33 @@ function validateReleaseCoordinator(workflows, violations, graph) {
);

const source = requireJob(violations, releaseFile, release, "source-proof");
add(violations, source.uses === "./.github/workflows/source-proof.yml", `${releaseFile} must call exact source proof`);
add(
violations,
createHash("sha256").update(JSON.stringify(source)).digest("hex")
=== releaseSourceProofSentinelDigest,
`${releaseFile} source proof placeholder must match the reviewed fail-closed sentinel`,
);
add(
violations,
source.uses === undefined
&& source["runs-on"] === "ubuntu-latest"
&& source["timeout-minutes"] === 1
&& permissionMapMatches(source.permissions, {})
&& object(source.env).SOURCE_SHA === "${{ github.sha }}",
`${releaseFile} source proof placeholder must fail closed without calling the broad source workflow`,
);
add(violations, sameMembers(needs(source), releaseChain.dependencies["source-proof"]), `${releaseFile} source proof dependencies must match the release claim graph`);
add(violations, object(source.with).ref === "${{ github.sha }}", `${releaseFile} source proof must receive the exact release SHA`);
requireStepRun(
violations,
releaseFile,
source,
"Refuse a second source proof",
[
'test "$SOURCE_SHA" = "$GITHUB_SHA"',
"Preflight did not resolve reusable exact-head source proof",
"exit 1",
],
);
// Reuse is admissible only through the authenticated closeout binding, never by simply
// dropping the gate: the job may be skipped, and only when preflight resolved reusable
// evidence for this exact tree.
Expand Down Expand Up @@ -2935,10 +2961,10 @@ function validateReleaseCoordinator(workflows, violations, graph) {
);
add(
violations,
object(source.with).version === "${{ needs.preflight.outputs.version }}"
&& object(source.with).freeze_receipt_digest === ""
&& object(source.with).emit_release_cells === undefined,
`${releaseFile} unreachable source fallback must fail closed without a post-calibration freeze`,
source.with === undefined
&& source.uses === undefined
&& list(source.steps).length === 1,
`${releaseFile} unreachable source fallback must remain a one-step fail-closed sentinel`,
);

const packaged = requireJob(violations, releaseFile, release, "packaged-proof");
Expand Down Expand Up @@ -6414,9 +6440,24 @@ function validateRemainingWorkflows(workflows, violations) {
job,
"${{ !inputs.calibration_mode && !inputs.server_behavior_only }}",
);
const calibrationPreflightName = "Validate unfrozen Metal calibration source";
const calibrationPreflight = namedStep(job, calibrationPreflightName);
add(
violations,
calibrationPreflight?.if === "inputs.calibration_mode"
&& calibrationPreflight?.shell === "bash"
&& calibrationPreflight?.["continue-on-error"] === undefined
&& stepRun(job, calibrationPreflightName).trim() === [
"set -euo pipefail",
'test "$(jq -r .status crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = unfrozen',
'test "$(jq -r .freeze_record crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = null',
].join("\n")
&& stepIndex(job, calibrationPreflightName)
=== stepIndex(job, "Checkout") + 1,
`${metalFile} must reject a frozen or stale calibration source immediately after checkout and before setup or compilation`,
);
const calibrationStepName = "Collect three independent Metal constant calibration runs";
requireStepRun(violations, metalFile, job, calibrationStepName, [
'test "$(jq -r .status crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = unfrozen',
"--proof-tier calibration",
"--engine-policy accelerated",
"--expected-backend Metal",
Expand Down Expand Up @@ -7951,6 +7992,60 @@ function permissionMapMatches(actualValue, expectedValue) {
&& Object.entries(expected).every(([key, value]) => actual[key] === value);
}

function reusableWorkflowPermissionViolations(workflows) {
const violations = [];
const permissionRank = value => (
value === "write" ? 2 : value === "read" ? 1 : 0
);
const permissionRequests = value => {
if (value === "write-all") return [["*", "write"]];
if (value === "read-all") return [["*", "read"]];
return Object.entries(object(value));
};
const permissionGrant = (value, scope) => {
if (value === "write-all") return { rank: 2, label: "write-all" };
if (value === "read-all") return { rank: 1, label: "read-all" };
const granted = object(value)[scope];
return { rank: permissionRank(granted), label: granted ?? "none" };
};
const localWorkflow = /^\.\/\.github\/workflows\/([^/]+\.ya?ml)$/u;

for (const [callerFile, callerWorkflow] of workflows) {
for (const [jobName, jobValue] of Object.entries(object(callerWorkflow.jobs))) {
const job = object(jobValue);
const match = String(job.uses ?? "").match(localWorkflow);
if (!match) continue;
const calleeFile = match[1];
const callee = workflows.get(calleeFile);
if (!callee) {
violations.push(
`[reusable_permissions] ${callerFile} job ${jobName} calls missing local workflow ${calleeFile}`,
);
continue;
}
const callerPermissions = job.permissions === undefined
? callerWorkflow.permissions
: job.permissions;
for (const [calleeJobName, calleeJobValue] of Object.entries(object(callee.jobs))) {
const calleeJob = object(calleeJobValue);
const requestedPermissions = calleeJob.permissions === undefined
? callee.permissions
: calleeJob.permissions;
for (const [scope, requested] of permissionRequests(requestedPermissions)) {
const granted = permissionGrant(callerPermissions, scope);
add(
violations,
granted.rank >= permissionRank(requested),
`[reusable_permissions] ${callerFile} job ${jobName} grants ${scope}: ${granted.label} but ${calleeFile} job ${calleeJobName} requests ${requested}`,
);
}
}
}
}

return violations;
}

function findNamedStep(workflow, name) {
for (const job of Object.values(object(workflow.jobs))) {
const found = namedStep(job, name);
Expand Down Expand Up @@ -8527,7 +8622,7 @@ export function releaseFreezeBarrierWorkflowViolations(
add(
violations,
object(workflow.permissions).statuses === "read",
"[freeze_barrier] packaged-platform-pr.yml must authenticate the exact-head freeze status",
"[freeze_barrier] packaged-platform-pr.yml must authenticate the exact-head freeze status without broad workflow authority",
);
}
add(
Expand Down Expand Up @@ -8735,13 +8830,16 @@ export function releaseFreezeBarrierWorkflowViolations(
sourceWorkflow,
acceptance.windows_job,
);
const windowsProbePowerShell
= `powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ". '{0}'"`;
add(
violations,
windowsJob.if === "inputs.acceptance_only"
&& sameMembers(needs(windowsJob), ["resolve"])
&& sameMembers(list(windowsJob["runs-on"]), list(acceptance.windows_runner))
&& windowsJob["timeout-minutes"] === 5
&& namedStep(windowsJob, acceptance.windows_step)?.shell === "pwsh"
&& namedStep(windowsJob, acceptance.windows_step)?.shell
=== windowsProbePowerShell
&& namedStep(windowsJob, acceptance.windows_step)?.["continue-on-error"] !== true,
"[freeze_barrier] source acceptance must execute the protected blocking Windows native probe",
);
Expand All @@ -8754,14 +8852,28 @@ export function releaseFreezeBarrierWorkflowViolations(
"cargo new --quiet --bin",
"cargo build --release --quiet",
"node --test .github/scripts/cargo-build-artifacts.test.mjs",
"const [root, deps] = process.argv.slice(2);",
"left.dev !== right.dev",
"left.ino !== right.ino",
"left.nlink !== 2n",
"right.nlink !== 2n",
'$identityScriptPath = Join-Path $probeRoot "verify-hardlink-identity.cjs"',
"Set-Content -LiteralPath $identityScriptPath -Value $identityScript -Encoding UTF8",
"node $identityScriptPath $rootExe $depsExe",
"Elapsed.TotalSeconds -ge 90",
"Remove-Item -LiteralPath $probeRoot -Recurse -Force",
],
);
forbidStepRun(
violations,
"source-proof.yml",
windowsJob,
acceptance.windows_step,
[
"node -e $identityScript",
"process.argv.slice(1)",
],
);

const publisherJob = requireJob(
violations,
Expand Down Expand Up @@ -8894,6 +9006,22 @@ export function releaseFreezeBarrierWorkflowViolations(
'.name == "full-source-gate" and .conclusion == "success"',
],
);
const packagedSourceJob = requireJob(
violations,
"packaged-platform-pr.yml",
coordinator,
"source-proof",
);
add(
violations,
permissionMapMatches(packagedSourceJob.permissions, {
actions: "write",
contents: "read",
"pull-requests": "read",
statuses: "write",
}),
"[freeze_barrier] packaged source-proof call must grant exactly the reusable workflow permissions",
);

const release = workflows.get("release.yml");
const auto = workflows.get("auto-release.yml");
Expand Down Expand Up @@ -8942,7 +9070,9 @@ export function releaseFreezeBarrierWorkflowViolations(
sourceJob.if === "needs.preflight.outputs.source_proof_reused != 'true'"
&& object(preflight.outputs).source_proof_reused
=== "${{ steps.reuse.outputs.source_proof_reused }}"
&& object(sourceJob.with).freeze_receipt_digest === "",
&& sourceJob.uses === undefined
&& sourceJob.with === undefined
&& namedStep(sourceJob, "Refuse a second source proof") !== undefined,
"[freeze_barrier] release must make the post-calibration source-proof fallback unreachable",
);

Expand Down Expand Up @@ -9038,9 +9168,13 @@ export function releaseWorkflowContractViolations(

const release = workflows.get("release.yml");
for (const jobName of policy.release_chain.exact_sha_jobs) {
const job = object(at(release, "jobs", jobName));
const exactSha = jobName === "source-proof" && job.uses === undefined
? object(job.env).SOURCE_SHA
: object(job.with).ref;
add(
violations,
object(at(release, "jobs", jobName, "with")).ref === policy.promotion.exact_sha_expression,
exactSha === policy.promotion.exact_sha_expression,
`[exact_sha] release.yml job ${jobName} must receive ${policy.promotion.exact_sha_expression}`,
);
}
Expand Down Expand Up @@ -10242,6 +10376,7 @@ export function validateWorkflows(workflows, graph = loadReleaseClaimGraph(repos
for (const [file, workflow] of workflows) {
violations.push(...basicWorkflowViolations(file, workflow));
}
violations.push(...reusableWorkflowPermissionViolations(workflows));
validateCargoTestFilters(workflows, violations);
validatePluginRelease(workflows, violations, graph);
validateMarketplaceSync(workflows, violations);
Expand Down
Loading
Loading