Skip to content

feat: OpenSSF Scorecard and dependency review workflows#33

Merged
ms280690 merged 14 commits into
mainfrom
issue-29-governance-observability
May 29, 2026
Merged

feat: OpenSSF Scorecard and dependency review workflows#33
ms280690 merged 14 commits into
mainfrom
issue-29-governance-observability

Conversation

@ms280690
Copy link
Copy Markdown
Collaborator

@ms280690 ms280690 commented May 21, 2026

Summary

Implements the code-deliverable items from issue #29 (enterprise governance and observability).

  • scorecard.yml — weekly OpenSSF Scorecard analysis; publishes results to the OpenSSF database and uploads SARIF to the GitHub Security tab
  • dependency-review.yml — blocks PRs that introduce dependencies with known vulnerabilities or denied licenses; reusable via workflow_call

Notes

Org-level items remaining in #29 (not implementable as code)

  • Org rulesets (required workflows enforcement) — GitHub UI
  • Audit log streaming to Wazuh — GitHub org settings
  • Actor rules for fork PRs — GitHub org settings

Closes #29

Test plan

  • Scorecard workflow runs on push to branch and completes without error
  • Scorecard results appear in the Security tab → Code scanning
  • Dependency review workflow triggers on PRs touching lockfiles
  • All action references are SHA-pinned (actionlint + zizmor pass)

For reviews I ran:

  1. Copilot(won't do it again)
  2. claude /security-review
  3. claude /code-review
  4. https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main securing-github-actions-workflows
  5. https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main detecting-supply-chain-attacks-in-ci-cd

scorecard.yml:
- Runs weekly (Monday 06:00 UTC), on push to main, and on workflow_dispatch
- Publishes results to the OpenSSF database (public repo, OIDC-signed)
- Uploads SARIF to GitHub Security tab and as a retained artifact
- Target score >= 8.0/10 per issue #29 acceptance criteria

dependency-review.yml:
- Triggers on PRs touching any supported lockfile format
- Reusable via workflow_call with fail-on-severity and deny-licenses inputs
- Posts a summary comment on the PR via comment-summary-in-pr: always
- Default deny list: GPL-2.0, GPL-3.0, AGPL-3.0

Also brings .pre-commit-config.yaml forward from issue-25 branch so
local hooks work while that PR is pending merge.

All action references SHA-pinned per #25 authoring standards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds organization-governance workflows to improve supply-chain security posture for this repository and to provide a reusable dependency gate for consuming repositories.

Changes:

  • Introduces an OpenSSF Scorecard workflow that runs on schedule/push and uploads SARIF results to GitHub Security.
  • Adds a reusable Dependency Review workflow (also runnable on PRs) with severity/license policy inputs.
  • Adds a local .pre-commit-config.yaml to run actionlint and zizmor consistently.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
.pre-commit-config.yaml Adds pre-commit hooks for action/workflow linting and security checks.
.github/workflows/scorecard.yml Runs OpenSSF Scorecard and uploads SARIF/artifacts for Security tab visibility.
.github/workflows/dependency-review.yml Adds dependency review gate (PR + reusable workflow) with policy inputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/dependency-review.yml Outdated
Comment thread .github/workflows/dependency-review.yml Outdated
Comment thread .github/workflows/scorecard.yml Outdated
@ms280690
Copy link
Copy Markdown
Collaborator Author

Code Review

Strengths

  • All action refs SHA-pinned with version comments — consistent with the standard being established in PR Add workflow authoring standards, actionlint/zizmor gate, and CONTRIB… #32.
  • persist-credentials: false on all checkout steps.
  • permissions: read-all at workflow level with job-level minimum overrides on Scorecard — the comment explaining why read-all is needed is a good call given it looks like an over-permission at first glance.
  • workflow_call inputs are well-designed on dependency-review.yml — exposed the two most likely customisation points (fail-on-severity, deny-licenses) with sensible defaults and descriptions.
  • || 'default' fallback pattern for workflow_call inputs is correct — handles the case where the workflow is triggered by pull_request (no inputs provided).
  • Artifact upload of SARIF before the CodeQL upload is a nice belt-and-suspenders approach.
  • retention-days: 5 on the artifact is appropriately short for ephemeral scan results.

Issues

1. upload-artifact action SHA looks incorrect

uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a  # v7.0.1

actions/upload-artifact is currently on v4 (latest stable). v7 does not exist — this SHA may resolve to an unrelated commit or fail. Verify against:

gh api repos/actions/upload-artifact/commits/v4 --jq '.sha'

2. No if: always() on the SARIF upload in scorecard.yml

- uses: github/codeql-action/upload-sarif@...
  with:
    sarif_file: scorecard-results.sarif

If the Scorecard action exits non-zero (rate-limited, transient failure), the SARIF upload will be skipped. Adding if: always() ensures results are uploaded even on partial failures — consistent with the pattern used in PR #32's zizmor upload.

3. Duplicate defaults for workflow_call inputs

fail-on-severity and deny-licenses have defaults defined twice: once in the inputs: block and once in the || fallback expressions. If these drift apart silently (e.g., someone updates one but not the other), behaviour will differ between pull_request and workflow_call triggers. GitHub resolves inputs.default even when triggered by pull_request, so the || fallback expressions can be dropped to keep defaults in one place.

4. fail-on-severity default is critical only

Blocking only on critical means high severity findings (CVSS 8.x) pass silently. For a security-focused org library, high is a more defensible default. Worth a conscious policy decision either way.

5. pull_request path filter omits some lockfiles

The list is comprehensive but misses a few patterns for completeness as a general-purpose org template:

  • **/pdm.lock (PDM, Python)
  • **/mix.lock (Elixir)
  • **/Package.resolved (Swift)

Not critical for Sparkgeo's current stack — flagging for awareness.

6. .pre-commit-config.yaml merge order dependency

As noted in the PR description, this file is identical to the one in PR #32. Whichever PR merges second will need a no-op conflict resolution. Merge PR #32 first to avoid the conflict landing here.


Priority before merging

  • (1) Verify the upload-artifact SHA — v7 does not exist, this is likely pointing at the wrong commit.
  • (2) Add if: always() to the Scorecard SARIF upload step.

Items 3 and 4 are policy/cleanup calls; items 5 and 6 are low priority.

ms280690 and others added 10 commits May 21, 2026 15:30
scorecard.yml:
- Remove redundant workflow-level permissions: read-all; job-level block
  is definitive (job permissions override, not merge, workflow-level)
- Add if: always() to SARIF upload so results are captured even on
  transient Scorecard failures

dependency-review.yml:
- Raise fail-on-severity default from critical to high — CVSS 8.x
  findings should not pass silently in a security-focused org library
- Add pdm.lock, mix.lock, Package.resolved to lockfile path filter
- Expose comment-summary-in-pr as a workflow_call input (default:
  on-failure) so callers can control PR comment verbosity; avoids
  requiring pull-requests: write for callers that don't want comments

Note: upload-artifact v7.0.1 SHA (043fb46d) is confirmed correct —
v7.0.1 is the current latest release of actions/upload-artifact.
The || fallback pattern on inputs.* is intentional and necessary:
inputs.default is only applied on workflow_call; pull_request-triggered
runs receive an empty string from the inputs context, requiring the
fallback to supply the default value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scorecard.yml / dependency-review.yml:
- Add workflow-level description comments (line 2)
- Add job-level description comments
- Capitalise job display names for consistency with Actions Quality Gate

README.md:
- Add OpenSSF Scorecard and Dependency Review to workflows table
- Add usage examples for both workflows including workflow_call inputs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ci.yml calls workflow-lint.yml via workflow_call and runs the
storage-optimizer composite action on every push and PR. Serves
as both a test harness and a reference implementation for consuming
repos. Also adds ci.yml to README workflows table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ci.yml

Each workflow capability is now a standalone composite action following
the same pattern as storage-optimizer and terramate-opentofu-setup:

- .github/actions/actionlint/  — actionlint via reviewdog
- .github/actions/zizmor/      — zizmor static security analysis
- .github/actions/scorecard/   — OpenSSF Scorecard + SARIF upload
- .github/actions/dependency-review/ — dependency-review-action with inputs

Deleted reusable workflows: workflow-lint.yml, scorecard.yml,
dependency-review.yml. All logic moved into composite actions above.

ci.yml updated to one job per composite action, all running in parallel.
scorecard skips pull_request; dependency-review runs only on pull_request.
schedule trigger added to ci.yml (weekly Monday 06:00 UTC) for scorecard.

README rewritten to reflect composite-action-first structure with
per-action usage examples and required permissions documented.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename .github/actions/actionlint/ → .github/actions/github-actionlint/
to make it explicit the action is specific to GitHub Actions workflows.
Update ci.yml and README references accordingly.

CONTRIBUTING.md:
- Update description from "workflows" to "composite actions" to match
  the new repo structure
- Rewrite "Adding a new workflow" → "Adding a new action" to reflect
  composite-action-first approach: create action.yml, add ci.yml job,
  update README table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Verifies both tools install correctly. Terramate stack steps
(generate, init, list) are no-ops in this repo since no stacks
are defined. OPENTOFU_VERSION and TERRAMATE_VERSION set via
job-level env to match the action defaults.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
terramate --changed diffs against git history; shallow clone (depth 1)
has only one commit so the flag errors. Full history required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ossf/scorecard-action hard-requires the default branch and fails with
"Only the default branch main is supported" on feature branches.
Added github.ref == 'refs/heads/main' guard so the job is skipped
on workflow_dispatch from feature branches and only runs on push
to main and the weekly schedule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When scorecard-action fails (e.g. wrong branch), scorecard-results.sarif
is not produced. The if: always() upload steps then error with
"Path does not exist". Add a check-sarif step that sets an output,
and gate both upload steps on that output so they skip cleanly
rather than error when the file is absent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ms280690
Copy link
Copy Markdown
Collaborator Author

Security Review: issue-29-governance-observability

Scope: .github/actions/dependency-review/action.yml, .github/actions/github-actionlint/action.yml, .github/actions/scorecard/action.yml, .github/actions/zizmor/action.yml, .github/workflows/ci.yml, CONTRIBUTING.md, README.md


Result: No findings

All candidate findings were filtered as false positives after parallel verification. Summary:

Finding Initial Confidence Filter Verdict Reason
checks: write + reviewdog compromise path 7/10 False positive (3/10) Requires pre-compromised pinned SHA — supply chain risk, not a workflow vulnerability
publish_results: true exposes posture data 8/10 False positive (2/10) Requires independent repo visibility misconfiguration; not a concrete exploitable path
Terramate action ignores declared inputs (env vs inputs) 9/10 False positive (2/10) Env vars are trusted values; no untrusted input path to exploit
SHA comment mismatch risk 6/10 Below threshold Theoretical; SHA pinning is the trust anchor by design
OIDC id-token: write blast radius 5/10 Below threshold Mitigated by SHA pinning + refs/heads/main guard
deny-licenses pass-through 4/10 Below threshold No untrusted caller path in current usage
SARIF artifact predictable name 5/10 Below threshold Artifact access requires repo read permission

Confirmed safe patterns:

  • No pull_request_target trigger (pwn-request vector absent)
  • No user-controlled context (github.event.*, github.head_ref) interpolated into run: steps
  • All checkout steps use persist-credentials: false
  • Top-level permissions locked to contents: read; jobs narrow further
  • Scorecard correctly gated to refs/heads/main only
  • No hardcoded secrets or tokens

🤖 Generated with Claude Code

@ms280690
Copy link
Copy Markdown
Collaborator Author

Code Review

Findings

1. terramate-opentofu-setup/action.yml:19 — Declared inputs are dead code

The action declares opentofu_version and terramate_version inputs but its steps consume ${{ env.OPENTOFU_VERSION }} and ${{ env.TERRAMATE_VERSION }} — the inputs are never read.

Failure scenario: A caller passes with: opentofu_version: "1.9.0" to pin an older release; the action silently installs whatever env.OPENTOFU_VERSION is set to (or errors if unset). The declared interface is misleading.

Fix: Change lines 19 and 25 in terramate-opentofu-setup/action.yml to use ${{ inputs.opentofu_version }} and ${{ inputs.terramate_version }} respectively, then remove the env: block from the terramate-opentofu-setup job in ci.yml.


2. ci.yml:86 — Version defaults duplicated in two places

ci.yml sets OPENTOFU_VERSION: "1.10.0" and TERRAMATE_VERSION: "0.14.7" in its env: block; the same values appear as default: in the action's inputs. Two sources of truth for the same version pins.

Failure scenario: A version bump is applied to the action's inputs.default but missed in ci.yml's env: block; the job silently runs the old version because env wins over the input. Resolves automatically once finding #1 is fixed.


3. workflow-lint.yml (deleted) — workflow_call removed with no migration path

The deleted file had a workflow_call trigger with an explicit comment: "Intentionally zero-input: callers get the same lint gate with no configuration." The new ci.yml has no workflow_call trigger. The composite actions are callable individually but require callers to construct their own job shell with correct permissions.

Failure scenario: Any Sparkgeo repo referencing uses: sparkgeo/github-actions/.github/workflows/workflow-lint.yml@main (branch-pinned, not SHA-pinned) gets a "workflow not found" error on the next PR — their lint gate silently drops.

Fix: Document the migration in the PR description and CONTRIBUTING.md; audit org repos for branch-pinned workflow_call references before merging.


4. scorecard/action.yml:13publish_results: true hardcoded, no override input

publish_results: true is hardcoded with no corresponding input:. ossf/scorecard-action errors when this flag is set on a private repository.

Failure scenario: A team adopts this composite action on a private repo and gets a failing workflow with no with: knob to disable publishing — they must fork and edit the action file.

Fix: Expose a publish_results input (default: true) and pass ${{ inputs.publish_results }} to the action.


5. scorecard/action.yml:15check-sarif probe step is unnecessary complexity

The three-step pattern (shell probe → output variable → two conditional upload steps) exists because upload-artifact errors on a missing file. But actions/upload-artifact has a first-class if-no-files-found: ignore option, and hashFiles() can gate upload-sarif without a shell step.

Simpler form:

- uses: actions/upload-artifact@...
  if: always()
  with:
    name: scorecard-results
    path: scorecard-results.sarif
    retention-days: 5
    if-no-files-found: ignore

- uses: github/codeql-action/upload-sarif@...
  if: always() && hashFiles('scorecard-results.sarif') != ''
  with:
    sarif_file: scorecard-results.sarif
    category: scorecard

This eliminates the check-sarif step and removes a shell-subprocess failure mode.


6. scorecard/action.yml:1 — Required permissions undocumented

The composite action requires id-token: write (for OIDC signing) and security-events: write (for SARIF upload) on the calling job but declares neither in its description nor as inputs.

Failure scenario: A consumer adds the action to a job with only contents: read; they get cryptic 401/403 errors from ossf/scorecard-action and upload-sarif with no guidance on what permissions to add.

Fix: Add a # Required permissions: id-token: write, security-events: write note to the action description, mirroring the pattern used in the README usage example.


7. ci.yml:25 — Checkout SHA copy-pasted into all six jobs

de0fac2e4500dabe0009e67214ff5f5447ce83dd appears verbatim in all six jobs. A single SHA rotation requires six edits.

Failure scenario: A security advisory requires rotating the actions/checkout SHA; one of six occurrences is missed; that job silently runs a stale version. Renovate will open six separate hunks for a single bump, increasing review noise and merge-conflict risk.


8. storage-optimizer/action.yml:1 — Runs docker system prune on a throwaway runner

The storage-optimizer CI job provisions its own ephemeral runner, runs docker system prune -af (30–60 s), deletes JDKs/SDKs, then exits. No other job shares that runner.

Failure scenario: Pure billed runner-minutes with zero benefit. The action is only useful when called from a job that subsequently does heavy disk work on the same runner. Either remove the job from ci.yml or gate it with workflow_dispatch only.


🤖 Generated with Claude Code

@ms280690
Copy link
Copy Markdown
Collaborator Author

GitHub Actions Security Audit

Repository: sparkgeo/github-actions
Date: 2026-05-28
Scope: All workflow and composite action files on this branch


Findings

⛔ HIGH — Default workflow permissions set to write

Settings → Actions → General → Workflow permissions returns default_workflow_permissions: write.

Any future workflow added without an explicit permissions: block inherits write access to contents, issues, pull-requests, packages, etc. The existing ci.yml correctly sets explicit permissions on every job — but the repo-level default is wide open, making this a latent risk for every future contribution.

Fix: Settings → Actions → General → Workflow permissions → "Read repository contents and packages permissions"


⛔ HIGH — GitHub Actions can approve pull requests

can_approve_pull_request_reviews: true

Actions can post approving reviews that count toward branch protection approval thresholds — defeating human review requirements.

Fix: Settings → Actions → General → Workflow permissions → uncheck "Allow GitHub Actions to create and approve pull requests"


⛔ HIGH — No branch protection on main

main has no branch protection rules (confirmed via API). PRs can be merged without required reviews or passing status checks.

Fix: Settings → Branches → Add rule for main:

  • Require pull request before merging (min 1 approval)
  • Require status checks to pass: actionlint, zizmor
  • Require branches to be up to date before merging
  • Do not allow bypassing the above settings

⚠️ MEDIUM — No CODEOWNERS for .github/ paths

Any repo collaborator can merge workflow and action changes without security team sign-off.

Fix:

# .github/CODEOWNERS
.github/workflows/  @sparkgeo/security-team
.github/actions/    @sparkgeo/security-team

⚠️ MEDIUM — No Dependabot for github-actions ecosystem

All 10 action references are correctly SHA-pinned, but there is no .github/dependabot.yml for the github-actions ecosystem. SHA pins will drift; a security advisory on any pinned action requires manual discovery and rotation across all 7 files.

Fix:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    commit-message:
      prefix: "ci"

ℹ️ LOW — allowed_actions: all (no publisher allow-list)

Any GitHub Action from any author is permitted. For a repo that is a security library, a contributor could add a reference to a malicious publisher and it would not be blocked.

Fix: Settings → Actions → General → restrict to:

  • Actions created by GitHub
  • Marketplace verified creators
  • Explicit allow-list: ossf/, zizmorcore/, reviewdog/, terramate-io/, opentofu/

Passing checks ✅

Check Result
SHA pinning (all 10 references) ✅ Pass
Script injection (${{ }} in run: steps) ✅ Pass
pull_request_target trigger ✅ Pass — not used
persist-credentials: false on all checkouts ✅ Pass
OIDC / no static cloud credentials ✅ Pass
Job-level minimum permissions ✅ Pass
Scorecard gated to refs/heads/main only ✅ Pass

Score: 5/10

The workflow code is clean. The 3 HIGH findings are repo settings, not code — fix in Settings before treating this repo as a trusted security reference for the org.

🤖 Generated with Claude Code

@ms280690
Copy link
Copy Markdown
Collaborator Author

Supply Chain Attack Detection Scan

Tool: detecting-supply-chain-attacks-in-ci-cd
Date: 2026-05-28
Files scanned: .github/workflows/ci.yml + all 6 .github/actions/*/action.yml


Results

Check Result
Unpinned action references ✅ All 10 pinned to full 40-char SHA digests
Script injection (user-controlled context in run:) ✅ No vectors found
pull_request_target trigger ✅ Not used
persist-credentials: false on all checkouts ✅ Pass
Third-party action + sensitive write permissions ✅ None
Unexpected action publishers ✅ All match expected allow-list
Secrets echoed to logs ✅ None
Permissions blocks ⚠️ See note below

Note on permissions warnings

The scanner flagged all 6 composite action.yml files for missing permissions: blocks. These are false positives — composite actions have no permissions: schema in the GitHub Actions spec; permissions are declared by the calling job. All 6 calling jobs in ci.yml correctly declare minimum required permissions.

Action publisher allow-list (confirmed)

All external action references resolve to expected publishers:

Publisher Actions used
ossf/ scorecard-action
reviewdog/ action-actionlint
zizmorcore/ zizmor-action
opentofu/ setup-opentofu
terramate-io/ terramate-action
actions/ checkout, upload-artifact, dependency-review-action
github/ codeql-action/upload-sarif

Verdict

No supply chain attack vectors detected in workflow code. The outstanding gaps (no Dependabot SHA auto-update, allowed_actions: all repo setting, no branch protection) are settings-level — not detectable by file scan, and already captured in the security audit comment above.

🤖 Generated with Claude Code

terramate-opentofu-setup/action.yml:
- Fix declared inputs being dead code: replace env.OPENTOFU_VERSION /
  env.TERRAMATE_VERSION with inputs.opentofu_version / inputs.terramate_version
  so callers using `with:` actually get their version honoured

ci.yml:
- Remove env: block from terramate-opentofu-setup job (now uses input defaults)
- Gate storage-optimizer job to workflow_dispatch only — running docker prune
  on a throwaway ephemeral runner wastes CI minutes with zero benefit

scorecard/action.yml:
- Expose publish_results as an input (default: true) so private-repo callers
  can set it to false without forking the action
- Replace check-sarif shell probe + output variable with if-no-files-found: ignore
  on upload-artifact and hashFiles() gate on upload-sarif — removes a step and
  eliminates the shell-subprocess failure mode
- Add required permissions (id-token: write, security-events: write) to description

CONTRIBUTING.md:
- Add migration guide from the deleted workflow_call / workflow-lint.yml pattern
  to the equivalent composite action calls

.github/dependabot.yml: add github-actions ecosystem, weekly schedule,
  7-day cooldown to throttle supply-chain-attack-via-rapid-update vectors
.github/CODEOWNERS: require @sparkgeo/security-team review for .github/ changes
README.md: document publish_results input on scorecard action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ms280690
Copy link
Copy Markdown
Collaborator Author

PR Review Fixes Applied

Code changes — commit a87a708

Fix File Change
Inputs dead code terramate-opentofu-setup/action.yml Steps now read ${{ inputs.opentofu_version }} / ${{ inputs.terramate_version }} instead of env.*with: overrides now work
Duplicate version defaults ci.yml Removed env: block from terramate-opentofu-setup job; action defaults apply directly
check-sarif probe step scorecard/action.yml Replaced shell probe + output variable with if-no-files-found: ignore on upload-artifact and hashFiles() gate on upload-sarif
publish_results hardcoded scorecard/action.yml Exposed as input (default: true); private-repo callers can pass publish_results: false
Undocumented permissions scorecard/action.yml Required permissions added to action description
workflow_call migration CONTRIBUTING.md Migration guide added: old workflow_call pattern → equivalent composite action calls
Storage optimizer overhead ci.yml Job gated to workflow_dispatch only — no longer burns runner-minutes on every PR
Dependabot .github/dependabot.yml Created — github-actions ecosystem, weekly, 7-day cooldown
CODEOWNERS .github/CODEOWNERS Created — @sparkgeo/security-team required for .github/workflows/ and .github/actions/

Repository settings applied via API

Setting Before After
Default workflow permissions write read
Actions can approve PRs true false
Branch protection on main none 1 required review + stale review dismissal + 4 required status checks + no force-push
Allowed actions all GitHub-owned + ossf/, reviewdog/, zizmorcore/, opentofu/, terramate-io/

Required status checks on main: Actionlint, Zizmor, Dependency Review, Terramate + OpenTofu Setup

Remaining (not addressable in code or via API)

  • @sparkgeo/security-team GitHub team must exist for CODEOWNERS to enforce review requirements — create the team in org settings and add members
  • require_code_owner_reviews is currently false in branch protection; enable it once the team exists: Settings → Branches → main → Require review from code owners

🤖 Generated with Claude Code

…rkflow changes

- Add concurrency group to ci.yml: cancels in-flight runs when new push
  arrives on same ref — avoids burning extra runner minutes on stale jobs
- Add paths filter on push trigger: skip CI for doc/README-only pushes to
  main; scorecard still runs weekly via schedule so coverage unchanged
- Parallelize storage-optimizer cleanup: run all rm -rf and docker prune
  in background with & + wait instead of sequentially — saves ~20-30s on
  workflow_dispatch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents all nine external action references currently in use with
pinned SHAs, versions, purposes, and review dates. Includes:
- Security review checklist for approving new actions
- Step-by-step process for adding a new publisher to the org allowlist
- Renovate SHA update policy (pinDigests: true, no manual SHA edits)
- Current org allowlist patterns (ossf/*, reviewdog/*, zizmorcore/*,
  opentofu/*, terramate-io/* — GitHub-owned via github_owned_allowed)

Satisfies the docs/approved-actions.md acceptance criterion for #26.
Remaining open item: Renovate github-actions manager (tracked in #8).

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@ms280690 ms280690 merged commit 6372d8c into main May 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: enterprise governance, rulesets, and observability

2 participants