From 7d4dbd670fd6cfdce3775007649d0db5326bebf2 Mon Sep 17 00:00:00 2001 From: p4gs <10093271+p4gs@users.noreply.github.com> Date: Sat, 12 Sep 2026 10:34:14 -0400 Subject: [PATCH 1/5] fix(security): TruffleHog-only secret scanning; close the 5 failing sscsb controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sscsb verify` on this tap reported 5 FAIL / 1 DEGRADED and exited 1. It now reports 0 FAIL / 1 DEGRADED and exits 0, with the one remaining item documented in the file that owns it rather than left to be rediscovered. Secret scanning is TruffleHog only, deliberately: - .gitleaks.toml deleted, the `gitleaks` CI job removed from secrets-scan.yml, and `gitleaks = false` set in .sscsb/config.toml. Both engines are pattern and entropy matchers over the same diff, but TruffleHog also VERIFIES a candidate against the issuing provider, so it separates "this looks like a key" from "this key is live right now". The second engine bought duplicate findings and a second allowlist file to keep correct — another place to open a blind spot — without adding a detection class TruffleHog lacks. `--results=verified,unknown` keeps unverifiable candidates in scope, so nothing narrowed. - Note recorded beside the toggle: sscsb 0.4.0 rewrites the sample .gitleaks.toml on any `sscsb init` because it keys off the `secrets` control, not the `gitleaks` flag. If it reappears, delete it. The approved-signers policy named a key that was retired six weeks ago: - .sscsb/policy/signers.toml listed the Apple Secure Enclave / Secretive ECDSA key, retired 2026-08-02. The maintainer has signed with the 1Password-vaulted ed25519 key (SHA256:prXatGO56nl8Or4JdDSzIIcj8hZE1jBxnFaXZOnAPDQ) ever since, so this policy could not validate a single commit made in that window — `sscsb hook pre-push` rejects with "signature cannot be validated against approved signers (status E)", which was reproduced directly against this repo. An allowed-signers list that omits the key doing the signing is not a strict policy, it is a broken one. Replaced; allowed_signers regenerated. - hardware_backed is now `false`, which is the honest value: the key is device-auth-gated in the 1Password vault, not resident on a security key. `require_hardware_backed = true` stays set as the standing target; the remediation is a YubiKey -sk key, not a truer-sounding flag. Closed as N/A, with the reason recorded next to the toggle: - github-attestations and sbom-attestation both install an `on: release published` workflow that builds, attests and verifies an artifact. This repo is a tap: no build, no tags, no releases (both `git tag` and `gh release list` are empty). Enabled, they would attest a `git archive` of the tap's own Ruby files. The provenance question a tap must answer is whether the bytes a formula points at are the ones upstream built, and formula-integrity.yml already answers it with cosign and slsa-verifier pinned to the expected source repo and tag. - deploy-gate.yml, which `sscsb init` writes for provenance-verify, is not committed for the same reason: its workflow_call entry is only reachable from a release.yml this repo does not have, and its workflow_dispatch entry needs a published release tag. Closed for real: - security-insights.yml added and filled in (no REPLACE-ME placeholders left). - .sscsb/best-practices-badge.md added, then corrected — the generated worksheet claimed controls this repo does not run (Gitleaks, Trivy/OSV, SBOM, Renovate, release signing) and cited a ci.yml that does not exist. Rewritten against actual evidence, with the genuinely-not-applicable rows marked as such and a "Known gaps" section for the three that are open. - .sscsb/osps-baseline.md added, plus a note that its table is sscsb's generic control map and not a claim about what is enabled here. - provenance-verify now pins builder_id, read out of nthpartyfinder v1.8.3's multiple.intoto.jsonl rather than assumed. Without it the gate accepts provenance from any builder slsa-verifier trusts for the source repo. - Three config keys that sscsb 0.4.0 silently ignored on every single run (harden-runner.egress_policy, signing-model.agent, signing-model.human_backend) removed; `sscsb status` is now warning-free. Left DEGRADED on purpose (recorded in .sscsb/policy/signing-model.toml): - signing-model. Three lanes (github-web vigilant mode + phishing-resistant MFA, Codespaces GPG verification) are account settings with no read API — only the account holder can attest them, and writing an attestation for something that has not happened turns a control into a lie. The other two lanes want a distinct AI signing identity, which this maintainer's standing policy explicitly retired on 2026-08-02 in favour of one signer everywhere. Verified: `sscsb verify` 18 PASS / 0 FAIL / 1 DEGRADED (was 15/5/1), exit 0; `sscsb sast` 0 findings; `sscsb status` clean; no gitleaks execution path, config file, or CI job remains anywhere in the tree. AI-Assisted: true AI-Tool: Claude Code AI-Model: Opus 5 AI-Role: draft Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm --- .github/workflows/secrets-scan.yml | 45 +++++++------------ .gitignore | 2 + .gitleaks.toml | 17 -------- .sscsb/best-practices-badge.md | 52 ++++++++++++++++++++++ .sscsb/config.toml | 70 +++++++++++++++++++++++++++--- .sscsb/osps-baseline.md | 48 ++++++++++++++++++++ .sscsb/policy/allowed_signers | 2 +- .sscsb/policy/signers.toml | 36 ++++++++++++--- .sscsb/policy/signing-model.toml | 28 ++++++++++++ README.md | 2 +- security-insights.yml | 37 ++++++++++++++++ 11 files changed, 278 insertions(+), 61 deletions(-) create mode 100644 .gitignore delete mode 100644 .gitleaks.toml create mode 100644 .sscsb/best-practices-badge.md create mode 100644 .sscsb/osps-baseline.md create mode 100644 security-insights.yml diff --git a/.github/workflows/secrets-scan.yml b/.github/workflows/secrets-scan.yml index e759231..f6987f9 100644 --- a/.github/workflows/secrets-scan.yml +++ b/.github/workflows/secrets-scan.yml @@ -1,5 +1,16 @@ -# sscsb: CI secret-scanning redundancy (TruffleHog + Gitleaks). -# Local hooks block first; this catches anything that slips past (e.g. web UI commits). +# sscsb: CI secret-scanning backstop (TruffleHog). +# Local hooks block first; this catches anything that slips past (e.g. web UI commits), +# and unlike the hooks it re-reads the FULL history on every run. +# +# TruffleHog is the only secret scanner here, on purpose. A Gitleaks job used to run +# beside it over the same diff; both are pattern/entropy engines, but TruffleHog also +# VERIFIES a candidate credential against the issuing provider, so it distinguishes +# "this looks like a key" from "this key is live right now". A second, non-verifying +# engine bought duplicate findings and a second allowlist file to keep correct — +# another place to open a blind spot — without adding a detection class TruffleHog +# lacks. `--results=verified,unknown` keeps the unverifiable candidates in scope, so +# dropping Gitleaks does not narrow what gets reported. +# # All actions pinned to full commit SHAs (sscsb actions-audit enforces this). name: Secret Scan on: @@ -23,36 +34,10 @@ jobs: with: fetch-depth: 0 persist-credentials: false - # Upstream README suggests @main; sscsb pins the release SHA instead (v3.95.9). + # Upstream README suggests @main; sscsb pins the release SHA instead (see the + # version comment on the `uses:` line, kept current by Dependabot). - name: TruffleHog scan uses: trufflesecurity/trufflehog@6f3c981e7b77f235fd2702dd74af25fc4b72bf11 # v3.96.0 with: extra_args: --results=verified,unknown - gitleaks: - runs-on: ubuntu-latest - steps: - - name: Harden runner - uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 - with: - egress-policy: audit - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - # The gitleaks *CLI* (MIT) is used directly rather than - # gitleaks/gitleaks-action, which requires a paid GITLEAKS_LICENSE for - # organization repositories. This runs the same engine the local sscsb - # pre-commit/pre-push hooks use, against the tap's own .gitleaks.toml. - - name: Install gitleaks - env: - GITLEAKS_VERSION: "8.30.1" - run: | - curl -fsSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ - -o /tmp/gitleaks.tgz - tar -xzf /tmp/gitleaks.tgz -C /tmp gitleaks - sudo install -m 0755 /tmp/gitleaks /usr/local/bin/gitleaks - gitleaks version - - name: Gitleaks scan (full history) - run: gitleaks git --config .gitleaks.toml --redact --no-banner . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20197c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# sscsb: generated output (SBOMs, receipts, VEX), not policy +.sscsb/out/ diff --git a/.gitleaks.toml b/.gitleaks.toml deleted file mode 100644 index c73ed1b..0000000 --- a/.gitleaks.toml +++ /dev/null @@ -1,17 +0,0 @@ -# sscsb sample Gitleaks configuration (.gitleaks.toml). -# Extends the built-in default ruleset; add project-specific allowlists here -# rather than weakening rules globally. - -title = "sscsb gitleaks config" - -[extend] -useDefault = true - -# Example: allowlist generated fixtures that contain entropy but no secrets. -# (Keep this list SHORT and reviewed — every entry is a scanning blind spot.) -[[allowlists]] -description = "sscsb generated non-secret artifacts" -paths = [ - '''\.sscsb/out/.*\.cdx\.json''', - '''\.sscsb/out/.*\.spdx\.json''', -] diff --git a/.sscsb/best-practices-badge.md b/.sscsb/best-practices-badge.md new file mode 100644 index 0000000..6a5b37d --- /dev/null +++ b/.sscsb/best-practices-badge.md @@ -0,0 +1,52 @@ +# OpenSSF Best Practices Badge — worksheet for grcengineering/homebrew-grcengineering + +`sscsb` can't register the badge for you (it needs your GitHub OAuth), but it +CAN make earning the **passing** badge a copy-paste exercise: most passing +criteria are already satisfied by the controls sscsb installs. Register, then +paste the evidence below. + +**Register:** https://www.bestpractices.dev/ → "Get Your Project Badge" → +sign in with GitHub → add `https://github.com/grcengineering/homebrew-grcengineering`. + +**Scorecard link:** OpenSSF Scorecard's `CII-Best-Practices` check reads the +badge API for this repo, so earning the badge also lifts your Scorecard score. + +## Passing criteria → evidence in THIS repository + +sscsb generates this table from its stock control list. It has been corrected to +what this repository actually enforces: a tap is a distribution channel with no +build, no releases and no package dependencies of its own, so several stock rows +are genuinely not applicable here rather than pending. A worksheet that claims a +control the repo does not run is worse than no worksheet — it is the kind of +unearned assurance this whole tap exists to prevent. + +| Criterion (passing level) | Evidence here | Status | +|---|---|---| +| Project has an OSS license | `LICENSE` (MIT) | ☑ | +| Public version-controlled source repo | GitHub `grcengineering/homebrew-grcengineering` | ☑ | +| Automated test suite runs in CI | `.github/workflows/tests.yml` (`brew test-bot`: `brew style`, `brew audit`, formula install/test) | ☑ | +| Static analysis (SAST) applied | `sast` (OpenGrep, `.github/workflows/sast-opengrep.yml`) + `codeql` (`actions` and `ruby` extractors) | ☑ | +| Vulnerability scanning of dependencies | n/a — the only dependency surface this repo owns is its GitHub Actions, which are SHA-pinned (`actions-audit`) and updated by Dependabot. There are no lockfiles or package manifests to scan; `vuln-scan` is off for that reason. | ☑ (n/a) | +| No unpatched medium+/high vulns | n/a, same reason as the row above | ☑ (n/a) | +| Secrets not committed; scanned | `secrets` — TruffleHog at pre-commit, pre-push and in CI over full history (`.github/workflows/secrets-scan.yml`) | ☑ | +| Vulnerability reporting process documented | `SECURITY.md` + private GitHub Security Advisories | ☑ | +| Cryptographic signing of releases | n/a — this tap publishes no releases. What it does instead is the harder half: `formula-integrity.yml` verifies UPSTREAM's Sigstore/SLSA signatures for every artifact a formula points at, pinned to the expected source repo and tag, on every PR, every push to `main`, and weekly. | ☑ (n/a) | +| Delivery over TLS / pinned deps | all actions SHA-pinned (`actions-audit`, `workflow-audit-extended`); all formula URLs are HTTPS with pinned `sha256` | ☑ | +| Change control (PRs, protected branch) | `branch-protection` verifies `main`: required PRs, signed commits, required status checks, no force-push, no deletion | ☑ | +| SBOM published | n/a — no build and no released artifact to describe. Upstream `nthpartyfinder` publishes the SBOM for the bytes this tap installs. | ☑ (n/a) | + +### Known gaps (open, not waved through) + +- **Approving reviews are not required on `main`.** Scorecard scores `Code-Review` + 0 for this repo. The cause is structural, not neglect: a solo maintainer cannot + self-approve, so turning the requirement on would block all work. Remediation is + a second maintainer, then `sscsb harden branch-protection --require-reviews`. +- **The human signing key is device-auth-gated, not hardware-resident.** See + `.sscsb/policy/signers.toml`; `require_hardware_backed = true` stays set in + `.sscsb/config.toml` as the standing target, and the remediation is a YubiKey + `-sk` key. +- **The badge itself is not registered.** Registration needs the maintainer's + GitHub OAuth and cannot be automated from here. + +_Generated by sscs-bootstrapper. Re-run `sscsb init` after deleting this file to +regenerate._ diff --git a/.sscsb/config.toml b/.sscsb/config.toml index b02b4df..ca13cf8 100644 --- a/.sscsb/config.toml +++ b/.sscsb/config.toml @@ -15,12 +15,24 @@ github_repo = "grcengineering/homebrew-grcengineering" # ── Phase 1 — Local source integrity ── -# Secret scanning hooks: TruffleHog + Gitleaks block secrets at pre-commit and pre-push +# Secret scanning hooks: TruffleHog blocks secrets at pre-commit and pre-push. +# +# TruffleHog ONLY, deliberately. Gitleaks used to run alongside it here and has been +# removed. Both are pattern/entropy engines over the same diff, but TruffleHog also +# VERIFIES a candidate credential against the issuing provider, so it can tell +# "this looks like a key" apart from "this key is live right now". A second, +# non-verifying engine adds duplicate findings and a second allowlist file to +# maintain — a second place to create a blind spot — without adding a detection +# class TruffleHog lacks. One scanner, the one whose hits can be proven. [controls.secrets] enabled = true trufflehog = true -gitleaks = true +gitleaks = false pre_push_range_scan = true +# NOTE for whoever runs `sscsb init` next: sscsb 0.4.0 writes the sample +# `.gitleaks.toml` whenever the `secrets` control is enabled, without consulting the +# `gitleaks` toggle above. If that file reappears, delete it — it is a scanner config +# for an engine this repo does not run, and leaving it implies a control that is off. # CommitSigningGuard: Hardware-backed, human-only signing enforced on protected branches at pre-push [controls.commit-signing] @@ -36,10 +48,12 @@ allowed_backends = ["github-app", "tpm", "fido2", "kms", "piv", "software"] max_key_age_days = 90 # Five-environment signing model: Machine-wide signing posture: human enclave lane, distinct agent identity, cloud/web/Codespaces guidance +# Lane identities are not config keys in sscsb 0.4.0 — the human key lives in +# .sscsb/policy/signers.toml and the un-probeable lanes (cloud / web / Codespaces) +# are dated attestations in .sscsb/policy/signing-model.toml. Setting `agent` / +# `human_backend` here was silently ignored on every run. [controls.signing-model] enabled = true -agent = "claude-code" -human_backend = "secretive" # Branch protection verification: Verify GitHub protected-branch rules (PRs, no force-push, signatures, checks) [controls.branch-protection] @@ -110,18 +124,62 @@ enabled = false [controls.slsa-provenance] enabled = false -# Provenance verification gates: slsa-verifier + cosign verification required before promote/deploy/publish +# GitHub artifact attestations / GitHub SBOM attestation: DISABLED, and the reason is +# structural rather than a deferral. +# +# Both controls install an `on: release published` workflow that builds something, +# attests the bytes, and verifies the attestation. This repository is a Homebrew TAP: +# it has no build, no tags and no releases (`gh release list` is empty, `git tag` is +# empty), and it never will — it ships Ruby metadata describing artifacts that are +# built, signed and attested in grcengineering/nthpartyfinder. Enabling them here +# would add two workflows that either never fire, or, if a tag were ever cut, attest +# a `git archive` tarball of the tap's own formula files. Attesting a stub is not +# provenance; it is a green check over a claim nobody makes. +# +# The provenance question a tap actually has to answer — "are the bytes this formula +# points at the ones upstream built?" — is answered by formula-integrity.yml, which +# downloads every (url, sha256) pair and verifies it against upstream's keyless +# signing identity with cosign and slsa-verifier, pinned to the expected source repo +# and tag, on every PR, every push to main, and weekly against already-merged +# formulae. The `provenance-verify` control below is the sscsb control that gates it. +# +# Re-enable both the day this repo builds and publishes an artifact of its own. +[controls.github-attestations] +enabled = false + +[controls.sbom-attestation] +enabled = false + +# Provenance verification gates: slsa-verifier + cosign verification required before +# promote/deploy/publish. Enabled — but note that `sscsb init` writes a +# `.github/workflows/deploy-gate.yml` for this control, and that workflow is +# unreachable in a tap: its `workflow_call` entry is only invoked by a `release.yml` +# this repo does not have, and its `workflow_dispatch` entry needs a published +# release tag (this repo has none). It is deliberately not committed; delete it if a +# future `sscsb init` puts it back. The verification that matters here runs in +# formula-integrity.yml against UPSTREAM's artifacts, which is what a distribution +# channel actually has to verify. [controls.provenance-verify] enabled = true +# Without this, `sscsb provenance verify` accepts provenance from ANY builder +# slsa-verifier trusts for the source repo — "something built this" rather than +# "the builder we expect built this". Pinned to the builder that actually produced +# the provenance the tap's formulae point at: read out of +# grcengineering/nthpartyfinder v1.8.3's multiple.intoto.jsonl +# (predicate https://slsa.dev/provenance/v0.2), not assumed. +builder_id = "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v2.1.0" # Octo STS federation: Short-lived repo-scoped GitHub credentials replacing long-lived PATs [controls.octo-sts] enabled = false # Harden-Runner: StepSecurity Harden-Runner egress/tamper monitoring in every workflow +# The egress policy itself is a per-workflow input (`with: egress-policy:`), not a +# config key sscsb 0.4.0 reads; setting `egress_policy` here was silently ignored. +# Every workflow in this repo runs harden-runner in `audit` mode — the audit output +# is the input to a future `block` allowlist. [controls.harden-runner] enabled = true -egress_policy = "audit" # Witness (optional): Richer in-toto attestation capture and policy around build steps [controls.witness] diff --git a/.sscsb/osps-baseline.md b/.sscsb/osps-baseline.md new file mode 100644 index 0000000..da1adf8 --- /dev/null +++ b/.sscsb/osps-baseline.md @@ -0,0 +1,48 @@ +# OSPS Baseline assessment — grcengineering/homebrew-grcengineering + +The [OpenSSF Project Security Baseline](https://baseline.openssf.org/) is a set of +minimum, maturity-tiered security requirements. sscsb maps the controls it installs +to the Baseline's requirement families so you can see, and attest to, which the repo +already satisfies. Run `sscsb report` for the live, machine-readable version (the +OSPS column) and `sscsb report --format json` for evidence. + +## Coverage by requirement family + +| OSPS family | Intent | Covered by sscsb controls | +|---|---|---| +| **OSPS-AC** Access Control | protect the primary branch; least-privilege access | `branch-protection`, `commit-signing`, `gittuf` | +| **OSPS-BR** Build & Release | integrity/provenance of source and released assets | `sigstore-signing`, `slsa-provenance`, `github-attestations`, `sbom-attestation`, `provenance-verify`, `model-signing`, `actions-audit` | +| **OSPS-QA** Quality | dependency transparency, review, well-documented code | `sbom`, `renovate`, `package-trust`, `sast` | +| **OSPS-SA** Security Assessment | architecture/security documentation | `security-insights`, `scorecard` | +| **OSPS-VM** Vulnerability Management | reporting process + finding/fixing defects | `vuln-scan`, `codeql`, `fuzzing`, `openvex`, `security-insights` | +| **OSPS-DO** Documentation | user & security docs | `SECURITY.md` (installed), `security-insights` | +| **OSPS-LE** Legal | license present, DCO/CLA | `LICENSE` (confirm) | +| **OSPS-GV** Governance | roles, decision-making | maintainer-authored (`security-insights` roles) | + +## What this table is, and is not, for this repository + +The table above is sscsb's generic control-to-family mapping: it says which sscsb +control WOULD cover each family, not which are enabled here. Several named controls +are deliberately off in this tap because it has no build, no releases and no package +dependencies of its own — `sbom`, `vuln-scan`, `renovate`, `package-trust`, +`sigstore-signing`, `slsa-provenance`, `github-attestations`, `sbom-attestation`, +`fuzzing`, `openvex`, `model-signing`, `gittuf`. `sscsb status` is the live answer; +`.sscsb/config.toml` carries the reason beside each toggle. + +What covers **OSPS-BR** here is not an attestation this repo produces but one it +CHECKS: `formula-integrity.yml` verifies upstream's Sigstore bundle or SLSA +provenance for every artifact a formula points at, pinned to the expected source +repository and tag, on every pull request, every push to `main`, and weekly against +already-merged formulae. For a distribution channel that is the load-bearing +Build & Release control; signing the tap's own Ruby files would attest nothing a +user consumes. + +## Maturity note + +sscsb's controls target the Baseline's **Level 1–2** technical requirements (the +machine-enforceable ones). Level-3 and the Documentation/Legal/Governance families +include human attestations sscsb cannot auto-satisfy — fill those in your +`security-insights.yml` and project docs. + +_Generated by sscs-bootstrapper. Mappings use real OSPS family IDs from +github.com/ossf/security-baseline. Delete + re-run `sscsb init` to regenerate._ diff --git a/.sscsb/policy/allowed_signers b/.sscsb/policy/allowed_signers index 45f5156..eaba490 100644 --- a/.sscsb/policy/allowed_signers +++ b/.sscsb/policy/allowed_signers @@ -1,2 +1,2 @@ # Generated by sscsb from .sscsb/policy/signers.toml — do not edit. -10093271+p4gs@users.noreply.github.com namespaces="git" ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ6+H5pdok/8I/b+254AhexNJmkzfwCI7wnBqi6LLwkKIHLxcjy9ADFYuhK9fvKKshsu9Ld2e0YRs8BbvF4zUHM= Git-Signing@secretive.mbp.local +10093271+p4gs@users.noreply.github.com namespaces="git,sscsb-scan-record" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBxEAReKc/G/sUxY/UbXprOccUKaxhd+sr/7Gbb40u9w github-signing@1password.p4gs diff --git a/.sscsb/policy/signers.toml b/.sscsb/policy/signers.toml index b61ff07..05d0acf 100644 --- a/.sscsb/policy/signers.toml +++ b/.sscsb/policy/signers.toml @@ -10,7 +10,9 @@ # which is exactly the diff an attacker wants merged and exactly the diff that # looks most boring to a reviewer. Requiring a hardware-backed human signature # on every such commit is the control that makes an automated path, or a stolen -# credential, insufficient on its own. +# credential, insufficient on its own. The current key is device-auth-gated rather +# than hardware-resident (see the signer entry below) — that is a recorded gap with +# a named remediation, not a silent one. # # AI agents draft changes here; they do not sign them. The `agent-signing` # control is off, so no `class = "ai"` key is emitted into allowed_signers at @@ -20,8 +22,30 @@ [[signer]] principal = "10093271+p4gs@users.noreply.github.com" class = "human" -# Apple Secure Enclave via Secretive. The private key is non-exportable and -# every signature requires a physical Touch ID tap, so a signed commit here -# carries evidence that a specific human was present at signing time. -hardware_backed = true -ssh_public_key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ6+H5pdok/8I/b+254AhexNJmkzfwCI7wnBqi6LLwkKIHLxcjy9ADFYuhK9fvKKshsu9Ld2e0YRs8BbvF4zUHM= Git-Signing@secretive.mbp.local" +# Active human signer since 2026-08-02: the 1Password-vaulted ed25519 key +# ("GitHub Signing Key (p4gs)"), signed through 1Password's op-ssh-sign and +# registered on GitHub as a signing key. +# +# This file previously listed an Apple Secure Enclave key (ecdsa-sha2-nistp256, +# Git-Signing@secretive.mbp.local) that was RETIRED on 2026-08-02. That left this +# policy unable to validate a single commit signed since — `sscsb hook pre-push` +# rejects with "signature cannot be validated against approved signers (status E)" +# for exactly that reason. An approved-signers list that does not contain the key +# doing the signing is not a strict policy, it is a broken one. +# +# The retired key is deliberately NOT carried here: this file governs which keys +# may LAND a commit, not which historical signatures still verify. Verifying +# pre-August commits is the job of the machine-global ~/.ssh/allowed_signers, +# which keeps the retired key, annotated as retired, for precisely that purpose. +# +# hardware_backed = false is the honest value, and honest is the point. The private +# key is stored encrypted in the 1Password vault and every signature requires a +# device unlock (Touch ID / system auth), which is real presence evidence — but the +# key is not resident on a security key, it is not an `ssh-ed25519-sk` credential, +# and it is exportable from the vault. Declaring it hardware-backed would attest a +# property it does not have. `require_hardware_backed = true` stays set in +# config.toml as the standing target: the remediation is a YubiKey `-sk` key, not a +# truer-sounding flag in this file. +backend = "software" +hardware_backed = false +ssh_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBxEAReKc/G/sUxY/UbXprOccUKaxhd+sr/7Gbb40u9w github-signing@1password.p4gs" diff --git a/.sscsb/policy/signing-model.toml b/.sscsb/policy/signing-model.toml index 45b1010..2ff364a 100644 --- a/.sscsb/policy/signing-model.toml +++ b/.sscsb/policy/signing-model.toml @@ -17,3 +17,31 @@ # [cloud-claude] # github_app_installed = "YYYY-MM-DD" # authorized the Claude GitHub App (not a personal token) # signing_mode = "app-signed" # app-signed | unsigned-drafts + +# ── Why every lane below is still unattested (recorded 2026-09-12) ── +# +# `sscsb verify signing-model` reports DEGRADED for this repository, and that is a +# deliberate, documented state rather than an oversight. An attestation here is a +# dated assertion of fact; writing one that has not happened would turn a control +# into a lie, which is the one failure mode this file exists to prevent. +# +# github-web (vigilant_mode, phishing_resistant_mfa) and codespaces +# (gpg_verification) are settings on the maintainer's GitHub ACCOUNT with no read +# API — sscsb cannot probe them and neither can this repository. Only the account +# holder can run `sscsb signing setup --confirm`, and only after actually +# performing the steps. Nothing in CI or in this repo can close these. +# +# agent-claude-code and cloud-claude both want a DISTINCT AI signing identity +# (a GIT_CONFIG_* env block for the local agent; a repo-level .claude/settings.json +# for cloud containers) so agent commits do not inherit the human's identity. That +# is good advice in general and it is NOT what this maintainer's standing policy +# says: as of 2026-08-02 there is exactly one signer across every repository and +# every machine — the human's 1Password-vaulted key in .sscsb/policy/signers.toml — +# and the separate agent identity was explicitly retired. Converging these two lanes +# would re-create the identity sscsb is asking for by contradicting the policy that +# removed it, so they stay open by decision. +# +# The control that actually keeps an agent off `main` here is not this file: it is +# `agent-signing = false` in config.toml, which emits no class="ai" key into +# allowed_signers at all, plus branch protection requiring signed commits and pull +# requests. An AI signature cannot be made verification-valid on this repo. diff --git a/README.md b/README.md index 3e9e511..d2e5237 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ stated plainly so the guarantee isn't misread. | **Weekly re-verification** | Merged formulae are re-checked on a schedule, so an asset swapped or re-pointed *after* review is caught. This is detection (≤1 week exposure), not prevention. | | **Human-only signing** | Protected-branch commits require a hardware-backed human signature. No AI key is registered, so an AI signature cannot be valid here. | | **Formula correctness** | `brew test-bot` runs `brew style` and `brew audit`, and builds/tests changed formulae on PRs across macOS x86_64, macOS arm64, and Linux — an authoritative per-platform fetch that resolves the artifact the way Homebrew actually will. | -| **Everything else** | SHA-pinned actions with least-privilege `permissions:`, Harden-Runner on every job, TruffleHog + Gitleaks, OpenGrep SAST (extended to Ruby), CodeQL, OpenSSF Scorecard, Dependabot for the actions the workflows call. | +| **Everything else** | SHA-pinned actions with least-privilege `permissions:`, Harden-Runner on every job, TruffleHog secret scanning (verified-credential detection; the single scanner, not one of two), OpenGrep SAST (extended to Ruby), CodeQL, OpenSSF Scorecard, Dependabot for the actions the workflows call. | **What it does not do:** none of the above proves a formula's install code is *safe* — provenance and digests are about the tarball's bytes, not what the diff --git a/security-insights.yml b/security-insights.yml new file mode 100644 index 0000000..ce181c9 --- /dev/null +++ b/security-insights.yml @@ -0,0 +1,37 @@ +# OpenSSF Security Insights — machine-readable security posture for this project. +# Spec: https://github.com/ossf/security-insights-spec (schema v2). +# sscsb generated this valid starter. Any placeholder values sscsb leaves behind +# need real information filled in — then validate with si-tooling (`si validate`). +# `sscsb verify security-insights` reports Info until every placeholder is gone. +# +# Only the `header` + `project` blocks are emitted (both validate on their own). +# Add the optional `repository:` block (status, core-team, license, security +# posture) when you're ready to attest repository-level details. +header: + schema-version: "2.0.0" + last-updated: "2026-09-12" + last-reviewed: "2026-09-12" + url: "https://github.com/grcengineering/homebrew-grcengineering/blob/main/security-insights.yml" + comment: | + Generated by sscs-bootstrapper. Supply-chain controls (secret scanning, + signing, SBOM, vulnerability scanning, SLSA provenance) are enforced via the + .sscsb config and .github/workflows installed by `sscsb init`. +project: + name: "grcengineering/homebrew-grcengineering" + homepage: "https://github.com/grcengineering/homebrew-grcengineering" + administrators: + - name: "Justin Pagano" + affiliation: "GRC Engineering" + primary: true + repositories: + - name: "grcengineering/homebrew-grcengineering" + comment: "Primary repository this Security Insights file is published in." + url: "https://github.com/grcengineering/homebrew-grcengineering" + vulnerability-reporting: + reports-accepted: true + bug-bounty-available: false + # sscsb installs SECURITY.md; adjust if you relocate the policy. + security-policy: "https://github.com/grcengineering/homebrew-grcengineering/blob/main/SECURITY.md" + comment: "Report privately via GitHub Security Advisories on this repository." + documentation: + detailed-guide: "https://github.com/grcengineering/homebrew-grcengineering/blob/main/SECURITY.md" From c26d09059ec0ecadf83d463663f42d354778b15d Mon Sep 17 00:00:00 2001 From: p4gs <10093271+p4gs@users.noreply.github.com> Date: Sat, 12 Sep 2026 10:56:49 -0400 Subject: [PATCH 2/5] fix(cask): drop deprecated `verified:` and fix depends_on stanza order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `brew test-bot` is failing on `main` — all three legs — and has been since Homebrew moved underneath the tap. Reproduced on unmodified `main` by dispatching tests.yml against it (run 34700332281): macos-26, macos-15-intel and ubuntu-latest all fail, with no repository change involved. Nothing here is a regression from a pull request; it is time-triggered drift, and a tap whose CI is red is a tap where the next formula bump cannot be cleanly judged. Two causes, both in Casks/nthpartyfinder.rb: 1. `url ... verified: "github.com/grcengineering/nthpartyfinder/"`. Current Homebrew lists `:verified` in `Cask::URL::DEPRECATED_URL_SPECS` and its own comment says it is "accepted as a no-op for compatibility with existing casks" — it is read into an attribute and never used. Newer Homebrew, which is what CI runs, raises the deprecation, and `brew readall --os=all` reports the cask as "Invalid cask (Linux on ARM64)" / "(Linux on Intel x86_64)" as a consequence. Removing it is behaviour-neutral by construction: the parameter no longer influences download, verification or anything else. 2. `Cask/StanzaOrder: depends_on stanza out of order`. Homebrew's cop wants `cask` before `formula` before `macos`. Reproduced locally with `brew style Casks/nthpartyfinder.rb` (2 offenses) and the reorder is what `brew style --fix` produces; the comment explaining the `macos:` line stays attached to it. `brew style` is now clean on both Casks/nthpartyfinder.rb and Formula/nthpartyfinder.rb. The `url` and both `sha256` values are untouched — this changes no byte that `brew install` fetches, which is the property that matters in a tap. One thing this does NOT fix: the cask's own header records that this file's source of truth lives in grcengineering/nthpartyfinder and is copied here by scripts/sync-homebrew-formula.sh. Unless the same two edits are made to the copy in the product repo, the next release sync will reintroduce both and CI will go red again at exactly the moment a release is being cut. AI-Assisted: true AI-Tool: Claude Code AI-Model: Opus 5 AI-Role: draft Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm --- Casks/nthpartyfinder.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Casks/nthpartyfinder.rb b/Casks/nthpartyfinder.rb index 4ad7a62..5322ccd 100644 --- a/Casks/nthpartyfinder.rb +++ b/Casks/nthpartyfinder.rb @@ -23,20 +23,20 @@ sha256 arm: "ee50de230c607c6cba9106059d30ac16948348e6511bfcfac3562b005fa13a9b", intel: "a3377ae60110b524b188ba1354d9e0f9ba35c6dd5e7c45e4135534bd6db47c7a" - url "https://github.com/grcengineering/nthpartyfinder/releases/download/v#{version}/nthpartyfinder-#{arch}-apple-darwin.tgz", - verified: "github.com/grcengineering/nthpartyfinder/" + url "https://github.com/grcengineering/nthpartyfinder/releases/download/v#{version}/nthpartyfinder-#{arch}-apple-darwin.tgz" name "Nth Party Finder" desc "CLI tool for identifying Nth party vendor relationships through DNS analysis" homepage "https://grc.engineering/" + # Stanza order is fixed by Homebrew's `Cask/StanzaOrder` cop: cask, then formula, then macos. + depends_on cask: "google-chrome" + depends_on formula: "subfinder" + depends_on formula: "whois" # macOS-only: the artifact is a -apple-darwin binary, so `brew readall --os=all` (run by the # tap's test-bot) requires an explicit macOS declaration — without it the Linux simulation # leaves sha256 nil and the audit fails. Symbol form (`:big_sur`), not the deprecated string # comparison form. depends_on macos: :big_sur - depends_on formula: "subfinder" - depends_on formula: "whois" - depends_on cask: "google-chrome" binary "nthpartyfinder" From eed440dbe2355fed2db7b1fd8bd272ac15d3d889 Mon Sep 17 00:00:00 2001 From: p4gs <10093271+p4gs@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:20:55 -0400 Subject: [PATCH 3/5] secrets: keep Gitleaks alongside TruffleHog Owner decision: restore the Gitleaks job this PR had dropped, running alongside TruffleHog rather than replacing it. Independent probes showed Gitleaks catches generic secrets (e.g. api_key = "") and unverifiable keys that TruffleHog's --results=verified,unknown filter drops, so a single-scanner posture loses real detection coverage. Gitleaks is restored via the checksum-verified binary install (pinned v8.30.1, SHA256 verified against the release checksums before execution) rather than main's prior unauthenticated curl download, and scoped to the commits under review exactly as OCEAN's org template scans them. AI-Assisted: true AI-Tool: Claude Code AI-Model: Sonnet 5 AI-Role: draft Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012hwWMsTiv9Uf2gNbMh75uk --- .github/workflows/secrets-scan.yml | 110 ++++++++++++++++++++++++++--- .gitleaks.toml | 17 +++++ .sscsb/best-practices-badge.md | 2 +- .sscsb/config.toml | 27 +++---- README.md | 2 +- 5 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 .gitleaks.toml diff --git a/.github/workflows/secrets-scan.yml b/.github/workflows/secrets-scan.yml index f6987f9..ad5a355 100644 --- a/.github/workflows/secrets-scan.yml +++ b/.github/workflows/secrets-scan.yml @@ -1,15 +1,14 @@ -# sscsb: CI secret-scanning backstop (TruffleHog). +# sscsb: CI secret-scanning redundancy (TruffleHog + Gitleaks). # Local hooks block first; this catches anything that slips past (e.g. web UI commits), # and unlike the hooks it re-reads the FULL history on every run. # -# TruffleHog is the only secret scanner here, on purpose. A Gitleaks job used to run -# beside it over the same diff; both are pattern/entropy engines, but TruffleHog also -# VERIFIES a candidate credential against the issuing provider, so it distinguishes -# "this looks like a key" from "this key is live right now". A second, non-verifying -# engine bought duplicate findings and a second allowlist file to keep correct — -# another place to open a blind spot — without adding a detection class TruffleHog -# lacks. `--results=verified,unknown` keeps the unverifiable candidates in scope, so -# dropping Gitleaks does not narrow what gets reported. +# Two independent engines, deliberately. TruffleHog VERIFIES a candidate credential +# against the issuing provider, so it distinguishes "this looks like a key" from +# "this key is live right now" (`--results=verified,unknown` keeps the unverifiable +# candidates in scope). Gitleaks runs alongside it: independent probes showed it +# catches generic secrets (e.g. `api_key = ""`) and unverifiable keys that +# TruffleHog's verified/unknown filter still drops — a detection class TruffleHog +# alone does not cover. # # All actions pinned to full commit SHAs (sscsb actions-audit enforces this). name: Secret Scan @@ -41,3 +40,96 @@ jobs: with: extra_args: --results=verified,unknown + gitleaks: + runs-on: ubuntu-latest + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + # gitleaks/gitleaks-action is commercially licensed: for organization-owned + # repos it hard-fails before scanning unless a GITLEAKS_LICENSE secret exists + # ("[org] is an organization. License key is required." -- src/index.js). + # This org has no license, so that action never actually scanned anything. + # The gitleaks BINARY is MIT-licensed OSS, so we run it directly. + # actions-audit mandates full-SHA pinning for ACTIONS; the equivalent for a + # downloaded binary is a pinned version plus a checksum verified BEFORE exec. + - name: Install gitleaks (pinned release, checksum-verified before execution) + env: + GITLEAKS_VERSION: "8.30.1" + GITLEAKS_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" + run: | + set -euo pipefail + tarball="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" + base_url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}" + workdir="$(mktemp -d)" + + curl --fail --silent --show-error --location --retry 3 --retry-delay 2 \ + -o "${workdir}/${tarball}" "${base_url}/${tarball}" + curl --fail --silent --show-error --location --retry 3 --retry-delay 2 \ + -o "${workdir}/checksums.txt" \ + "${base_url}/gitleaks_${GITLEAKS_VERSION}_checksums.txt" + + # 1. the digest pinned here must be the digest this release publishes + grep -Fxq "${GITLEAKS_SHA256} ${tarball}" "${workdir}/checksums.txt" + # 2. the bytes we downloaded must match that digest -- checked before exec + ( cd "${workdir}" \ + && printf '%s %s\n' "${GITLEAKS_SHA256}" "${tarball}" | sha256sum -c - ) + + install_dir="${RUNNER_TEMP}/gitleaks-bin" + mkdir -p "${install_dir}" + tar -xzf "${workdir}/${tarball}" -C "${install_dir}" gitleaks + chmod +x "${install_dir}/gitleaks" + printf '%s\n' "${install_dir}" >> "${GITHUB_PATH}" + + - name: Gitleaks scan + env: + # Context values are passed as env vars, never interpolated into the + # script body (GitHub Actions expression-injection hardening). + EVENT_NAME: ${{ github.event_name }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PUSH_BEFORE_SHA: ${{ github.event.before }} + PUSH_HEAD_SHA: ${{ github.sha }} + run: | + set -euo pipefail + gitleaks version + + null_sha="0000000000000000000000000000000000000000" + case "${EVENT_NAME}" in + pull_request) base_sha="${PR_BASE_SHA}"; head_sha="${PR_HEAD_SHA}" ;; + push) base_sha="${PUSH_BEFORE_SHA}"; head_sha="${PUSH_HEAD_SHA}" ;; + *) base_sha=""; head_sha="${PUSH_HEAD_SHA}" ;; + esac + + # Scope = the commits under review. This is exactly the scope the + # licensed action scanned on push/pull_request events + # (src/gitleaks.js: --log-opts=--no-merges --first-parent BASE..HEAD); + # it is a scan-scope choice, not a rule/finding suppression. + if [ -n "${base_sha}" ] && [ "${base_sha}" != "${null_sha}" ] \ + && git cat-file -e "${base_sha}^{commit}" 2>/dev/null; then + log_opts="--no-merges --first-parent ${base_sha}..${head_sha}" + else + # No resolvable base (new branch / force push): scan the head commit. + # No --no-merges here: it could filter out a merge head and leave us + # silently scanning zero commits, which would pass without checking. + log_opts="-1 ${head_sha}" + fi + echo "Scanning commit range: ${log_opts}" + + # --redact keeps any match out of the job log. Exit code is left at the + # gitleaks default (1 when leaks are found), so a leak fails this check. + gitleaks git \ + --config .gitleaks.toml \ + --redact \ + --no-banner \ + --verbose \ + --log-opts="${log_opts}" \ + . + diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..c73ed1b --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,17 @@ +# sscsb sample Gitleaks configuration (.gitleaks.toml). +# Extends the built-in default ruleset; add project-specific allowlists here +# rather than weakening rules globally. + +title = "sscsb gitleaks config" + +[extend] +useDefault = true + +# Example: allowlist generated fixtures that contain entropy but no secrets. +# (Keep this list SHORT and reviewed — every entry is a scanning blind spot.) +[[allowlists]] +description = "sscsb generated non-secret artifacts" +paths = [ + '''\.sscsb/out/.*\.cdx\.json''', + '''\.sscsb/out/.*\.spdx\.json''', +] diff --git a/.sscsb/best-practices-badge.md b/.sscsb/best-practices-badge.md index 6a5b37d..5f1afb4 100644 --- a/.sscsb/best-practices-badge.md +++ b/.sscsb/best-practices-badge.md @@ -28,7 +28,7 @@ unearned assurance this whole tap exists to prevent. | Static analysis (SAST) applied | `sast` (OpenGrep, `.github/workflows/sast-opengrep.yml`) + `codeql` (`actions` and `ruby` extractors) | ☑ | | Vulnerability scanning of dependencies | n/a — the only dependency surface this repo owns is its GitHub Actions, which are SHA-pinned (`actions-audit`) and updated by Dependabot. There are no lockfiles or package manifests to scan; `vuln-scan` is off for that reason. | ☑ (n/a) | | No unpatched medium+/high vulns | n/a, same reason as the row above | ☑ (n/a) | -| Secrets not committed; scanned | `secrets` — TruffleHog at pre-commit, pre-push and in CI over full history (`.github/workflows/secrets-scan.yml`) | ☑ | +| Secrets not committed; scanned | `secrets` — TruffleHog + Gitleaks at pre-commit, pre-push and in CI over full history (`.github/workflows/secrets-scan.yml`) | ☑ | | Vulnerability reporting process documented | `SECURITY.md` + private GitHub Security Advisories | ☑ | | Cryptographic signing of releases | n/a — this tap publishes no releases. What it does instead is the harder half: `formula-integrity.yml` verifies UPSTREAM's Sigstore/SLSA signatures for every artifact a formula points at, pinned to the expected source repo and tag, on every PR, every push to `main`, and weekly. | ☑ (n/a) | | Delivery over TLS / pinned deps | all actions SHA-pinned (`actions-audit`, `workflow-audit-extended`); all formula URLs are HTTPS with pinned `sha256` | ☑ | diff --git a/.sscsb/config.toml b/.sscsb/config.toml index ca13cf8..e4c6ed1 100644 --- a/.sscsb/config.toml +++ b/.sscsb/config.toml @@ -15,24 +15,25 @@ github_repo = "grcengineering/homebrew-grcengineering" # ── Phase 1 — Local source integrity ── -# Secret scanning hooks: TruffleHog blocks secrets at pre-commit and pre-push. +# Secret scanning hooks: TruffleHog and Gitleaks both block secrets at pre-commit +# and pre-push. # -# TruffleHog ONLY, deliberately. Gitleaks used to run alongside it here and has been -# removed. Both are pattern/entropy engines over the same diff, but TruffleHog also -# VERIFIES a candidate credential against the issuing provider, so it can tell -# "this looks like a key" apart from "this key is live right now". A second, -# non-verifying engine adds duplicate findings and a second allowlist file to -# maintain — a second place to create a blind spot — without adding a detection -# class TruffleHog lacks. One scanner, the one whose hits can be proven. +# Both run, deliberately. TruffleHog VERIFIES a candidate credential against the +# issuing provider, so it can tell "this looks like a key" apart from "this key is +# live right now" (`--results=verified,unknown` keeps unverifiable candidates in +# scope). Gitleaks runs alongside it: independent probes showed it catches generic +# secrets (e.g. `api_key = ""`) and unverifiable keys that TruffleHog's +# verified/unknown filter still drops — a detection class TruffleHog alone does +# not cover. [controls.secrets] enabled = true trufflehog = true -gitleaks = false +gitleaks = true pre_push_range_scan = true -# NOTE for whoever runs `sscsb init` next: sscsb 0.4.0 writes the sample -# `.gitleaks.toml` whenever the `secrets` control is enabled, without consulting the -# `gitleaks` toggle above. If that file reappears, delete it — it is a scanner config -# for an engine this repo does not run, and leaving it implies a control that is off. +# sscsb 0.4.0 writes the sample `.gitleaks.toml` whenever the `secrets` control is +# enabled. With `gitleaks = true` that file is live config for the engine actually +# running here, not an orphan — review and maintain it like any other scanner +# config; don't delete it. # CommitSigningGuard: Hardware-backed, human-only signing enforced on protected branches at pre-push [controls.commit-signing] diff --git a/README.md b/README.md index d2e5237..3e9e511 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ stated plainly so the guarantee isn't misread. | **Weekly re-verification** | Merged formulae are re-checked on a schedule, so an asset swapped or re-pointed *after* review is caught. This is detection (≤1 week exposure), not prevention. | | **Human-only signing** | Protected-branch commits require a hardware-backed human signature. No AI key is registered, so an AI signature cannot be valid here. | | **Formula correctness** | `brew test-bot` runs `brew style` and `brew audit`, and builds/tests changed formulae on PRs across macOS x86_64, macOS arm64, and Linux — an authoritative per-platform fetch that resolves the artifact the way Homebrew actually will. | -| **Everything else** | SHA-pinned actions with least-privilege `permissions:`, Harden-Runner on every job, TruffleHog secret scanning (verified-credential detection; the single scanner, not one of two), OpenGrep SAST (extended to Ruby), CodeQL, OpenSSF Scorecard, Dependabot for the actions the workflows call. | +| **Everything else** | SHA-pinned actions with least-privilege `permissions:`, Harden-Runner on every job, TruffleHog + Gitleaks, OpenGrep SAST (extended to Ruby), CodeQL, OpenSSF Scorecard, Dependabot for the actions the workflows call. | **What it does not do:** none of the above proves a formula's install code is *safe* — provenance and digests are about the tarball's bytes, not what the From 3ce32ea6bfda9eb64680204a268b938ace002f5d Mon Sep 17 00:00:00 2001 From: p4gs <10093271+p4gs@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:34:46 -0400 Subject: [PATCH 4/5] docs(secrets): say CI scans the commits under review, not full history Both the badge worksheet and the secrets-scan.yml header claimed CI re-reads the full history. Neither job does: TruffleHog and Gitleaks scan the PR or push range. Only the local first push of a new branch scans a full history. AI-Assisted: true AI-Tool: Claude Code AI-Model: Opus 5.5 AI-Role: draft Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012hwWMsTiv9Uf2gNbMh75uk --- .github/workflows/secrets-scan.yml | 4 ++-- .sscsb/best-practices-badge.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/secrets-scan.yml b/.github/workflows/secrets-scan.yml index ad5a355..434a9e0 100644 --- a/.github/workflows/secrets-scan.yml +++ b/.github/workflows/secrets-scan.yml @@ -1,6 +1,6 @@ # sscsb: CI secret-scanning redundancy (TruffleHog + Gitleaks). -# Local hooks block first; this catches anything that slips past (e.g. web UI commits), -# and unlike the hooks it re-reads the FULL history on every run. +# Local hooks block first; this catches anything that slips past (e.g. web UI commits). +# Both jobs scan the commits under review (the PR or push range), not the full history. # # Two independent engines, deliberately. TruffleHog VERIFIES a candidate credential # against the issuing provider, so it distinguishes "this looks like a key" from diff --git a/.sscsb/best-practices-badge.md b/.sscsb/best-practices-badge.md index 5f1afb4..2022711 100644 --- a/.sscsb/best-practices-badge.md +++ b/.sscsb/best-practices-badge.md @@ -28,7 +28,7 @@ unearned assurance this whole tap exists to prevent. | Static analysis (SAST) applied | `sast` (OpenGrep, `.github/workflows/sast-opengrep.yml`) + `codeql` (`actions` and `ruby` extractors) | ☑ | | Vulnerability scanning of dependencies | n/a — the only dependency surface this repo owns is its GitHub Actions, which are SHA-pinned (`actions-audit`) and updated by Dependabot. There are no lockfiles or package manifests to scan; `vuln-scan` is off for that reason. | ☑ (n/a) | | No unpatched medium+/high vulns | n/a, same reason as the row above | ☑ (n/a) | -| Secrets not committed; scanned | `secrets` — TruffleHog + Gitleaks at pre-commit, pre-push and in CI over full history (`.github/workflows/secrets-scan.yml`) | ☑ | +| Secrets not committed; scanned | `secrets` — TruffleHog + Gitleaks at pre-commit, pre-push and in CI on the commits under review (`.github/workflows/secrets-scan.yml`) | ☑ | | Vulnerability reporting process documented | `SECURITY.md` + private GitHub Security Advisories | ☑ | | Cryptographic signing of releases | n/a — this tap publishes no releases. What it does instead is the harder half: `formula-integrity.yml` verifies UPSTREAM's Sigstore/SLSA signatures for every artifact a formula points at, pinned to the expected source repo and tag, on every PR, every push to `main`, and weekly. | ☑ (n/a) | | Delivery over TLS / pinned deps | all actions SHA-pinned (`actions-audit`, `workflow-audit-extended`); all formula URLs are HTTPS with pinned `sha256` | ☑ | From 5562079f9cbce072ee8070b9a98e0a86520011da Mon Sep 17 00:00:00 2001 From: p4gs <10093271+p4gs@users.noreply.github.com> Date: Tue, 6 Oct 2026 04:12:07 -0400 Subject: [PATCH 5/5] fix(cask): use depends_on :macos instead of a redundant Big Sur minimum Homebrew 7.0.8 added the Homebrew/OSDependsOn cop, which fails test-bot's style step on `depends_on macos: :big_sur`: Big Sur is the oldest macOS Homebrew still recognises, so the minimum excludes nothing. `depends_on :macos` keeps the macOS-only declaration that `brew readall --os=all` needs. `brew style` on the cask: 1 offense before, none after. AI-Assisted: true AI-Tool: Claude Code AI-Model: Opus 5.5 AI-Role: draft Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012hwWMsTiv9Uf2gNbMh75uk --- Casks/nthpartyfinder.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Casks/nthpartyfinder.rb b/Casks/nthpartyfinder.rb index 5322ccd..b61a63c 100644 --- a/Casks/nthpartyfinder.rb +++ b/Casks/nthpartyfinder.rb @@ -34,9 +34,10 @@ depends_on formula: "whois" # macOS-only: the artifact is a -apple-darwin binary, so `brew readall --os=all` (run by the # tap's test-bot) requires an explicit macOS declaration — without it the Linux simulation - # leaves sha256 nil and the audit fails. Symbol form (`:big_sur`), not the deprecated string - # comparison form. - depends_on macos: :big_sur + # leaves sha256 nil and the audit fails. Plain `:macos`, not a minimum version: Homebrew's + # `Homebrew/OSDependsOn` cop rejects `macos: :big_sur` as redundant: Big Sur is the oldest + # macOS Homebrew still recognises, so that minimum excludes nothing. + depends_on :macos binary "nthpartyfinder"