13 KiB
1. Introduction
Overview
Nightly Build & Package currently has two active workflow failures that must
be fixed together in one minimal-scope PR:
- SBOM generation failure in
Generate SBOM(Syft fetch/version resolution). - Dispatch failure from nightly workflow with
Missing required input 'pr_number' not provided.
This plan hard-locks runtime code changes to
.github/workflows/nightly-build.yml only.
Objectives
- Restore deterministic nightly SBOM generation.
- Enforce strict default-deny dispatch behavior for non-PR nightly events
(
schedule,workflow_dispatch). - Preserve GitHub Actions best practices: pinned SHAs, least privilege, and deterministic behavior.
- Keep both current failures in a single scope and do not pivot to unrelated fixes.
- Remove
security-pr.ymlfrom nightly dispatch list unless a hard requirement is proven.
2. Research Findings
2.1 Primary Workflow Scope
File analyzed: .github/workflows/nightly-build.yml
Relevant areas:
- Job
build-and-push-nightly, stepGenerate SBOMusesanchore/sbom-action@17ae1740179002c89186b61233e0f892c3118b11. - Job
trigger-nightly-validationdispatches downstream workflows usingactions/github-scriptand currently includessecurity-pr.yml.
2.2 Root Cause: Missing pr_number
Directly related called workflow:
.github/workflows/security-pr.yml- Trigger contract includes:
workflow_dispatch.inputs.pr_number.required: true
Impact:
- Nightly dispatcher invokes
createWorkflowDispatchforsecurity-pr.ymlwithoutpr_number. - For nightly non-PR contexts (scheduled/manual nightly), there is no natural PR number, so dispatch fails by contract.
- PR lookup by nightly head SHA is not a valid safety mechanism for nightly
non-PR trigger types and must not be relied on for
scheduleorworkflow_dispatch.
2.3 Decision: Remove PR-Only Workflow from Nightly Dispatch List
Assessment result:
- No hard requirement was found that requires nightly workflow to dispatch
security-pr.yml. security-pr.ymlis contractually PR/manual-oriented because it requirespr_number.- Keeping it in nightly fan-out adds avoidable failure risk and encourages invalid context synthesis.
Decision:
- Remove
security-pr.ymlfrom nightly dispatch list. - Keep strict default-deny guard logic to prevent accidental future dispatch from non-PR events.
Risk reduction from removal:
- Eliminates
pr_numbercontract mismatch in nightly non-PR events. - Removes a class of false failures from nightly reliability metrics.
- Simplifies dispatcher logic and review surface.
2.4 Root Cause: SBOM/Syft Fetch Failure
Observed behavior indicates Syft retrieval/version resolution instability during
the SBOM step. In current workflow, no explicit syft-version is set in
nightly-build.yml, so resolution is not explicitly pinned at the workflow
layer.
2.5 Constraints and Policy Alignment
- Keep action SHAs pinned.
- Keep permission scopes unchanged unless required.
- Keep change minimal and limited to nightly workflow path only.
3. Technical Specification (EARS)
-
WHEN nightly runs from
scheduleorworkflow_dispatch, THE SYSTEM SHALL enforce strict default-deny for PR-only dispatches. -
WHEN nightly runs from
scheduleorworkflow_dispatch, THE SYSTEM SHALL NOT perform PR-number lookup from nightly head SHA. -
WHEN evaluating downstream nightly dispatches, THE SYSTEM SHALL exclude
security-pr.ymlfrom nightly dispatch targets unless a hard requirement is explicitly introduced and documented. -
IF
security-pr.ymlis reintroduced in the future, THEN THE SYSTEM SHALL dispatch it ONLY when a real PR context includes a concretepr_number, and SHALL deny by default in all other contexts. -
WHEN
Generate SBOMruns in nightly, THE SYSTEM SHALL use a deterministic two-stage strategy in the same PR scope:- Primary path:
syft-version: v1.42.1viaanchore/sbom-action - In-PR fallback path: explicit Syft CLI installation/generation with pinned version/checksum and hard verification
- Primary path:
-
IF primary SBOM generation fails or does not produce a valid file, THEN THE SYSTEM SHALL execute fallback generation and SHALL fail the job when fallback also fails or output validation fails.
-
THE SYSTEM SHALL keep GitHub Actions pinned to immutable SHAs and SHALL NOT broaden token permissions for this fix.
4. Exact Implementation Edits
4.1 .github/workflows/nightly-build.yml
Edit A: Harden downstream dispatch for non-PR triggers
Location: job trigger-nightly-validation, step
Dispatch Missing Nightly Validation Workflows.
Exact change intent:
- Remove
security-pr.ymlfrom the nightly dispatch list. - Keep dispatch for
e2e-tests-split.yml,codecov-upload.yml,supply-chain-verify.yml, andcodeql.ymlunchanged. - Add explicit guard comments and logging stating non-PR nightly events are default-deny for PR-only workflows.
- Explicitly prohibit PR number synthesis and prohibit PR lookup from nightly
SHA for
scheduleandworkflow_dispatch.
Implementation shape (script-level):
- Keep workflow list explicit.
- Keep a local denylist/set for PR-only workflows and ensure they are never dispatched from nightly non-PR events.
- No PR-number inputs are synthesized from nightly SHA or non-PR context.
- No PR lookup calls are executed for nightly non-PR events.
Edit B: Stabilize Syft source in Generate SBOM
Location: job build-and-push-nightly, step Generate SBOM.
Exact change intent:
- Keep existing pinned
anchore/sbom-actionSHA unless evidence shows that SHA itself is the failure source. - Add explicit
syft-version: v1.42.1inwith:block as the primary pin. - Set the primary SBOM step to
continue-on-error: trueto allow deterministic in-PR fallback execution. - Add fallback step gated on primary step failure OR missing/invalid output:
- Install Syft CLI
v1.42.1from official release with checksum validation. - Generate
sbom-nightly.jsonvia CLI.
- Install Syft CLI
- Add mandatory verification step (no
continue-on-error) with explicit pass/fail criteria:sbom-nightly.jsonexists.- file size is greater than 0 bytes.
- JSON parses successfully (
jq empty). - expected top-level fields exist for selected format.
- If verification fails, job fails. SBOM cannot pass silently without generated artifact.
4.2 Scope Lock
- No edits to
.github/workflows/security-pr.ymlin this plan. - Contract remains unchanged:
workflow_dispatch.inputs.pr_number.required: true.
5. Reconfirmation: Non-Target Files
No changes required:
.gitignorecodecov.yml.dockerignoreDockerfile
Rationale:
- Both failures are workflow orchestration issues, not source-ignore, coverage policy, Docker context, or image build recipe issues.
6. Risks and Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
security-pr.yml accidentally dispatched in non-PR mode |
Low | Remove from nightly dispatch list and enforce default-deny comments/guards |
Primary Syft acquisition fails (v1.42.1) |
Medium | Execute deterministic in-PR fallback with pinned checksum and hard output verification |
| SBOM step appears green without real artifact | High | Mandatory verification step with explicit file/JSON checks and hard fail |
| Action SHA update introduces side effects | Medium | Limit SHA change to Generate SBOM step only and validate end-to-end nightly path |
| Over-dispatch/under-dispatch in validation job | Low | Preserve existing dispatch logic for all non-PR-dependent workflows |
7. Rollback Plan
- Revert runtime behavior changes in
.github/workflows/nightly-build.yml:trigger-nightly-validationdispatch logicGenerate SBOMprimary + fallback + verification sequence
- Re-run nightly dispatch manually to verify previous baseline runtime behavior.
Rollback scope: runtime workflow behavior only in
.github/workflows/nightly-build.yml. Documentation updates are not part of
runtime rollback.
8. Validation Plan
8.1 Static Validation
cd /projects/Charon
pre-commit run actionlint --files .github/workflows/nightly-build.yml
8.2 Behavioral Validation (Nightly non-PR)
gh workflow run nightly-build.yml --ref nightly -f reason="nightly dual-fix validation" -f skip_tests=true
gh run list --workflow "Nightly Build & Package" --branch nightly --limit 1
gh run view <run-id> --json databaseId,headSha,event,status,conclusion,createdAt
gh run view <run-id> --log
Expected outcomes:
Generate SBOMsucceeds through primary path or deterministic fallback andsbom-nightly.jsonis uploaded.- Dispatch step does not attempt
security-pr.ymlfrom nightly run. - No
Missing required input 'pr_number' not providederror. - Both targeted nightly failures are resolved in the same run scope:
pr_numberdispatch failure and Syft/SBOM failure.
8.3 Explicit Negative Dispatch Verification (Run-Scoped/Time-Scoped)
Verify security-pr.yml was not dispatched by this specific nightly run using
time scope and actor scope (not SHA-only):
RUN_JSON=$(gh run view <nightly-run-id> --json databaseId,createdAt,updatedAt,event,headBranch)
START=$(echo "$RUN_JSON" | jq -r '.createdAt')
END=$(echo "$RUN_JSON" | jq -r '.updatedAt')
gh api repos/<owner>/<repo>/actions/workflows/security-pr.yml/runs \
--paginate \
-f event=workflow_dispatch | \
jq --arg start "$START" --arg end "$END" '
[ .workflow_runs[]
| select(.created_at >= $start and .created_at <= $end)
| select(.head_branch == "nightly")
| select(.triggering_actor.login == "github-actions[bot]")
] | length'
Expected result: 0
8.4 Positive Validation: Manual security-pr.yml Dispatch Still Works
Run a manual dispatch with a valid PR number and verify successful start:
gh workflow run security-pr.yml --ref <pr-branch> -f pr_number=<valid-pr-number>
gh run list --workflow "Security Scan (PR)" --limit 5 \
--json databaseId,event,status,conclusion,createdAt,headBranch
gh run view <security-pr-run-id> --log
Expected results:
- Workflow is accepted (no missing-input validation errors).
- Run event is
workflow_dispatch. - Run completes according to existing workflow behavior.
8.5 Contract Validation (No Contract Change)
security-pr.ymlcontract remains PR/manual specific and unchanged.- Nightly non-PR paths do not consume or synthesize
pr_number.
9. Acceptance Criteria
Nightly Build & Packageno longer fails inGenerate SBOMdue to Syft fetch/version resolution, with deterministic in-PR fallback.- Nightly validation dispatch no longer fails with missing required
pr_number. - For non-PR nightly triggers (
schedule/workflow_dispatch), PR-only dispatch ofsecurity-pr.ymlis default-deny and not attempted from nightly dispatch targets. - Workflow remains SHA-pinned and permissions are not broadened.
- Validation evidence includes explicit run-scoped/time-scoped proof that
security-pr.ymlwas not dispatched by the tested nightly run. - No changes made to
.gitignore,codecov.yml,.dockerignore, orDockerfile. - Manual dispatch of
security-pr.ymlwith validpr_numberis validated to still work. - SBOM step fails hard when neither primary nor fallback path produces a valid SBOM artifact.
10. PR Slicing Strategy
Decision
Single PR.
Trigger Reasons
- Changes are tightly coupled inside one workflow path.
- Shared validation path (nightly run) verifies both fixes together.
- Rollback safety is high with one-file revert.
Ordered Slices
PR-1: Nightly Dual-Failure Workflow Fix
Scope:
.github/workflows/nightly-build.ymlonly.- SBOM Syft stabilization with explicit tag pin + fallback rule.
- Remove
security-pr.ymlfrom nightly dispatch list and enforce strict default-deny semantics for non-PR nightly events.
Files:
.github/workflows/nightly-build.ymldocs/plans/current_spec.md
Dependencies:
security-pr.ymlkeeps requiredworkflow_dispatchpr_numbercontract.
Validation gates:
actionlintpasses.- Nightly manual dispatch run passes both targeted failure points.
- SBOM artifact upload succeeds through primary path or fallback path.
- Explicit run-scoped/time-scoped negative check confirms zero
bot-triggered
security-pr.ymldispatches during the nightly run window. - Positive manual dispatch check with valid
pr_numbersucceeds.
Rollback and contingency:
- Revert PR-1.
- If both primary and fallback Syft paths fail, treat as blocking regression and do not merge until generation criteria pass.
11. Complexity Estimate
- Implementation complexity: Low.
- Validation complexity: Medium (requires workflow run completion).
- Blast radius: Low (single workflow file, no runtime code changes).