The security checks in every Lionshead PR
At enterprise scale, a breach is a bad quarter. You have a legal team to coordinate disclosure. A disaster-recovery plan you drill annually. A PR team to control the narrative. A security team to quarantine, investigate, and triage the incident. A bank account big enough to absorb regulatory fines, class-action settlements, and the customer churn that follows.
At solo scale, you have none of those. A real breach of user data will almost certainly end the product. Not "hurts the brand," not "sets us back a quarter." Ends it.
So you breach yourself first.
The security stack in every Lionshead PR is an automated self-breach. Every merge attempt is scanned by tools a mid-sized security team would run against production code weekly. If any of them find something, the merge blocks. The vulnerability never reaches production, and the version of me that would have caused an incident never gets the chance.
Here's what actually runs on every PR against every Lionshead product.
Trivy filesystem — vulnerability, secret, and license scanning across the whole repo. Runs on every PR, no path gate. Catches dependency CVEs, accidental hardcoded credentials, license conflicts. Configured via trivy.yaml at the repo root; fails on any CRITICAL or HIGH severity. Trivy is Aqua Security's open-source scanner and it's the single best free vulnerability scanner I've used.
Trivy IaC (Terraform) — the same tool, second mode, against Terraform files. Catches insecure infrastructure defaults (public buckets, permissive IAM roles, unencrypted resources). Path-gated: only runs when terraform/** files change. Same severity gate.
Gitleaks — secret scanning across the entire git history, not just the current diff. Catches API keys, tokens, private keys, connection strings that someone (probably me) committed years ago and later deleted. The secret is still in the history and still exploitable. Runs on every PR. Uses the default ruleset unless the repo drops a .gitleaks.toml override.
Checkov — a second IaC security scanner, run alongside Trivy IaC for defense in depth. Checkov and Trivy IaC use different rulesets and different heuristics; they catch different classes of problem. Path-gated on terraform/**. Skip lists for known-false-positive rules go in .checkov.yaml.
OWASP ZAP baseline — active DAST against the PR's Vercel preview URL. When a repo produces a preview deploy, ZAP hits the deployed site with a baseline security scan. Catches missing security headers, cookie configuration issues, common injection vectors. Fails on any WARN+ alert; per-rule suppressions live in the repo's .zap/rules.tsv.
Actionlint — GitHub Actions workflow linting. Not strictly security, but security-adjacent: catches syntax errors, invalid contexts, and permission-scope typos that would silently over-scope your workflow's access. Path-gated on .github/workflows/**.
Shellcheck — shell script static analysis. Catches quoting errors, command-injection patterns, and the classic "unquoted variable in an rm command" bug that has ended more careers than any single vulnerability. Path-gated on *.sh files.
Hadolint — Dockerfile linting. Catches insecure image bases, missing USER directives, and other Dockerfile-shape issues that expand blast radius on container escape. Path-gated on Dockerfile*.
Action pins check — a Lionshead-authored guardrail that validates every third-party GitHub Action referenced in workflow files is pinned to a full commit SHA, not a moving tag. Prevents a supply-chain attack against an action's tag from silently propagating into Lionshead's CI. Fails PRs that add or update actions without a SHA pin.
Infracost + OPA cost gate — not security in the CVE sense, but security in the "my card gets charged $10K/month by mistake" sense. Runs on every Terraform PR, computes the cost delta, and pauses the PR on a GitHub Environment approval gate when the delta exceeds $50/month. I covered this in more detail in the last post; mentioned here because it lives in the same workflow.
Two more layers run outside the PR gate.
Scheduled security audit — every product repo runs lionshead-security-audit.yml daily against the default branch. It runs npm audit (or the language-appropriate equivalent) plus a full Trivy filesystem scan. Findings open or update a single security:dependency tracking issue per repo; clean runs close any open issue. The point is to catch CVEs disclosed after code was merged, not just what a PR introduces. The majority of real-world CVEs are disclosed against code already in production; a PR-time scan alone misses them.
Renovate — dependency-update automation. The ci-standards Renovate preset applies to every product repo via a one-line renovate.json. Renovate opens PRs to bump dependencies, pin action SHAs, and update base container images. Its github-actions manager keeps the pinned reusable workflow SHAs current, which is how new products cloned from template-product stay on the latest ci-standards workflow without manual intervention. Weekly schedule keeps the noise bounded.
Three things are on the roadmap but not shipping yet.
Semgrep (SAST) — the security baseline mandates Semgrep OSS for static-analysis coverage as a free-and-open-source substitute for GitHub Advanced Security. Rollout across all products is in progress.
OWASP ZAP as a scheduled cron against dev — today, ZAP runs at PR time against the preview URL. The plan is to also run ZAP against the shared dev environment weekly to catch drift between preview builds and the environment that lives longer.
Pre-commit hooks — a Husky-driven local hook stack that runs the most annoying failure modes (a Gitleaks pre-commit scan, at minimum) before code even gets to the PR. Removes a full round-trip when you catch a mistake before pushing. On the shortlist to ship next.
The whole stack costs zero in licensing. Every tool above is open source. The cost is time to wire them up and ongoing time to tune false-positive suppressions. That cost is measured in hours, once, not months. What it buys is the difference between "my product survives" and "my product ends" in the scenario where I fat-finger a Terraform PR at 11pm.
That is the trade at solo scale. It happens to look like the trade at enterprise scale for exactly the same reason.
What's next
Next in the CI/CD sub-series: the preview environment pattern that ZAP runs against. Vercel + a fresh Neon Postgres branch per PR, how the reviewer gets a real running app talking to a real database on every PR, and why that costs almost nothing.
In this series: Building Lionshead
- building-lionshead
- ci-cd
- security