From 361c9d393e170b623a55fce6b4197fc86cb25485 Mon Sep 17 00:00:00 2001 From: p4gs <10093271+p4gs@users.noreply.github.com> Date: Sat, 12 Sep 2026 11:53:42 -0400 Subject: [PATCH 1/2] chore(security): bootstrap SSCS Bootstrapper baseline and harden CI `sscsb init` on the repo behind grc.engineering, then fix every gap the first `sscsb verify` reported. Verify went 5 FAIL / 2 DEGRADED -> 1 FAIL / 1 DEGRADED (the remaining two are remote-GitHub and account-level, handled separately). Policy set before the first verify: - Credential detection is TruffleHog ONLY. It verifies a candidate against the issuing provider, so a finding is a live credential rather than a regex match; gitleaks-action also needs a paid licence for org-owned repos. [controls.secrets] gitleaks = false, and the gitleaks job and .gitleaks.toml are removed by hand because the init template does not render from that key. - SAST is CodeQL + OpenGrep, both enabled, plus the pre-existing Semgrep Cloud. Existing workflows hardened (actions-audit, workflow-audit-extended, harden-runner, dependency-pinning all FAIL -> PASS): - ci.yml: top-level `contents: write` -> `contents: read` with a job-scoped write grant; harden-runner added; checkout/setup-python/cache pinned to full commit SHAs; the four `pip install` steps version-pinned. checkout keeps its credential deliberately and says why -- `mkdocs gh-deploy` pushes to gh-pages over the origin remote. - semgrep.yml: moved off the mutable `semgrep/semgrep` container onto the runner host so harden-runner can attach (it cannot monitor a container job), Semgrep version pinned, actions SHA-pinned, persist-credentials: false. CodeQL was analysing `actions` only -- it had never read this site's JavaScript. The matrix now covers actions + javascript-typescript with security-extended. That requires GitHub's code-scanning *default setup* to be turned off, which is done on the repo alongside this change. Also: SECURITY.md written (security-insights.yml referenced a file that did not exist); security-insights placeholders filled; the owner's signing key recorded in .sscsb/policy/signers.toml with hardware_backed = false, which is the honest value and leaves commit-signing's advisory note standing rather than asserting a secure-element key that does not exist; protected_branches narrowed to the one branch this repo actually has. AI-Assisted: true AI-Tool: Claude Code AI-Model: claude-opus-5 AI-Role: draft Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm --- .github/PULL_REQUEST_TEMPLATE.md | 34 +++ .github/chainguard/sscsb-automation.sts.yaml | 26 ++ .github/workflows/ci.yml | 42 ++- .github/workflows/codeql.yml | 57 ++++ .github/workflows/deploy-gate.yml | 293 +++++++++++++++++++ .github/workflows/octo-sts-example.yml | 35 +++ .github/workflows/release-attest-sbom.yml | 132 +++++++++ .github/workflows/release-attest.yml | 102 +++++++ .github/workflows/release-sign.yml | 59 ++++ .github/workflows/release-slsa.yml | 113 +++++++ .github/workflows/sast-opengrep.yml | 58 ++++ .github/workflows/sbom.yml | 37 +++ .github/workflows/scorecard.yml | 43 +++ .github/workflows/secrets-scan.yml | 42 +++ .github/workflows/semgrep.yml | 37 ++- .github/workflows/vuln-scan.yml | 52 ++++ .gitignore | 6 + .sscsb/best-practices-badge.md | 36 +++ .sscsb/config.toml | 246 ++++++++++++++++ .sscsb/hooks/commit-msg | 13 + .sscsb/hooks/pre-commit | 13 + .sscsb/hooks/pre-push | 13 + .sscsb/osps-baseline.md | 30 ++ .sscsb/policy/allowed_signers | 2 + .sscsb/policy/packages.toml | 14 + .sscsb/policy/signers.toml | 51 ++++ .sscsb/policy/signing-model.toml | 19 ++ .sscsb/rules/sscsb-default.yaml | 43 +++ .trufflehog.yaml | 7 + SECURITY.md | 63 ++++ renovate.json5 | 22 ++ security-insights.yml | 40 +++ 32 files changed, 1759 insertions(+), 21 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/chainguard/sscsb-automation.sts.yaml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy-gate.yml create mode 100644 .github/workflows/octo-sts-example.yml create mode 100644 .github/workflows/release-attest-sbom.yml create mode 100644 .github/workflows/release-attest.yml create mode 100644 .github/workflows/release-sign.yml create mode 100644 .github/workflows/release-slsa.yml create mode 100644 .github/workflows/sast-opengrep.yml create mode 100644 .github/workflows/sbom.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 .github/workflows/secrets-scan.yml create mode 100644 .github/workflows/vuln-scan.yml create mode 100644 .sscsb/best-practices-badge.md create mode 100644 .sscsb/config.toml create mode 100755 .sscsb/hooks/commit-msg create mode 100755 .sscsb/hooks/pre-commit create mode 100755 .sscsb/hooks/pre-push create mode 100644 .sscsb/osps-baseline.md create mode 100644 .sscsb/policy/allowed_signers create mode 100644 .sscsb/policy/packages.toml create mode 100644 .sscsb/policy/signers.toml create mode 100644 .sscsb/policy/signing-model.toml create mode 100644 .sscsb/rules/sscsb-default.yaml create mode 100644 .trufflehog.yaml create mode 100644 SECURITY.md create mode 100644 renovate.json5 create mode 100644 security-insights.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..751f853 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,34 @@ + +## Summary + + + +## AI Provenance Declaration + + + +- [ ] AI generated or assisted with **code** in this PR +- [ ] AI generated or assisted with **tests** in this PR +- [ ] AI introduced or suggested **new dependencies** in this PR +- [ ] AI generated or assisted with **documentation** in this PR + +**AI tool(s)/model(s) used (if any):** + +**Human review performed on AI-generated parts (what/how):** + +## Dependency Changes + + + +- [ ] No new dependencies +- [ ] New dependencies validated (`sscsb deps check`) and approved + +## Merge Policy Reminder + +Merges to protected branches must be signed by an approved **human** hardware-backed +key. When AI involvement is declared above, the merge commit needs review +evidence (`Reviewed-by:` trailer). See `docs/signing.md`. diff --git a/.github/chainguard/sscsb-automation.sts.yaml b/.github/chainguard/sscsb-automation.sts.yaml new file mode 100644 index 0000000..7882c0c --- /dev/null +++ b/.github/chainguard/sscsb-automation.sts.yaml @@ -0,0 +1,26 @@ +# sscsb Octo STS trust policy — lives at .github/chainguard/sscsb-automation.sts.yaml +# in the repo that GRANTS access. Workflows in `subject_pattern` below can +# exchange their OIDC identity for a short-lived token with EXACTLY these +# permissions on THIS repo. No PAT, nothing stored, expires in ~1 hour. +# +# Docs: https://github.com/octo-sts/app +issuer: https://token.actions.githubusercontent.com + +# Which workflow identities may federate. GitHub's OIDC `sub` claim is +# ID-DECORATED — `repo:OWNER@/REPO@:ref:refs/heads/main` — +# so a pattern spelled from names alone never matches (Octo STS refuses with +# `subject "repo:OWNER@123/REPO@456:ref:…" did not match "repo:OWNER/REPO:ref:…"`). +# The `(@)?` groups accept both the bare and the decorated form; the ids +# are what survive a rename and what a re-created repository of the same name +# does NOT share, so pin them (sscsb fills them in from the GitHub API when +# `gh` is available; otherwise `[0-9]+` accepts any id until you replace it): +# gh api repos/grcengineering/grcengineering.github.io --jq .id → repo id +# gh api users/grcengineering --jq .id → owner id +# `.` in the repository name is escaped: this is a regular expression. +subject_pattern: 'repo:grcengineering(@155832502)?/grcengineering\.github\.io(@743624125)?:ref:refs/heads/main' + +permissions: + # Least privilege: grant only what the automation needs. + contents: read + # issues: write + # pull_requests: write diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5706725..e646732 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,50 @@ -name: Deploy GRC Engineering MkDocs site +name: Deploy GRC Engineering MkDocs site on: push: branches: - - master + - master - main + +# Least privilege at the top level: nothing inherits write. The single job that +# genuinely needs to write takes a job-scoped grant below. permissions: - contents: write + contents: read + jobs: deploy: runs-on: ubuntu-latest + permissions: + # `mkdocs gh-deploy` pushes the built site to the gh-pages branch. + contents: write steps: - - uses: actions/checkout@v4 + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + # persist-credentials stays at its default (true) for this job ONLY: + # `mkdocs gh-deploy` pushes to the gh-pages branch over the origin remote + # and needs the checkout credential to do it. Every other workflow in this + # repo sets persist-credentials: false. + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Configure Git Credentials run: | git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: 3.x - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@v4 + - run: echo "cache_id=$(date --utc '+%V')" >> "$GITHUB_ENV" + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: key: mkdocs-material-${{ env.cache_id }} path: .cache restore-keys: | mkdocs-material- - - run: pip install mkdocs-material - - run: pip install "mkdocs-material[imaging]" - - run: pip install mkdocs-git-revision-date-localized-plugin - - run: pip install mkdocs-git-committers-plugin-2 - - run: mkdocs gh-deploy --force \ No newline at end of file + # Version-pinned: an unpinned `pip install` takes whatever the index serves + # at build time, so a compromised or yanked upstream release lands silently. + # Renovate (renovate.json5) proposes the bumps. + - run: pip install "mkdocs-material==9.7.7" + - run: pip install "mkdocs-material[imaging]==9.7.7" + - run: pip install "mkdocs-git-revision-date-localized-plugin==1.5.4" + - run: pip install "mkdocs-git-committers-plugin-2==2.5.0" + - run: mkdocs gh-deploy --force diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..c75c73f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,57 @@ +# sscsb: CodeQL — deep interprocedural analysis on PRs and the default branch. +# +# Languages for THIS repo. The sscsb template ships `actions` only and tells you +# to add your own, because CodeQL that does not read your source code is not +# analysing your source code: +# actions — the workflows in this directory +# javascript-typescript — docs/javascripts/tablesort.js and the nthpartyfinder +# design-system scripts (ds/node-graph.js, +# ds/site-header.js), plus the inline