From bb24415f7bf40755545c4a5bfdae589577072807 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Wed, 2 Sep 2026 14:08:19 +0000 Subject: [PATCH 1/3] ci: migrate ci_security and ci_scheduled to org-infra reusable workflows Replace inline osv-scanner and scorecard-action steps (both using dead SHAs) with calls to complytime/org-infra reusable workflows: - ci_security.yml: reusable_vuln_scan.yml + reusable_security.yml - ci_scheduled.yml: reusable_scheduled.yml Pins org-infra at 0c784711 (v0.7.1), matching unbound-force/unbound-force and complytime/.github. Resolves the OSV-Scanner and OpenSSF Scorecards hard-fails on PR #40. Closes #NNN --- .github/workflows/ci_scheduled.yml | 48 ++------------ .github/workflows/ci_security.yml | 42 +++---------- .../.openspec.yaml | 2 + .../design.md | 63 +++++++++++++++++++ .../proposal.md | 27 ++++++++ .../specs/ci-security/spec.md | 53 ++++++++++++++++ .../tasks.md | 21 +++++++ 7 files changed, 181 insertions(+), 75 deletions(-) create mode 100644 openspec/changes/fix-ci-security-reusable-migration/.openspec.yaml create mode 100644 openspec/changes/fix-ci-security-reusable-migration/design.md create mode 100644 openspec/changes/fix-ci-security-reusable-migration/proposal.md create mode 100644 openspec/changes/fix-ci-security-reusable-migration/specs/ci-security/spec.md create mode 100644 openspec/changes/fix-ci-security-reusable-migration/tasks.md diff --git a/.github/workflows/ci_scheduled.yml b/.github/workflows/ci_scheduled.yml index 30358fc..b339c78 100644 --- a/.github/workflows/ci_scheduled.yml +++ b/.github/workflows/ci_scheduled.yml @@ -11,47 +11,11 @@ permissions: security-events: none jobs: - osv-scanner: - name: OSV-Scanner (Scheduled) - runs-on: ubuntu-latest - timeout-minutes: 10 + call_reusable_scheduled: + name: OSV-Scanner and Scorecards permissions: contents: read - actions: read - security-events: write - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Run OSV-Scanner - uses: google/osv-scanner-action/osv-scanner-action@e5012758b0e8dd5bfb87e3def776e6a05a2a5af2 # v2.0.2 - with: - scan-args: |- - --recursive - . - - scorecards: - name: OpenSSF Scorecards (Scheduled) - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - id-token: write - security-events: write - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Run Scorecards - uses: ossf/scorecard-action@05b42c624433fc40578a4093d9a6c705f1243920 # v2.4.2 - with: - results_file: results.sarif - results_format: sarif - publish_results: true - - - name: Upload SARIF - uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3 - with: - sarif_file: results.sarif + actions: read # Required to upload SARIF file to CodeQL. See: https://github.com/github/codeql-action/issues/2117 + security-events: write # Required for writing security events to upload SARIF file to security tab + id-token: write # Needed to access GitHub's OIDC token which verifies the authenticity of the result when publishing it. + uses: complytime/org-infra/.github/workflows/reusable_scheduled.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1 diff --git a/.github/workflows/ci_security.yml b/.github/workflows/ci_security.yml index a99bc1d..97f8deb 100644 --- a/.github/workflows/ci_security.yml +++ b/.github/workflows/ci_security.yml @@ -13,49 +13,25 @@ permissions: actions: none id-token: none security-events: none + packages: none jobs: - osv-scanner: + call_reusable_vuln_scan: name: OSV-Scanner - runs-on: ubuntu-latest - timeout-minutes: 10 permissions: contents: read actions: read security-events: write - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + packages: write # required by reusable even when trivy image scan is skipped + id-token: write # required by reusable even when trivy image scan is skipped + uses: complytime/org-infra/.github/workflows/reusable_vuln_scan.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1 + with: + enable_trivy_source: false - - name: Run OSV-Scanner - uses: google/osv-scanner-action/osv-scanner-action@e5012758b0e8dd5bfb87e3def776e6a05a2a5af2 # v2.0.2 - with: - scan-args: |- - --recursive - . - - scorecards: + call_reusable_security: name: OpenSSF Scorecards - runs-on: ubuntu-latest - timeout-minutes: 10 permissions: contents: read id-token: write security-events: write - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Run Scorecards - uses: ossf/scorecard-action@05b42c624433fc40578a4093d9a6c705f1243920 # v2.4.2 - with: - results_file: results.sarif - results_format: sarif - publish_results: true - - - name: Upload SARIF - uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3 - with: - sarif_file: results.sarif + uses: complytime/org-infra/.github/workflows/reusable_security.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1 diff --git a/openspec/changes/fix-ci-security-reusable-migration/.openspec.yaml b/openspec/changes/fix-ci-security-reusable-migration/.openspec.yaml new file mode 100644 index 0000000..032461f --- /dev/null +++ b/openspec/changes/fix-ci-security-reusable-migration/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-02 diff --git a/openspec/changes/fix-ci-security-reusable-migration/design.md b/openspec/changes/fix-ci-security-reusable-migration/design.md new file mode 100644 index 0000000..4f18e3b --- /dev/null +++ b/openspec/changes/fix-ci-security-reusable-migration/design.md @@ -0,0 +1,63 @@ +## Context + +Both `ci_security.yml` and `ci_scheduled.yml` contain two inline jobs with the same dead SHAs: +- `osv-scanner`: calls `google/osv-scanner-action/osv-scanner-action@e5012758...` (v2.0.2) — SHA gone upstream. +- `scorecards`: calls `ossf/scorecard-action@05b42c62...` (v2.4.2) — SHA gone upstream. + +Both hard-fail with "unable to find version" — `ci_security.yml` on every PR, `ci_scheduled.yml` on the nightly cron. A previous attempt to fix the OSV SHA in `ci_security.yml` was reverted (`89ea9d7`) because the fix also required switching from the inline step form to OSV's own reusable workflow caller form — a structural change, not just a SHA bump. + +The canonical fix pattern is established in `unbound-force/unbound-force` and `complytime/.github`, both of which replace the inline jobs with calls to `complytime/org-infra` reusable workflows. Notably, `complytime/.github` consolidates the scheduled OSV + Scorecard runs into a single `reusable_scheduled.yml` call rather than keeping separate inline jobs. + +See `proposal.md` for motivation. + +## Goals / Non-Goals + +**Goals:** +- Replace both broken inline jobs with org-infra reusable workflow calls. +- Match the structure and permission set used in `unbound-force/unbound-force` (the org canonical). +- Pin the org-infra SHA using the same `@ # ` comment convention used everywhere else in this repo. + +**Non-Goals:** +- Adding Trivy source scanning (not in scope for this repo; `enable_trivy_source` defaults to `false` in `reusable_vuln_scan.yml`). +- Touching `ci_checks.yml` — its inline Go lint/test jobs are repo-specific and not covered by `reusable_ci.yml`. +- Touching `ci_dependencies.yml` — that is covered separately by issue #41. + +## Decisions + +### Decision: Pin to the same org-infra SHA as `unbound-force/unbound-force` + +The org canonical (`unbound-force/unbound-force`) pins both reusable calls at `0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1`. Using the same SHA avoids introducing drift between managed repos and is immediately verifiable against a known-good reference. + +**Alternative considered**: Pin to the latest org-infra `main` SHA (`6f6dc6c9...`). Rejected because it has not been vetted as a tagged release and would silently diverge from the org canonical. + +### Decision: Mirror the `unbound-force/unbound-force` permissions exactly + +The org canonical sets: +- `call_reusable_vuln_scan`: `contents: read`, `actions: read`, `security-events: write`, `packages: write`, `id-token: write` +- `call_reusable_security`: `contents: read`, `id-token: write`, `security-events: write` + +The `packages: write` and `id-token: write` on the vuln-scan job are required by the reusable even when trivy image scanning is skipped (the reusable's job-level permissions are inherited). Omitting them causes a permission error at runtime. + +**Alternative considered**: Trim to only what the enabled inputs need. Rejected — the org-infra reusable declares its permission needs; callers must satisfy them regardless of which inputs are toggled. + +### Decision: Keep the same job names as the current workflow + +The current check names (`OSV-Scanner`, `OpenSSF Scorecards`) are set by the reusable workflows' own job `name:` fields and will be preserved automatically. The caller `name:` fields should match so GitHub's required-checks UI stays consistent. + +### Decision: Use `reusable_scheduled.yml` for `ci_scheduled.yml` rather than separate reusable calls + +`complytime/.github` consolidates the scheduled OSV + Scorecard runs into a single `reusable_scheduled.yml` call. The alternative — calling `reusable_vuln_scan.yml` and `reusable_security.yml` individually from `ci_scheduled.yml` (as `ci_security.yml` does) — would work but diverges from the peer reference. `reusable_scheduled.yml` is purpose-built for the cron use case and requires a simpler permission set than the combined individual calls. + +**Alternative considered**: Call `reusable_vuln_scan.yml` + `reusable_security.yml` from `ci_scheduled.yml` the same way `ci_security.yml` does. Rejected — `reusable_scheduled.yml` already encapsulates this combination for the scheduled context and is what the peer repo uses. + +## Risks / Trade-offs + +- **[Risk] org-infra SHA drifts** → Mitigation: adding `dependabot.yml` for `github-actions` ecosystem (tracked in issue #41 / PR #40) will auto-propose SHA updates via Dependabot PRs. +- **[Risk] Future org-infra reusable changes break callers** → Mitigation: pinning by SHA means no surprise breakage; updates are explicit and reviewable. + +## Migration Plan + +1. Edit `.github/workflows/ci_security.yml` on branch `opsx/fix-ci-security-reusable-migration` — replace both inline jobs with `reusable_vuln_scan.yml` and `reusable_security.yml` caller jobs. +2. Edit `.github/workflows/ci_scheduled.yml` on the same branch — replace both inline jobs with a single `reusable_scheduled.yml` caller job. +3. Push — CI re-runs automatically on the open PR and the two failing checks should go green. The scheduled workflow fix takes effect on the next nightly cron run after merge. +4. No rollback complexity: if the reusable calls fail, reverting the two file edits restores the previous state (though the previous state also hard-fails, so a rollback is only useful to isolate a new issue). diff --git a/openspec/changes/fix-ci-security-reusable-migration/proposal.md b/openspec/changes/fix-ci-security-reusable-migration/proposal.md new file mode 100644 index 0000000..4f44165 --- /dev/null +++ b/openspec/changes/fix-ci-security-reusable-migration/proposal.md @@ -0,0 +1,27 @@ +## Why + +`ci_security.yml` and `ci_scheduled.yml` both pin two action SHAs that no longer resolve: `google/osv-scanner-action@e5012758...` (v2.0.2) and `ossf/scorecard-action@05b42c62...` (v2.4.2). The dead SHAs cause a hard CI failure on every PR — including PR #40 — with "unable to find version", and will fail the nightly cron job identically. The upstream actions have moved on and those SHAs are gone. Adopting the org-infra reusable workflows (`reusable_vuln_scan.yml`, `reusable_security.yml`, and `reusable_scheduled.yml`) fixes both files by delegating to SHAs that org-infra actively maintains, and aligns this repo with the org-standard pattern already used in `unbound-force/unbound-force` and `complytime/.github`. + +## What Changes + +- Replace the inline `osv-scanner` and `scorecards` jobs in `ci_security.yml` with calls to `reusable_vuln_scan.yml` and `reusable_security.yml` from `complytime/org-infra` — this also switches from the old inline step form to the OSV-maintained reusable workflow caller form (v2.5.1). +- Replace the inline `osv-scanner` and `scorecards` jobs in `ci_scheduled.yml` with a single call to `reusable_scheduled.yml` from `complytime/org-infra`, which handles the scheduled OSV + Scorecard runs together (matching the `complytime/.github` pattern). +- Both files pin org-infra at the same SHA already used by `unbound-force/unbound-force` (`0c784711... # v0.7.1`). + +## Capabilities + +### New Capabilities + +- `ci-security`: The security CI workflows (`ci_security.yml` and `ci_scheduled.yml`) must delegate OSV scanning and OpenSSF Scorecard analysis to org-infra reusable workflows rather than running inline steps with self-managed action SHAs. + +### Modified Capabilities + +*(none — no existing specs)* + +## Impact + +- `.github/workflows/ci_security.yml` — inline OSV + Scorecard jobs replaced. +- `.github/workflows/ci_scheduled.yml` — inline OSV + Scorecard jobs replaced. +- No other workflows, configs, or source files are affected. +- The `OSV-Scanner` and `OpenSSF Scorecards` check names visible in GitHub CI remain the same (defined by the reusable workflows). +- Unblocks PR #40 (`issue-39-enrich-org-labels`) which currently has two hard CI failures from `ci_security.yml`; also prevents the same failures from hitting the nightly cron via `ci_scheduled.yml`. diff --git a/openspec/changes/fix-ci-security-reusable-migration/specs/ci-security/spec.md b/openspec/changes/fix-ci-security-reusable-migration/specs/ci-security/spec.md new file mode 100644 index 0000000..9640453 --- /dev/null +++ b/openspec/changes/fix-ci-security-reusable-migration/specs/ci-security/spec.md @@ -0,0 +1,53 @@ +## Purpose + +Defines the security CI workflows' obligations for OSV vulnerability scanning and OpenSSF Scorecard analysis — both on PRs (`ci_security.yml`) and on schedule (`ci_scheduled.yml`) — delegating execution to org-infra reusable workflows rather than managing action SHAs inline. + +## ADDED Requirements + +### Requirement: OSV scanning via org-infra reusable workflow +The security CI workflow SHALL delegate OSV dependency scanning to `complytime/org-infra/.github/workflows/reusable_vuln_scan.yml` rather than running an inline `google/osv-scanner-action` step. + +#### Scenario: OSV scan runs on pull_request to main +- **WHEN** a pull request targets the `main` branch +- **THEN** the `reusable_vuln_scan.yml` caller job is triggered and the OSV-Scanner check is reported in CI + +#### Scenario: OSV scan runs on push to main +- **WHEN** a commit is pushed directly to `main` +- **THEN** the `reusable_vuln_scan.yml` caller job is triggered and the OSV-Scanner check is reported in CI + +#### Scenario: Dead OSV SHA no longer blocks CI +- **WHEN** a PR is opened against `main` +- **THEN** the CI workflow SHALL NOT fail with "unable to find version" for `google/osv-scanner-action` + +### Requirement: OpenSSF Scorecard analysis via org-infra reusable workflow +The security CI workflow SHALL delegate OpenSSF Scorecard analysis to `complytime/org-infra/.github/workflows/reusable_security.yml` rather than running an inline `ossf/scorecard-action` step. + +#### Scenario: Scorecard runs on push to main +- **WHEN** a commit is pushed directly to `main` +- **THEN** the `reusable_security.yml` caller job is triggered and the OpenSSF Scorecards check is reported in CI + +#### Scenario: Scorecard runs on schedule +- **WHEN** the scheduled cron trigger fires +- **THEN** the `reusable_security.yml` caller job is triggered + +#### Scenario: Dead Scorecard SHA no longer blocks CI +- **WHEN** a PR is opened against `main` +- **THEN** the CI workflow SHALL NOT fail with "unable to find version" for `ossf/scorecard-action` + +### Requirement: Scheduled OSV and Scorecard runs via org-infra reusable workflow +The scheduled CI workflow SHALL delegate the nightly OSV scan and Scorecard analysis to `complytime/org-infra/.github/workflows/reusable_scheduled.yml` rather than running inline steps. + +#### Scenario: Scheduled workflow runs on cron trigger +- **WHEN** the nightly cron trigger fires +- **THEN** the `reusable_scheduled.yml` caller job is triggered and both OSV-Scanner and OpenSSF Scorecards checks are executed + +#### Scenario: Dead SHAs no longer block scheduled runs +- **WHEN** the scheduled cron trigger fires +- **THEN** the workflow SHALL NOT fail with "unable to find version" for either `google/osv-scanner-action` or `ossf/scorecard-action` + +### Requirement: Caller workflows pin org-infra reusable by SHA +The `uses:` reference to each org-infra reusable workflow in both `ci_security.yml` and `ci_scheduled.yml` SHALL be pinned to a full commit SHA with a version comment, following the same pin-by-SHA convention used throughout this repo. + +#### Scenario: SHA pin present in both workflow files +- **WHEN** `ci_security.yml` or `ci_scheduled.yml` is read +- **THEN** each `uses: complytime/org-infra/...` line includes a full 40-character SHA and an inline version comment (e.g., `# v1.2.3`) diff --git a/openspec/changes/fix-ci-security-reusable-migration/tasks.md b/openspec/changes/fix-ci-security-reusable-migration/tasks.md new file mode 100644 index 0000000..fab6ee5 --- /dev/null +++ b/openspec/changes/fix-ci-security-reusable-migration/tasks.md @@ -0,0 +1,21 @@ +## 1. Rewrite ci_security.yml + +- [x] 1.1 Replace the inline `osv-scanner` job in `.github/workflows/ci_security.yml` with a `call_reusable_vuln_scan` job that calls `complytime/org-infra/.github/workflows/reusable_vuln_scan.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1` with permissions `contents: read`, `actions: read`, `security-events: write`, `packages: write`, `id-token: write`. Verify the job block matches the `unbound-force/unbound-force` canonical. + +- [x] 1.2 Replace the inline `scorecards` job with a `call_reusable_security` job that calls `complytime/org-infra/.github/workflows/reusable_security.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1` with permissions `contents: read`, `id-token: write`, `security-events: write`. Verify the job block matches the canonical. + +- [x] 1.3 Update the top-level `permissions` block to add `packages: none` alongside the existing `actions: none`, `id-token: none`, `security-events: none` (mirroring `complytime/.github`). Verify no stale permission fields remain. + +## 2. Rewrite ci_scheduled.yml + +- [x] 2.1 Replace both inline jobs (`osv-scanner` and `scorecards`) in `.github/workflows/ci_scheduled.yml` with a single `call_reusable_scheduled` job that calls `complytime/org-infra/.github/workflows/reusable_scheduled.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1` with permissions `contents: read`, `actions: read`, `security-events: write`, `id-token: write`. Verify the result matches the `complytime/.github` canonical. + +## 3. Verify locally + +- [x] 3.1 Run `make lint` (or `yamllint .github/workflows/ci_security.yml .github/workflows/ci_scheduled.yml`) and confirm zero lint errors on both edited files. + +## 4. Push and confirm CI + +- [ ] 4.1 Push the branch. In PR #40, confirm that both `OSV-Scanner` and `OpenSSF Scorecards` checks transition from FAILURE to SUCCESS (or neutral/skipped if the repo lacks OIDC publish rights for Scorecards). Verify the `Dependency Review` check is the only remaining failure (addressed separately by issue #41). + + From 927ae8d2fdd5be234b048d24ffc81d8855b34777 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Wed, 2 Sep 2026 14:15:34 +0000 Subject: [PATCH 2/3] chore(openspec): mark code review passed and add config scaffold - Add marker to tasks.md - Add openspec/config.yaml (schema: spec-driven) generated by CLI Assisted-by: claude-sonnet Generated with AI assistance (claude-sonnet) --- openspec/changes/fix-ci-security-reusable-migration/tasks.md | 1 + openspec/config.yaml | 1 + 2 files changed, 2 insertions(+) create mode 100644 openspec/config.yaml diff --git a/openspec/changes/fix-ci-security-reusable-migration/tasks.md b/openspec/changes/fix-ci-security-reusable-migration/tasks.md index fab6ee5..5bec858 100644 --- a/openspec/changes/fix-ci-security-reusable-migration/tasks.md +++ b/openspec/changes/fix-ci-security-reusable-migration/tasks.md @@ -19,3 +19,4 @@ - [ ] 4.1 Push the branch. In PR #40, confirm that both `OSV-Scanner` and `OpenSSF Scorecards` checks transition from FAILURE to SUCCESS (or neutral/skipped if the repo lacks OIDC publish rights for Scorecards). Verify the `Dependency Review` check is the only remaining failure (addressed separately by issue #41). + diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 0000000..b4bbeb9 --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1 @@ +schema: spec-driven From d12588dcf60e47c5b7763cbf16a6ab855ce49010 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Wed, 2 Sep 2026 14:41:23 +0000 Subject: [PATCH 3/3] ci: drop packages and id-token write from call_reusable_vuln_scan reusable_vuln_scan.yml does not require packages: write or id-token: write when enable_trivy_source: false. Aligns ci_security.yml permissions exactly with complytime/.github, eliminating the Scorecard code-scanning alert on PRs. Assisted-by: claude-sonnet Generated with AI assistance (claude-sonnet) --- .github/workflows/ci_security.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci_security.yml b/.github/workflows/ci_security.yml index 97f8deb..2cb4f2a 100644 --- a/.github/workflows/ci_security.yml +++ b/.github/workflows/ci_security.yml @@ -22,8 +22,6 @@ jobs: contents: read actions: read security-events: write - packages: write # required by reusable even when trivy image scan is skipped - id-token: write # required by reusable even when trivy image scan is skipped uses: complytime/org-infra/.github/workflows/reusable_vuln_scan.yml@0c784711926c9864f027ec565fd7c06a382d80f8 # v0.7.1 with: enable_trivy_source: false