Skip to content

sec(sscsb): close every verify gap but branch protection; TruffleHog is the only secret scanner - #22

Open
p4gs wants to merge 1 commit into
mainfrom
sec/sscsb-hardening
Open

sec(sscsb): close every verify gap but branch protection; TruffleHog is the only secret scanner#22
p4gs wants to merge 1 commit into
mainfrom
sec/sscsb-hardening

Conversation

@p4gs

@p4gs p4gs commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

What this does

sscsb verify on this repo went 19 PASS / 8 FAIL / 1 DEGRADED → 27 PASS / 1 FAIL / 1 DEGRADED.

Secret scanning is now TruffleHog only

gitleaks is off in .sscsb/config.toml, its CI job is gone from secrets-scan.yml, and .gitleaks.toml is deleted. TruffleHog validates a candidate against the issuing provider, so verified means a live credential; gitleaks matches entropy and regex and cannot make that call. Running both bought a second scan and a second allowlist file to keep honest, for no detection the stronger tool did not already make.

The same reasoning retires the weaker duplicate TruffleHog job in ci.yml: secrets-scan.yml runs the same tool on a superset of triggers (push to main + every PR, vs PRs targeting main) with a newer pinned release and --results=verified,unknown instead of --only-verified. Nothing is lost by deleting the duplicate.

SAST keeps three engines on purpose

codeql.yml is added (rust + actions, buildless) alongside the existing OpenGrep and Semgrep workflows. CodeQL does interprocedural taint tracking over a compiled database and surfaces classes an OpenGrep default ruleset does not express at all. That is not the kind of redundancy gitleaks was.

⚠️ Check this before merging. GitHub code-scanning default setup is currently configured on this repo (actions, python, rust — gh api repos/grcengineering/OCEAN/code-scanning/default-setup). Default setup and an advanced-setup workflow cannot both be active. If the CodeQL job on this PR fails for that reason, either drop codeql.yml and keep default setup (CodeQL is already running and already finding real issues), or switch to advanced setup and add python to the matrix so nothing is lost.

Five controls were failing for one shared reason

secrets, commit-signing, ai-trailers, ai-dep-gate and package-trust all failed because core.hooksPath pointed at .git/hooks, which holds only git's samples — so no sscsb hook had ever run. The shims were also tracked at mode 0644, and git silently skips a non-executable hook, so a fresh clone would have hit the same silent no-op even with the path set. Both fixed; the shims are 0755 in the tree now.

dependency-pinning

  • pip install pyyaml in the parity job → hash-locked pip install --require-hashes -r scripts/requirements-parity.txt.
  • Both Dockerfile stages digest-pinned.

Pinning stage 2 surfaced a live break: cgr.dev/chainguard/cc-dynamic is no longer anonymously pullable (403 FORBIDDEN on the anonymous pull token), so that build could not have succeeded on a runner without Chainguard credentials. Swapped to glibc-dynamic, which is anonymously pullable and carries everything this stage needs — verified by listing the image layers: libc.so.6, libgcc_s.so.1, ld-linux-*.so, ca-certificates.crt.

Also

  • release.yml: top-level write grants no longer leak into build and sbom; attestations: write dropped outright (no job in that workflow uses actions/attest*).
  • LICENSE (Apache-2.0) added — Cargo.toml and README.md both declared it and the README linked to a file that did not exist.
  • SECURITY.md added — security-insights.yml already pointed at it.
  • Stale "REPLACE-ME / TODO" note removed from security-insights.yml (there were no placeholders left), flipping it INFO → PASS.
  • .sscsb/out/ gitignored (generated SBOM/scan output).

What is NOT closed

Written down in .sscsb/POSTURE.md rather than left to be scrolled past.

branch-protection FAILs. A ruleset lives in GitHub's settings, not the tree, and sscsb harden branch-protection cannot write this one — it looks for a ruleset naming the branch literally, and ours targets the ~DEFAULT_BRANCH alias, so the planner reports "no ruleset targets this branch" and changes nothing. The three missing rules (required PRs, required signed commits, required status checks) are checked in as an importable ruleset:

gh api -X PUT repos/grcengineering/OCEAN/rulesets/14413947 \
  --input .github/rulesets/main-branch-protection.json
sscsb verify branch-protection

signing-model stays DEGRADED. Three lanes need an owner attestation of GitHub web settings that have no read API. The agent-claude-code lane is deliberately not converged: it asks for a distinct agent identity, and this owner runs one-signer-everywhere with AI involvement recorded through commit trailers instead.

