diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..7ff7438 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Changes to workflow and action files require security team review. +.github/workflows/ @sparkgeo/security-team +.github/actions/ @sparkgeo/security-team diff --git a/.github/actions/dependency-review/action.yml b/.github/actions/dependency-review/action.yml new file mode 100644 index 0000000..6d2ee88 --- /dev/null +++ b/.github/actions/dependency-review/action.yml @@ -0,0 +1,20 @@ +name: "Dependency Review" +description: "Blocks PRs that introduce dependencies with known vulnerabilities or denied licenses; posts a summary comment on the PR." +inputs: + fail-on-severity: + description: "Minimum vulnerability severity to fail: critical|high|moderate|low" + default: "high" + deny-licenses: + description: "Comma-separated list of SPDX license identifiers to deny" + default: "GPL-2.0,GPL-3.0,AGPL-3.0" + comment-summary-in-pr: + description: "Post a summary comment on the PR: always|on-failure|never" + default: "on-failure" +runs: + using: "composite" + steps: + - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + fail-on-severity: ${{ inputs.fail-on-severity }} + deny-licenses: ${{ inputs.deny-licenses }} + comment-summary-in-pr: ${{ inputs.comment-summary-in-pr }} diff --git a/.github/actions/github-actionlint/action.yml b/.github/actions/github-actionlint/action.yml new file mode 100644 index 0000000..bcf13ac --- /dev/null +++ b/.github/actions/github-actionlint/action.yml @@ -0,0 +1,10 @@ +name: "Actionlint" +description: "Lints GitHub Actions workflow and composite action YAML files using actionlint via reviewdog." +runs: + using: "composite" + steps: + - uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0 + with: + github_token: ${{ github.token }} + reporter: github-check + fail_level: error diff --git a/.github/actions/scorecard/action.yml b/.github/actions/scorecard/action.yml new file mode 100644 index 0000000..204f1ec --- /dev/null +++ b/.github/actions/scorecard/action.yml @@ -0,0 +1,31 @@ +name: "OpenSSF Scorecard" +description: | + Runs OpenSSF Scorecard checks, uploads SARIF to the GitHub Security tab, and publishes results to the OpenSSF database. + Required permissions on calling job: id-token: write, security-events: write, actions: read, contents: read +inputs: + publish_results: + description: "Publish results to the public OpenSSF Scorecard database and generate the Scorecard badge. Set to false for private repositories." + default: "true" +runs: + using: "composite" + steps: + - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: scorecard-results.sarif + results_format: sarif + repo_token: ${{ github.token }} + publish_results: ${{ inputs.publish_results }} + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: always() + with: + name: scorecard-results + path: scorecard-results.sarif + retention-days: 5 + if-no-files-found: ignore + + - uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + if: always() && hashFiles('scorecard-results.sarif') != '' + with: + sarif_file: scorecard-results.sarif + category: scorecard diff --git a/.github/actions/storage-optimizer/action.yml b/.github/actions/storage-optimizer/action.yml index 1c7d7aa..1626709 100644 --- a/.github/actions/storage-optimizer/action.yml +++ b/.github/actions/storage-optimizer/action.yml @@ -6,29 +6,19 @@ runs: - name: Cleanup shell: bash run: | - # Remove Java (JDKs) - sudo rm -rf /usr/lib/jvm - # Remove .NET SDKs - sudo rm -rf /usr/share/dotnet - # Remove Swift toolchain - sudo rm -rf /usr/share/swift - # Remove Haskell (GHC) - sudo rm -rf /usr/local/.ghcup - # Remove Julia - sudo rm -rf /usr/local/julia* - # Remove Android SDK - sudo rm -rf /usr/local/lib/android - # Remove Chromium - sudo rm -rf /usr/local/share/chromium - # Remove Microsoft and Google tools - sudo rm -rf /opt/microsoft /opt/google - # Remove Azure CLI - sudo rm -rf /opt/az - # Remove PowerShell - sudo rm -rf /usr/local/share/powershell - # Remove cached tools - sudo rm -rf /opt/hostedtoolcache - # Clean up Docker - docker system prune -af || true - docker builder prune -af || true + sudo rm -rf \ + /usr/lib/jvm \ + /usr/share/dotnet \ + /usr/share/swift \ + /usr/local/.ghcup \ + /usr/local/julia* \ + /usr/local/lib/android \ + /usr/local/share/chromium \ + /opt/microsoft /opt/google \ + /opt/az \ + /usr/local/share/powershell \ + /opt/hostedtoolcache & + docker system prune -af || true & + docker builder prune -af || true & + wait df -h diff --git a/.github/actions/terramate-opentofu-setup/action.yml b/.github/actions/terramate-opentofu-setup/action.yml index 8e1ce61..dc9de04 100644 --- a/.github/actions/terramate-opentofu-setup/action.yml +++ b/.github/actions/terramate-opentofu-setup/action.yml @@ -16,13 +16,13 @@ runs: - name: Setup OpenTofu uses: opentofu/setup-opentofu@847eaa4afeb791b06daa46e8eafa8b1b68d7cfb4 # v2.0.1 with: - tofu_version: ${{ env.OPENTOFU_VERSION }} + tofu_version: ${{ inputs.opentofu_version }} tofu_wrapper: false - name: Setup Terramate uses: terramate-io/terramate-action@c5a1375801a3fac44d29fc7e0ebb3757ea0db3e0 # v3.0.0 with: - version: ${{ env.TERRAMATE_VERSION }} + version: ${{ inputs.terramate_version }} - name: Check Terramate configurations are up to date shell: bash diff --git a/.github/actions/zizmor/action.yml b/.github/actions/zizmor/action.yml new file mode 100644 index 0000000..df14ed7 --- /dev/null +++ b/.github/actions/zizmor/action.yml @@ -0,0 +1,11 @@ +name: "Zizmor" +description: "Runs zizmor static security analysis against GitHub Actions workflow and action YAML files; uploads findings as SARIF to the GitHub Security tab." +runs: + using: "composite" + steps: + - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 + with: + version: "1.25.2" + advanced-security: "true" + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..bc1d356 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + commit-message: + prefix: "ci" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1ad4783 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,114 @@ +name: CI +# Dogfoods this repo's own composite actions on every push and PR. +# Serves as a live reference implementation for consuming repos. + +on: + push: + branches: [main] + paths: + - '.github/**' + pull_request: + schedule: + - cron: '0 6 * * 1' # weekly Monday 06:00 UTC — for scorecard + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # Lint workflow and action YAML files with actionlint. + actionlint: + name: Actionlint + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: ./.github/actions/github-actionlint + + # Security-scan workflow and action YAML files with zizmor. + zizmor: + name: Zizmor + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: ./.github/actions/zizmor + + # Run OpenSSF Scorecard on push to main and schedule only. + # Scorecard hard-requires the default branch; skipped on PRs and feature branches. + scorecard: + name: OpenSSF Scorecard + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + permissions: + contents: read + actions: read + security-events: write + id-token: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: ./.github/actions/scorecard + + # Review dependency changes for vulnerabilities and license issues. + # Only meaningful on pull_request — requires PR base/head context. + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: ./.github/actions/dependency-review + + # Verify Terramate and OpenTofu are installed correctly. + # Terramate stack steps are no-ops in this repo (no stacks defined). + terramate-opentofu-setup: + name: Terramate + OpenTofu Setup + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 # terramate --changed requires full git history + + - uses: ./.github/actions/terramate-opentofu-setup + + # Verify the storage optimizer composite action runs without error. + # Gated to workflow_dispatch: storage-optimizer is only useful when a job does + # heavy disk work on the same runner; running it on a throwaway CI runner wastes minutes. + storage-optimizer: + name: Storage Optimizer + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: ./.github/actions/storage-optimizer diff --git a/.github/workflows/workflow-lint.yml b/.github/workflows/workflow-lint.yml deleted file mode 100644 index 07f3294..0000000 --- a/.github/workflows/workflow-lint.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Actions Quality Gate -# Lints and security-scans all workflow and composite action YAML files using actionlint and zizmor. - -on: - pull_request: - paths: - - '.github/workflows/**' - - '.github/actions/**' - # Intentionally zero-input: callers get the same lint gate with no configuration. - # Pin this workflow at a SHA in calling workflows for supply-chain safety. - workflow_call: - -permissions: - contents: read - -jobs: - # Validates workflow and action YAML syntax and logic using actionlint. - # Posts findings as GitHub Check annotations visible in the PR checks tab. - actionlint: - name: Workflow Lint - runs-on: ubuntu-latest - permissions: - contents: read - checks: write - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0 - with: - github_token: ${{ github.token }} - reporter: github-check - fail_level: error - - # Runs zizmor static security analysis against workflow and action YAML files. - # Uploads findings as SARIF to the GitHub Security tab for triage and tracking. - zizmor: - name: Static Security Analysis - runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 - with: - version: "1.25.2" - advanced-security: "true" - env: - GITHUB_TOKEN: ${{ github.token }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1341dab..ce25150 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -This repo is the central library of reusable GitHub Actions workflows for the Sparkgeo organisation. All contributions must meet the security standards below before a PR can be merged. +This repo is the central library of reusable GitHub Actions composite actions for the Sparkgeo organisation. All contributions must meet the security standards below before a PR can be merged. ## Workflow authoring checklist @@ -57,14 +57,60 @@ gh api repos/actions/checkout/commits/v6 --jq '.sha' Once configured, Renovate (issue #8) will keep pinned SHAs current automatically via automated PRs — do not update SHAs manually unless fixing a security incident. -## Adding a new workflow +## Adding a new action 1. Create a GitHub issue (parent + context sub-issues where applicable) 2. Assign to the relevant team or individual, type: Feature, labels: `security`, `enhancement`, `documentation`, `priority: high` 3. Branch from `main`: `git checkout -b issue--` -4. Implement the workflow — satisfy all checklist items above -5. Update the workflow index table in `README.md` -6. Open a PR referencing the issue: `Closes #` +4. Create the composite action under `.github/actions//action.yml` — satisfy all checklist items above +5. Add a job for it in `.github/workflows/ci.yml` with minimum required permissions +6. Update the composite actions table in `README.md` with a usage example +7. Open a PR referencing the issue: `Closes #` + +## Migrating from the reusable workflow pattern + +Prior to issue #29, this repo exposed an `Actions Quality Gate` reusable workflow +(`workflow-lint.yml`) callable via `workflow_call`. That file has been deleted. + +If your repo references it: + +```yaml +# OLD — no longer works +jobs: + lint: + uses: sparkgeo/github-actions/.github/workflows/workflow-lint.yml@ +``` + +Replace with direct composite action calls: + +```yaml +# NEW +jobs: + actionlint: + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@ + with: + persist-credentials: false + - uses: sparkgeo/github-actions/.github/actions/github-actionlint@ + + zizmor: + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@ + with: + persist-credentials: false + - uses: sparkgeo/github-actions/.github/actions/zizmor@ +``` + +The composite actions are individually callable and require explicit job shells with +correct permissions (see README for full usage examples). ## Security pillars reference diff --git a/README.md b/README.md index 0de93da..6c2d5b8 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,107 @@ # sparkgeo/github-actions -Reusable GitHub Actions workflows and composite actions for the Sparkgeo organisation. +Reusable GitHub Actions composite actions and CI workflow for the Sparkgeo organisation. -All action references in this repo are pinned to full commit SHAs. See [CONTRIBUTING.md](CONTRIBUTING.md) for authoring standards and how to add new workflows. +All action references in this repo are pinned to full commit SHAs. See [CONTRIBUTING.md](CONTRIBUTING.md) for authoring standards and how to add new actions. -## Reusable Workflows - -Call these from any Sparkgeo repo by referencing the workflow file at a pinned SHA. +## Workflow | Workflow | File | Triggers | Purpose | |---|---|---|---| -| Actions Quality Gate | [`workflow-lint.yml`](.github/workflows/workflow-lint.yml) | `pull_request` on `.github/**`, `workflow_call` | Runs `actionlint` and `zizmor` against all workflow and composite action YAML files; posts annotations via GitHub Checks and uploads SARIF to the Security tab | +| CI | [`ci.yml`](.github/workflows/ci.yml) | `push` to `main`, `pull_request`, `schedule` (weekly), `workflow_dispatch` | Dogfoods all composite actions in this repo; serves as a live reference implementation | -### Usage +## Composite Actions -```yaml -# .github/workflows/lint.yml (in a consuming repo) -on: - pull_request: - paths: - - '.github/workflows/**' - - '.github/actions/**' +Drop these into any job with a `uses:` step. Pin to a full commit SHA for supply-chain safety. + +```bash +# Find the SHA to pin to +gh api repos/sparkgeo/github-actions/commits/main --jq '.sha' +``` +| Action | Path | Purpose | Inputs | +|---|---|---|---| +| GitHub Actionlint | [`github-actionlint`](.github/actions/github-actionlint/action.yml) | Lints workflow and action YAML files using actionlint via reviewdog; posts annotations as GitHub Checks | None | +| Zizmor | [`zizmor`](.github/actions/zizmor/action.yml) | Runs zizmor static security analysis against workflow and action YAML files; uploads findings as SARIF to the Security tab | None | +| OpenSSF Scorecard | [`scorecard`](.github/actions/scorecard/action.yml) | Runs OpenSSF Scorecard checks; uploads SARIF to the Security tab and publishes results to the OpenSSF database | `publish_results` (default: `true` — set to `false` for private repos) | +| Dependency Review | [`dependency-review`](.github/actions/dependency-review/action.yml) | Blocks PRs introducing dependencies with known vulnerabilities or denied licenses; posts a summary comment | `fail-on-severity` (default: `high`), `deny-licenses` (default: `GPL-2.0,GPL-3.0,AGPL-3.0`), `comment-summary-in-pr` (default: `on-failure`) | +| Storage Optimizer | [`storage-optimizer`](.github/actions/storage-optimizer/action.yml) | Frees disk space on GitHub-hosted runners by removing unused toolchains (JDK, .NET, Swift, Android SDK, etc.) and pruning Docker | None | +| Terramate + OpenTofu Setup | [`terramate-opentofu-setup`](.github/actions/terramate-opentofu-setup/action.yml) | Installs Terramate and OpenTofu, validates generated files are up to date, initialises changed stacks, and lists changed stacks | `opentofu_version` (default: `1.10.0`), `terramate_version` (default: `0.14.7`) | + +### GitHub Actionlint + +```yaml jobs: lint: - uses: sparkgeo/github-actions/.github/workflows/workflow-lint.yml@ + runs-on: ubuntu-latest permissions: contents: read checks: write + steps: + - uses: actions/checkout@ + with: + persist-credentials: false + - uses: sparkgeo/github-actions/.github/actions/github-actionlint@ +``` + +### Zizmor + +```yaml +jobs: + security-scan: + runs-on: ubuntu-latest + permissions: + contents: read security-events: write + steps: + - uses: actions/checkout@ + with: + persist-credentials: false + - uses: sparkgeo/github-actions/.github/actions/zizmor@ ``` -Replace `` with the full commit SHA of the version you want to pin to: +### OpenSSF Scorecard -```bash -gh api repos/sparkgeo/github-actions/commits/main --jq '.sha' +Requires `id-token: write` for OIDC signing. Only runs on public repos with `publish_results: true`. + +```yaml +jobs: + scorecard: + runs-on: ubuntu-latest + permissions: + contents: read + actions: read + security-events: write + id-token: write + steps: + - uses: actions/checkout@ + with: + persist-credentials: false + - uses: sparkgeo/github-actions/.github/actions/scorecard@ ``` -## Composite Actions +### Dependency Review -Drop these into any job with a `uses:` step. +Only meaningful on `pull_request` events — requires PR base/head context. -| Action | Path | Purpose | Inputs | -|---|---|---|---| -| Storage Optimizer | [`storage-optimizer`](.github/actions/storage-optimizer/action.yml) | Frees disk space on GitHub-hosted runners by removing unused toolchains (JDK, .NET, Swift, Android SDK, etc.) and pruning Docker | None | -| Terramate + OpenTofu Setup | [`terramate-opentofu-setup`](.github/actions/terramate-opentofu-setup/action.yml) | Installs Terramate and OpenTofu, validates that generated files are up to date, initialises changed stacks, and lists changed stacks | `opentofu_version` (default: `1.10.0`), `terramate_version` (default: `0.14.7`) | +```yaml +jobs: + dependency-review: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@ + with: + persist-credentials: false + - uses: sparkgeo/github-actions/.github/actions/dependency-review@ + with: + fail-on-severity: high # critical | high | moderate | low + deny-licenses: GPL-2.0,AGPL-3.0 # SPDX identifiers + comment-summary-in-pr: always # always | on-failure | never +``` ### Storage Optimizer @@ -74,7 +131,7 @@ This repo is part of the Sparkgeo GitHub Actions security programme. The pillars | Pillar | Issue | Summary | |---|---|---| | Workflow authoring standards | #25 | SHA pinning policy; `actionlint`/`zizmor` gate | -| Supply chain hardening | #26 | Org allowlist; dependency locking | +| Supply chain hardening | #26 | Org allowlist; dependency locking; [approved actions](docs/approved-actions.md) | | OIDC & secret federation | #27 | No static credentials; OIDC for cloud auth; environment-scoped secrets | | Runner egress control | #28 | `harden-runner` audit → block; self-hosted runner isolation policy | | Enterprise governance & observability | #29 | Org rulesets; OpenSSF Scorecard; audit log → SIEM | diff --git a/docs/approved-actions.md b/docs/approved-actions.md new file mode 100644 index 0000000..a2f35a1 --- /dev/null +++ b/docs/approved-actions.md @@ -0,0 +1,79 @@ +# Approved Third-Party GitHub Actions + +This document is the authoritative list of approved external actions for use across the Sparkgeo GitHub organisation. Any action not on this list requires a security review before use (see [Adding a new action](#adding-a-new-action)). + +## Org-level allowlist policy + +The GitHub organisation is configured to allow only: + +- Actions created by GitHub (`github_owned_allowed: true`) +- Actions on the approved list below (`allowed_actions: selected`) +- Verified Marketplace creators are **not** automatically allowed (`verified_allowed: false`) + +This is enforced at: **Org Settings → Actions → General → Allow selected actions**. + +## Approved actions + +| Action | Publisher | Current pinned version | Used in | Purpose | Review date | +|---|---|---|---|---|---| +| `actions/checkout` | GitHub (org-owned) | `de0fac2e` (v6.0.2) | all composite actions | Checkout repo contents | 2026-05-21 | +| `actions/upload-artifact` | GitHub (org-owned) | `043fb46d` (v7.0.1) | `scorecard` | Upload SARIF as retained artifact | 2026-05-21 | +| `actions/dependency-review-action` | GitHub (org-owned) | `a1d282b3` (v5.0.0) | `dependency-review` | Block PRs with vulnerable/denied-license deps | 2026-05-21 | +| `github/codeql-action/upload-sarif` | GitHub (org-owned) | `9e0d7b8d` (v4.35.5) | `scorecard`, `zizmor` | Upload SARIF to GitHub Security tab | 2026-05-21 | +| `ossf/scorecard-action` | OpenSSF | `4eaacf05` (v2.4.3) | `scorecard` | OpenSSF Scorecard supply-chain checks | 2026-05-21 | +| `reviewdog/action-actionlint` | reviewdog | `6fb7acc9` (v1.72.0) | `github-actionlint` | Actionlint via reviewdog; posts Check annotations | 2026-05-21 | +| `zizmorcore/zizmor-action` | zizmorcore | `5f14fd08` (v0.5.6) | `zizmor` | Zizmor static security analysis; uploads SARIF | 2026-05-21 | +| `opentofu/setup-opentofu` | OpenTofu | `847eaa4a` (v2.0.1) | `terramate-opentofu-setup` | Install OpenTofu CLI | 2026-05-21 | +| `terramate-io/terramate-action` | Terramate | `c5a13758` (v3.0.0) | `terramate-opentofu-setup` | Install Terramate CLI | 2026-05-21 | + +## Security review criteria + +Before approving a new action, verify all of the following: + +``` +[ ] Publisher is the canonical owner of the project (not a fork or impersonator) +[ ] Action is actively maintained — last commit within 12 months, issues responded to +[ ] Source code is publicly auditable — action.yml does not pull opaque binaries without checksum +[ ] Minimum required permissions — does not request write access it does not need +[ ] No outbound network calls to non-registry endpoints (check action source for curl/wget) +[ ] Pin to a commit SHA, not a mutable tag — confirm SHA matches the intended tag +[ ] Add SHA and version to this table; add publisher pattern to org allowlist if new publisher +``` + +## Adding a new action + +1. Open a PR adding the action to a composite action or workflow. +2. Complete the security review checklist above. +3. Add a row to the table above with the pinned SHA, version, and review date. +4. If the publisher is new, add the publisher pattern to the org allowlist via: + ```bash + # Append to selected_actions_allowed in org Actions permissions + gh api --method PUT orgs/sparkgeo/actions/permissions/selected-actions \ + --input - <<'EOF' + { + "github_owned_allowed": true, + "verified_allowed": false, + "patterns_allowed": [ + "ossf/*", "reviewdog/*", "zizmorcore/*", "opentofu/*", "terramate-io/*", + "/*" + ] + } + EOF + ``` +5. CODEOWNERS enforces that `.github/` changes require `@sparkgeo/security-team` review. + +## Renovate SHA update policy + +Renovate (issue #8) is configured with `pinDigests: true` for the `github-actions` manager. When a new version of an approved action is released, Renovate opens a PR that updates both the SHA and the inline version comment. Do not update SHAs manually — let Renovate handle it. The only exception is an emergency security patch: update immediately, then update this table's review date. + +## Org allowlist patterns (current) + +``` +ossf/* +reviewdog/* +zizmorcore/* +opentofu/* +terramate-io/* +``` + +GitHub-owned actions (`actions/*`, `github/*`) are covered by `github_owned_allowed: true` and do not need explicit patterns.