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
48 changes: 6 additions & 42 deletions .github/workflows/ci_scheduled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,11 @@
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
Comment thread
marcusburghardt marked this conversation as resolved.
Dismissed
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
40 changes: 7 additions & 33 deletions .github/workflows/ci_security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,23 @@ 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
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-02
63 changes: 63 additions & 0 deletions openspec/changes/fix-ci-security-reusable-migration/design.md
Original file line number Diff line number Diff line change
@@ -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 `@<sha> # <version>` 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).
27 changes: 27 additions & 0 deletions openspec/changes/fix-ci-security-reusable-migration/proposal.md
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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`)
22 changes: 22 additions & 0 deletions openspec/changes/fix-ci-security-reusable-migration/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## 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).

<!-- spec-review: passed -->
<!-- code-review: passed -->
1 change: 1 addition & 0 deletions openspec/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
schema: spec-driven
Loading