From dcfa3e6d0833f5694dcb79dd737cdae41e2ce390 Mon Sep 17 00:00:00 2001 From: Bryan Fawcett Date: Fri, 26 Jun 2026 01:09:14 +0000 Subject: [PATCH] docs(ci): document missing workflows, fix SHA drift and allow-list gaps Four issues found during governance-repo audit: 1. README was missing three shipped reusable workflows (reusable-ci-typescript-lib.yml, reusable-ci-docker.yml, reusable-ci-terraform.yml) and the GOVERNANCE.md index file. 2. ORG_SETTINGS.md Actions allow-list was missing hashicorp/setup-terraform and terraform-linters/setup-tflint, both used by reusable-ci-terraform.yml. Any repo enabling "Allow select actions" would have silently failed. 3. Three newer workflow files used different SHA pins for actions/checkout # v6 and pnpm/action-setup # v6 than the rest of the org. Also github/codeql-action/upload-sarif in reusable-ci-docker.yml had a unique SHA. Standardised all to the SHA used across the majority of org workflows. 4. reusable-ci-docs-mdx.yml passed inputs.build-command through bash -c with no validation. Added allowlist guard blocking shell metacharacters (; & | > < backtick dollar () {} backslash) before the exec. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Bryan Fawcett --- .github/workflows/reusable-ci-docker.yml | 8 ++++---- .github/workflows/reusable-ci-docs-mdx.yml | 11 +++++++++++ .github/workflows/reusable-ci-terraform.yml | 8 ++++---- .../workflows/reusable-ci-typescript-lib.yml | 18 +++++++++--------- ORG_SETTINGS.md | 2 ++ README.md | 4 ++++ 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/.github/workflows/reusable-ci-docker.yml b/.github/workflows/reusable-ci-docker.yml index c285379..9e88977 100644 --- a/.github/workflows/reusable-ci-docker.yml +++ b/.github/workflows/reusable-ci-docker.yml @@ -69,7 +69,7 @@ jobs: outputs: docker: ${{ steps.filter.outputs.docker }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 with: fetch-depth: 0 - id: filter @@ -88,7 +88,7 @@ jobs: if: needs.changes.outputs.docker == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 - name: Install hadolint env: HADOLINT_VERSION: ${{ inputs.hadolint-version }} @@ -127,7 +127,7 @@ jobs: contents: read security-events: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 - name: docker build env: @@ -181,7 +181,7 @@ jobs: "${IMAGE_NAME}:ci" - name: Upload Trivy SARIF to GitHub Security tab - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 with: sarif_file: trivy-results.sarif category: trivy-docker diff --git a/.github/workflows/reusable-ci-docs-mdx.yml b/.github/workflows/reusable-ci-docs-mdx.yml index b42eaf6..e307fc2 100644 --- a/.github/workflows/reusable-ci-docs-mdx.yml +++ b/.github/workflows/reusable-ci-docs-mdx.yml @@ -83,4 +83,15 @@ jobs: shell: bash run: | set -euo pipefail + # Guard against injection via inputs.build-command. + # Allow: alphanumeric, space, and safe script characters (._:/-). + # Block: shell metacharacters (; & | > < ` $ ( ) { } \). + if [[ "$BUILD_COMMAND" =~ [;&|><\`\$\(\)\{\}\\] ]]; then + echo "::error::build-command '$BUILD_COMMAND' must not contain shell metacharacters (; & | > < \` \$ () {} \\)." + exit 1 + fi + if [[ ! "$BUILD_COMMAND" =~ ^[a-zA-Z0-9_\ \.:/-]+$ ]]; then + echo "::error::build-command '$BUILD_COMMAND' must only contain alphanumeric characters, spaces, and [_.:/- ]." + exit 1 + fi bash -c "$BUILD_COMMAND" diff --git a/.github/workflows/reusable-ci-terraform.yml b/.github/workflows/reusable-ci-terraform.yml index 118cd24..eafd6f2 100644 --- a/.github/workflows/reusable-ci-terraform.yml +++ b/.github/workflows/reusable-ci-terraform.yml @@ -57,7 +57,7 @@ jobs: outputs: terraform: ${{ steps.filter.outputs.terraform }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 with: fetch-depth: 0 - id: filter @@ -76,7 +76,7 @@ jobs: if: needs.changes.outputs.terraform == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 with: terraform_version: ${{ inputs.terraform-version }} @@ -99,7 +99,7 @@ jobs: if: needs.changes.outputs.terraform == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 with: terraform_version: ${{ inputs.terraform-version }} @@ -128,7 +128,7 @@ jobs: if: needs.changes.outputs.terraform == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 - uses: terraform-linters/setup-tflint@b480b8fcdaa6f2c577f8e4fa799e89e756bb7c93 # v6.2.2 with: tflint_version: ${{ inputs.tflint-version }} diff --git a/.github/workflows/reusable-ci-typescript-lib.yml b/.github/workflows/reusable-ci-typescript-lib.yml index 1d1f139..384428c 100644 --- a/.github/workflows/reusable-ci-typescript-lib.yml +++ b/.github/workflows/reusable-ci-typescript-lib.yml @@ -56,7 +56,7 @@ jobs: outputs: typescript: ${{ steps.filter.outputs.typescript }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 with: fetch-depth: 0 - id: filter @@ -84,8 +84,8 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: pnpm/action-setup@71c92474e7e4f5bca283fb17ef80fba9cdb2b4b1 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version-file: ${{ inputs.node-version-file }} @@ -102,8 +102,8 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: pnpm/action-setup@71c92474e7e4f5bca283fb17ef80fba9cdb2b4b1 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version-file: ${{ inputs.node-version-file }} @@ -120,8 +120,8 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: pnpm/action-setup@71c92474e7e4f5bca283fb17ef80fba9cdb2b4b1 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version-file: ${{ inputs.node-version-file }} @@ -138,8 +138,8 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: pnpm/action-setup@71c92474e7e4f5bca283fb17ef80fba9cdb2b4b1 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v6 + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version-file: ${{ inputs.node-version-file }} diff --git a/ORG_SETTINGS.md b/ORG_SETTINGS.md index 40823b6..463da3e 100644 --- a/ORG_SETTINGS.md +++ b/ORG_SETTINGS.md @@ -95,6 +95,8 @@ At **Settings → Actions → General**: aquasecurity/trivy-action@*, sigstore/cosign-installer@*, opentofu/setup-opentofu@*, + hashicorp/setup-terraform@*, + terraform-linters/setup-tflint@*, actions/attest-build-provenance@* ``` diff --git a/README.md b/README.md index 4e5ae47..bb1cefb 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ not define its own equivalent file. | Path | Purpose | Status | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: | +| `GOVERNANCE.md` | Navigational index linking to the three governance documents (NA-01, NA-02, NA-03). Entry point for readers arriving at the repo root who want the governance overview. | ✅ | | `ORG_SETTINGS.md` | Source of truth for intended org and repo settings (branch protection, required checks, signing, secret scanning). Rulesets in `github-rulesets/`. Audit quarterly. | ✅ | | `profile/governance/NA-01_CONSTITUTION.md` | Nyuchi Africa corporate constitution — legal identity, purpose, decision rights, IP ownership, divisional structure. | ✅ | | `profile/governance/NA-02_OPEN_SOURCE.md` | Open source & contribution governance — licensing posture, sovereignty fallbacks, contribution principles. | ✅ | @@ -96,6 +97,7 @@ reusable by commit SHA rather than `@main`. | Path | Purpose | Status | | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: | | `.github/workflows/reusable-ci-typescript.yml` | TypeScript CI for non-monorepo projects (pnpm, tsc, Vitest/Jest, ESLint/Biome). Inputs: `node-version-file`, `typecheck-script`, `lint-script`, `test-script`, `build-script`, `skip-build`. | ✅ | +| `.github/workflows/reusable-ci-typescript-lib.yml` | TypeScript library / package CI with path-based change detection and `working-directory` support. For standalone packages and pnpm workspaces that are not Turborepo monorepos. Inputs: `node-version-file`, `skip-build`, `working-directory`. | ✅ | | `.github/workflows/reusable-ci-nextjs-monorepo.yml` | Turborepo + pnpm CI. Inputs: `tasks`, `node-version-file`. Secrets: `TURBO_TOKEN`, `TURBO_TEAM`. | ✅ | | `.github/workflows/reusable-ci-rust-monorepo.yml` | Cargo workspace CI. Input: `toolchain` (default `stable`). | ✅ | | `.github/workflows/reusable-ci-python-monorepo.yml` | uv workspace CI. Convention-based, no inputs. | ✅ | @@ -112,7 +114,9 @@ reusable by commit SHA rather than `@main`. | `.github/workflows/reusable-ci-solidity.yml` | Foundry CI for smart contracts. Jobs: `forge fmt`, `forge build`, `forge test`, `forge coverage` (≥60% line coverage). NA-03 §6.1, §10.4. | ✅ | | `.github/workflows/reusable-openssf-scorecard.yml` | OpenSSF Scorecard analysis. Runs supply-chain security checks and uploads SARIF results to the GitHub Security tab. Inputs: `publish-results`. | ✅ | | `.github/workflows/reusable-ci-container.yml` | Docker / OCI CI. Jobs: Trivy vulnerability scan, multi-platform build + push (linux/amd64 + arm64), cosign keyless signing. Inputs: `image-name`, `push`, `sign`. | ✅ | +| `.github/workflows/reusable-ci-docker.yml` | Docker CI gate (CI-only, no push/sign). Jobs: hadolint Dockerfile lint, `docker build`, Trivy CVE scan with SARIF upload. Lighter alternative to `reusable-ci-container.yml` when push is not needed. Inputs: `dockerfile`, `context`, `image-name`, `trivy-severity`. | ✅ | | `.github/workflows/reusable-ci-opentofu.yml` | OpenTofu / Terraform IaC CI. Jobs: `tofu fmt --check`, `tofu validate`, Trivy IaC scan, `tofu plan` (PR comment). Inputs: `working-directory`, `tofu-version`. | ✅ | +| `.github/workflows/reusable-ci-terraform.yml` | Terraform CI (CI-only, no apply or plan). Jobs: `terraform fmt --check`, `terraform validate`, `tflint`. Lighter complement to `reusable-ci-opentofu.yml` for repos using the HashiCorp Terraform CLI. Inputs: `working-directory`, `terraform-version`, `tflint-version`. | ✅ | Legend: ✅ shipped · ⏳ planned