Verified, not assumed

  • sscsb verify 27 PASS / 1 FAIL / 1 DEGRADED / 3 INFO / 15 disabled
  • sscsb sast — 0 findings
  • sscsb scan — 1 low (DS-0026, Dockerfile HEALTHCHECK), below the high fail threshold
  • the hash-locked pip install resolved and installed in a clean venv
  • both image digests resolved from the live registries
  • the glibc-dynamic layer contents were listed, not assumed

…is the only secret scanner

`sscsb verify` went 19 PASS / 8 FAIL / 1 DEGRADED -> 27 PASS / 1 FAIL / 1 DEGRADED.

Secret scanning is now TruffleHog ONLY. gitleaks is off in .sscsb/config.toml, its
CI job is gone from secrets-scan.yml, and .gitleaks.toml is deleted. TruffleHog
validates a candidate against the issuing provider, so `verified` means a live
credential; gitleaks matches entropy and regex and cannot make that call. Running
both bought a second scan and a second allowlist file to keep honest, for no
detection the stronger tool did not already make. The same reasoning retires the
weaker duplicate TruffleHog job in ci.yml: secrets-scan.yml runs the same tool on a
superset of triggers with a newer pinned release and --results=verified,unknown
instead of --only-verified, so nothing is lost by deleting the duplicate.

SAST keeps THREE engines on purpose. codeql.yml is added (rust + actions, buildless)
alongside the existing OpenGrep and Semgrep workflows: CodeQL does interprocedural
taint tracking over a compiled database and surfaces classes an OpenGrep default
ruleset does not express at all. That is not the kind of redundancy gitleaks was.

Five controls (secrets, commit-signing, ai-trailers, ai-dep-gate, package-trust) were
failing for one shared reason: core.hooksPath pointed at .git/hooks, which holds only
git's samples, so NO sscsb hook had been running. The shims were also tracked mode
0644 — git silently skips a non-executable hook, so a fresh clone would have had the
same silent no-op even with the path set. Both fixed; the shims are 0755 now.

dependency-pinning: the parity job's `pip install pyyaml` becomes a hash-locked
`--require-hashes` install, and both Dockerfile stages are digest-pinned. Pinning
stage 2 surfaced a live break — cgr.dev/chainguard/cc-dynamic is no longer
anonymously pullable (403 FORBIDDEN on the anonymous pull token), so that build could
not have succeeded on a runner without Chainguard credentials. Swapped to
glibc-dynamic, which is anonymously pullable and carries everything this stage needs
(verified in the image: libc.so.6, libgcc_s.so.1, ld-linux, ca-certificates.crt).

Also: release.yml's top-level write grants no longer leak into `build` and `sbom`,
and `attestations: write` is dropped outright because no job in that workflow uses
actions/attest*. LICENSE (Apache-2.0) is added — Cargo.toml and README both declared
it and README linked to a file that did not exist. SECURITY.md is added, which
security-insights.yml already pointed at. The stale "REPLACE-ME / TODO" note is
removed from security-insights.yml (there were no placeholders left), flipping it
INFO -> PASS.

What is NOT closed, with reasons, is written down in .sscsb/POSTURE.md rather than
left to be scrolled past:

- branch-protection FAILs because a ruleset lives in GitHub's settings, not the tree,
  and `sscsb harden` cannot write this one (it looks for a ruleset naming the branch
  literally; ours targets ~DEFAULT_BRANCH). The three missing rules are checked in as
  an importable ruleset under .github/rulesets/, with the gh command beside it.
- signing-model stays DEGRADED. Three lanes need an owner attestation of GitHub web
  settings that have no read API. The agent-claude-code lane is deliberately NOT
  converged: it asks for a distinct agent identity, and this owner runs one-signer,
  everywhere, with AI involvement recorded through commit trailers instead.

Verified, not assumed: sscsb verify 27/1/1; sscsb sast 0 findings; sscsb scan 1 low
(Dockerfile HEALTHCHECK, below the `high` fail threshold); the hash-locked pip
install resolved and installed in a clean venv; both image digests resolved from the
live registries; the glibc-dynamic layer contents were listed, not assumed.

AI-Assisted: true
AI-Tool: Claude Code
AI-Model: claude-opus-5
AI-Role: draft

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017N6Qc2T7buVAXHZ9vYa1xm
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​pyyaml@​6.0.3100100100100100

View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant