Skip to content

Repository files navigation

security-baseline

A portable Secure-by-Tier + Production Readiness harness for Claude Code and Codex CLI, packaged as a plugin. It right-sizes security and production-grade review to a project's tier/profile — it never gold-plates a throwaway spike, and never under-protects an app holding real user data. You opt in per project; projects you don't opt in stay untouched.

Review effort should scale with blast radius, operational reality, and maintainability risk — not ambition.

Contents


Why this

Every app that touches a network or stores data has a security surface. Every production app also has an operational surface: tests, rollback, logging, scaling limits, data deletion, and the ability for someone else to understand the code later. The question is never "is it secure?" or "is it production ready?" in the abstract — it's "has it been checked enough for what it actually does?"

Two failure modes are equally bad:

  • Under-securing. A real app with users and PII that skips the basics. Most breaches are not exotic — they come from cheap-to-prevent mistakes: a secret committed to git, a known-vulnerable dependency, a missing auth check, SQL injection, a cookie without httpOnly. These are the OWASP Top 10 and they account for the large majority of real-world incidents.
  • Over-securing. Forcing a weekend prototype through a SOC 2 checklist, a pentest, and encryption-at-rest. That burns time and money on risk that doesn't exist yet, and it teaches people to ignore the process.

The fix is right-sizing. Classify the project by blast radius, then apply only the controls that match. A scratch script needs secret hygiene. A multi-tenant SaaS holding customer data needs that plus auth, tenant isolation, input validation, tests, observability, rollback paths, and dependency/secret scanning in CI. A health or fintech app needs all of that plus encryption, audit logging, a threat model, compliance scope, and a pentest.

This plugin encodes that judgement so you don't have to re-derive it each time, and so an AI agent working in your repo applies the correct bar automatically. It also calls out technical debt from fast AI-assisted coding when that debt would block safe production work.

The cheapest, highest-value controls (any real project): keep secrets out of the repo (gitleaks), scan dependencies in CI (npm audit / pip-audit), require auth on anything non-public, add security headers, and set httpOnly/secure cookies. Near-zero effort, catches most real issues. The plugin proposes these first.

The tiers

Cumulative — each tier includes every control from the tiers below it. First match wins, top-down.

Tier Name You're here if… Core controls it adds
T0 Throwaway / Local Never deployed; no real data; runs only on your machine Secrets gitignored; no prod creds locally. That's it.
T1 PoC / Demo Deployed, but throwaway data; internal eyes only + OWASP basics, dependency scan, secret scan, auth on non-public
T2 Production (low-sensitivity) Real users + auth; non-sensitive PII (name/email) + SAST, input validation, per-request authZ + tenant isolation, security headers, rate limiting, HTTPS + secure cookies, no PII in logs, GDPR/PDPA baseline
T3 Production (regulated) Health/financial/biometric data, payments, or contractual ISO/SOC 2 + encryption at rest, full GDPR/PDPA program, HIPAA controls, threat model, audit logging, pentest

Overlays (any tier): EU users → GDPR · Thai users → PDPA · health data → HIPAA · payments → bump to T3.

The full model with the complete per-tier checklist lives in reference/security-baseline-spec.md.

What's in the box

Component What it does
skill security-baseline Tier-aware security review. Detects the tier → applies only that tier's checklist → reports findings as 🔴/🟡/🟢 with file:line + a concrete fix.
skill production-readiness Production-grade review orchestrator. Selects the right review mode and dimensions across security, privacy/compliance, architecture, scalability, reliability, testing, observability, technical debt, and release readiness.
skill security-baseline-init The per-project opt-in. Classifies the security tier and production readiness profile, writes the baseline block into CLAUDE.md / AGENTS.md, and optionally installs the pre-commit scanner.
skill security-reviewer Cross-runtime, read-only reviewer for one delegated security area. Codex subagents can invoke it explicitly during fan-out.
agents Read-only, area-scoped reviewers. The security and production-readiness skills can fan out to security, architecture, technical debt, scalability, ops, testing, observability, and compliance reviewers for larger audits.
hook pre-commit-scan.sh Deterministic scanners (gitleaks + npm audit / pip-audit). Optional SBL_PROD=1 strict mode also runs discoverable lint/typecheck/test gates. No AI reasoning involved.
script check-baseline-config.sh Lightweight deterministic check that CLAUDE.md / AGENTS.md contains the expected security tier and production readiness profile block.
skill security-ledger Reviews with memory: a per-project ledger (.security-baseline/ledger.md, private by default) with stable SB-### IDs, a status lifecycle (openfixedfixed-verified, plus accepted-risk and false-positive; a fixed-verified item that returns is reopened and annotated regressed), a foundation checklist, and review history. Runs the CLI decision loop.
script ledger.py Deterministic ledger operations (IDs, transitions, summary, history). Stdlib Python; the skill is its only caller.

security-reviewer exists twice on purpose: the agent (agents/security-reviewer.md) and the skill (skills/security-reviewer/SKILL.md) do the same job for different runtimes — the agent is Claude Code's native subagent form, the skill is the portable one Codex invokes as $security-reviewer. Same review, one per runtime; you never need both in a single fan-out.

security-baseline/
├── .claude-plugin/
│   ├── plugin.json          # manifest
│   └── marketplace.json     # makes the repo installable
├── .codex-plugin/
│   └── plugin.json          # Codex / universal plugin manifest
├── skills/
│   ├── security-baseline/SKILL.md         # review
│   ├── production-readiness/SKILL.md      # production gate orchestrator
│   ├── security-baseline-init/            # opt-in command
│   │   ├── SKILL.md
│   │   ├── scripts/install-project-assets.sh   # copies consumer assets, migrates legacy
│   │   └── assets/codex-agents/*.toml          # project-scoped Codex reviewer templates
│   ├── security-reviewer/SKILL.md         # portable delegated reviewer
│   └── security-ledger/SKILL.md           # reviews with memory
├── agents/
│   ├── security-reviewer.md
│   ├── architecture-reviewer.md
│   ├── technical-debt-reviewer.md
│   ├── scalability-reviewer.md
│   ├── ops-readiness-reviewer.md
│   ├── testing-reviewer.md
│   ├── observability-reviewer.md
│   └── compliance-reviewer.md
├── hooks/pre-commit-scan.sh
├── scripts/
│   ├── check-baseline-config.sh
│   ├── ledger.py             # deterministic ledger operations
│   └── validate-package.py  # cross-runtime packaging checks
├── tests/
│   ├── smoke.sh              # packaging, installer, scanner, ledger CLI end-to-end
│   └── test_ledger.py        # ledger.py unit tests
└── reference/
    ├── security-baseline-spec.md          # the security model
    ├── production-readiness-spec.md       # the production-readiness model
    ├── production-readiness-plan.md       # expansion roadmap
    ├── models.md                          # model tiers, profiles, guard rules
    ├── decision-brief.md                  # decision brief shape + prompt handling
    └── examples/
        ├── production-readiness-report.md
        └── github-actions-production-gates.yml

Install — Claude Code

Quick / local (development & trying it out)

No marketplace needed — point Claude Code at the plugin directory for a session:

claude --plugin-dir /path/to/security-baseline

Persistent (from GitHub)

The repo ships a .claude-plugin/marketplace.json, so:

/plugin marketplace add pmrster/security-baseline-plugin
/plugin install security-baseline@security-baseline-marketplace

Pin a release with <owner>/<repo>@v0.3.0, or use a full git URL for non-GitHub hosts.

Team-wide, add it to .claude/settings.json instead of running the command on each machine:

{
  "extraKnownMarketplaces": {
    "security-baseline-marketplace": {
      "source": { "source": "github", "repo": "<owner>/<repo>" }
    }
  }
}

Install — Codex CLI

The repo includes a validated .codex-plugin/plugin.json following the OpenAI plugin packaging model. For local development before the plugin is published, link the skills into the Codex skills directory (~/.codex/skills/, or the cross-runtime ~/.agents/skills/):

ln -s /path/to/security-baseline/skills/security-baseline       ~/.codex/skills/security-baseline
ln -s /path/to/security-baseline/skills/production-readiness    ~/.codex/skills/production-readiness
ln -s /path/to/security-baseline/skills/security-baseline-init  ~/.codex/skills/security-baseline-init
ln -s /path/to/security-baseline/skills/security-reviewer       ~/.codex/skills/security-reviewer

Codex notes:

  • It reads AGENTS.md for project instructions — security-baseline-init writes the same tier block there.

  • Current Codex releases enable subagent workflows by default. No legacy multi_agent feature flag is required. A user can disable them with agents.enabled = false; the skills then review inline.

  • To install project-owned scanner assets and the bundled read-only Codex custom agents, run from the consuming project after reviewing the script:

    bash /path/to/security-baseline/skills/security-baseline-init/scripts/install-project-assets.sh \
      --codex-agents .

    Then, if .git/hooks/pre-commit does not already exist, wire the runtime-neutral scanner:

    ln -s ../../.security-baseline/hooks/pre-commit-scan.sh .git/hooks/pre-commit

    Never overwrite an existing Git hook; compose it deliberately instead.


How to use it

There are three ways the security review reaches your code. They stack — use as many as you want.

1. Opt the project in (recommended first step)

Run this once in a project:

/security-baseline:security-baseline-init

It will:

  1. Detect or ask the project's tier, production readiness profile, and overlay questions.
  2. Write a baseline block into CLAUDE.md (and AGENTS.md for Codex) — see the next section.
  3. Offer to install the pre-commit scanner. If you say yes, it copies project-owned files to .security-baseline/, then offers Claude Code hook wiring or a native Git hook for Codex and other runtimes. Hook configuration requires approval because it can block commits.
  4. For Codex projects, offer project-scoped read-only reviewer agents under .codex/agents/.
  5. Report exactly what it changed. Nothing global is touched.

Re-run it anytime to change the tier or refresh the hook.

2. Let the agent call it automatically

You don't have to name the tool. Skills carry a description that tells the agent when to fire. Just ask, in plain language:

"Is this safe to deploy?" "Review the security of my changes before I open a PR." "Run a production-readiness review before launch." "Check whether this vibe-coded feature has technical debt that blocks production." "Did I leave any secrets or obvious vulns in here?" "Set up CI security scans for this repo."

Claude (or Codex) will invoke the relevant skill, detect the tier, and review the diff or production path against the selected controls.

3. Call the skills manually

Invoke them directly by name. In Claude Code, use plugin-namespaced slash commands:

/security-baseline:security-baseline          # run a tier review
/security-baseline:production-readiness       # run a production-readiness review
/security-baseline:security-baseline-init      # opt the project in / change the tier

In Codex CLI or the IDE extension, use skill mentions:

$security-baseline
$production-readiness
$security-baseline-init
$security-reviewer

Reading the output

Every review groups findings by severity, and each finding names the file, the control it violates, and the fix:

🔴 Must fix before merge
  src/server.js:3 — hardcoded Stripe key (secrets must come from env) → move to process.env.STRIPE_KEY
🟡 Should fix
  no rate limiting on /login → add express-rate-limit (e.g. 20 req / 15 min)
🟢 Nice to have
  no .gitignore → add one so .env can't be committed

It only flags controls for your tier (no T3 demands on a T1 demo), and it never claims your app is "secure" — only "checked X against tier Y; out of scope: Z."

Reviews with memory

Once a project has a ledger, every review reads it first and reports New · Still open · Regressed · Verified fixed by ID — a fixed issue can't silently come back as "critical"; it comes back as SB-004 regressed @<commit>. quick-diff and pr-gate reviews scope to the diff since the last review, which is also the main token saving; pre-prod and full-audit deliberately ignore that boundary and review the full production path or the whole repo.

In the CLI you see a compact verdict, then one decision at a time for 🔴/🟠 items: a plain "why it matters", the standard it comes from, and a full comparison of the options across security, effort, user impact, performance, conflicts, tech debt, over-engineering, structure, deployment/live, rollback, cost, and testing — then go A / go B / accept-risk / later / explain more / ask a question. Your answer is the only thing that gets written as a decision.

open: 2 (🔴1 🟠1) · accepted-risk: 1 · fixed-verified: 5 · checklist: 7/12
New       SB-007 🟠 — Invoice job can double-charge on retry — src/jobs/sendInvoice.ts:31
Regressed SB-001 🔴 — Any user can read another customer's project by ID — src/app/api/projects/[id]/route.ts:42
decisions needed: 2

Model use follows reference/models.md: judgment work on the best model, verification on a balanced model, presence checks on the cheapest — and a cheap model never closes a finding.

Run the scanner on its own

The pre-commit scanner is plain bash and needs no AI:

bash hooks/pre-commit-scan.sh              # scan staged changes (or the repo if nothing staged)
SBL_FULL=1 bash hooks/pre-commit-scan.sh   # force a full repo secret scan
SBL_PROD=1 bash hooks/pre-commit-scan.sh   # include strict production gates

Exit 0 = clean / warnings only · exit 2 = blocked (a configured blocking finding). A scanner that isn't installed is skipped with a warning — a missing tool never blocks your commit.

SBL_PROD=1 adds deterministic readiness checks when they are discoverable:

  • Node: every npm script named in SBL_PROD_CHECKS (default: lint, typecheck, test) that exists in package.json.
  • Python: ruff, mypy, and pytest map to the lint/typecheck/test check names and run only when matching config/test signals exist.

Set SBL_PROD_CHECKS to choose which checks run (any npm script name works for Node), for example:

SBL_PROD=1 SBL_PROD_CHECKS=lint,typecheck,build bash hooks/pre-commit-scan.sh

Check that a project has opted in correctly (from the plugin repo, or via the copy that security-baseline-init places at .security-baseline/scripts/check-baseline-config.sh in opted-in projects):

bash .security-baseline/scripts/check-baseline-config.sh

Do I need a line in CLAUDE.md?

No — the skills work without it. You can call them manually (/security-baseline:…) or let the agent auto-invoke them when you ask a security question. Nothing in CLAUDE.md is required for that.

But the block is what makes it stick. security-baseline-init writes a small block like:

<!-- security-baseline:start -->
## Security tier
This project is **T2** (production, low-sensitivity PII). EU users: no. Thai users: yes (PDPA applies).
Apply the Secure-by-Tier baseline via the `security-baseline` skill. Run a tier review before each PR to `main`.

## Production readiness profile
This project uses the **production** profile. Default PR review: `pr-gate`. Default deploy/launch review: `pre-prod`.
Enabled dimensions: security, architecture, scalability, reliability/ops, testing, observability, technical debt, release readiness; compliance only when triggered.
Strict deterministic gates: use `SBL_PROD=1` in CI or pre-push; the default commit hook remains security-focused.
Apply the Production Readiness baseline via the `production-readiness` skill. Run a production-readiness review before major PRs and launches.
<!-- security-baseline:end -->

That block does three jobs a one-off command can't:

  1. Declares the tier, so the review skill stops asking "what tier is this?" every time and reviews against the right bar immediately.
  2. Declares the production profile, so production-readiness knows whether to run as quick-diff, pr-gate, pre-prod, or a fuller review.
  3. Is a standing instruction. Because CLAUDE.md (and AGENTS.md for Codex) is loaded into the agent's context every session, the agent proactively remembers to run a tier review before a PR — instead of only when you happen to ask. It turns "I review when I remember" into "the agent reviews for me."

So: optional for ad-hoc use, recommended for any project you'll come back to. That's why init writes it for you.

How it works

  1. Classify. The tier and production readiness profile are read from CLAUDE.md/AGENTS.md if declared; otherwise inferred from signals (deploy config, auth code, PII/payment in the schema, regulated data) and confirmed with you.
  2. Load the checklist. Only the cumulative controls for that tier/profile (T0 ⊆ T1 ⊆ T2 ⊆ T3).
  3. Review. The diff by default (token-lean); the whole repo on request. For a big audit, the review skill can dispatch read-only area reviewers in parallel — security, architecture, technical debt, scalability, ops, testing, observability, and compliance — then merge their findings.
  4. Report & offer fixes. Findings as 🔴/🟡/🟢 with file:line + fix, then an offer to apply the free/mechanical ones (CI config, headers, .gitignore).

Two enforcement layers, deliberately split by what each is good at:

  • Deterministic scanners (gitleaks, npm audit, optional strict lint/typecheck/test gates) → the pre-commit hook. It runs real commands and can block. Reliable; needs no reasoning.
  • Smart, tier-aware review (authZ, tenant isolation, "is this PII in a log?") → an instruction in CLAUDE.md. A hook can't reason about your business logic; an AI agent can. Both are project-scoped, which is exactly the opt-in mechanism.

Production readiness expansion

The plugin is security-first, and now includes a first production-readiness orchestrator that keeps security-baseline as the security pillar and adds architecture, scalability, reliability, testing, observability, maintainability, technical-debt, compliance, and release readiness review.

The model is documented here:

The executable layer is skills/production-readiness/SKILL.md. It can fan out to the area-specific read-only reviewers under agents/ in Claude Code. In Codex it uses the portable security-reviewer skill plus project-scoped read-only custom agents installed by init, with a generic read-only subagent fallback. The deterministic scanner also has an opt-in SBL_PROD=1 strict mode for discoverable lint/typecheck/test gates.

The opt-in model

  • A project opts in by running security-baseline-init once.
  • A project that never runs it is completely unaffected — no global hook, no forced review.
  • Installing the plugin only makes the skills and agents available; it changes no project's behavior until you opt that project in.

Limitations

  • Not a pentest / SAST product. It orchestrates and right-sizes, surfacing the free high-value controls first — it doesn't replace dedicated tooling at T3.
  • Not hard enforcement. On free private GitHub repos you can't enforce branch protection; the hook is a local gate and the review is advisory.
  • CodeQL needs Code Scanning (GHAS) — free on public repos, not on free private ones; any emitted CI keeps it optional.
  • In-memory rate limiting is per-instance only — use a shared store (e.g. Redis) for real distributed limits.
  • The copied hook doesn't auto-update with the plugin. Re-run init to refresh it.
  • Codex custom agents are project-scoped. Plugin installation distributes the skills; run security-baseline-init in each consuming repo that should also receive .codex/agents/*.toml.
  • Upgrading from ≤0.2.x: consumer assets moved from .claude/hooks/ to .security-baseline/. Re-run security-baseline-init; it migrates the legacy layout.

Never claims "secure"

Output is always "checked X against tier Y; out of scope: Z" — never a blanket "this is secure." Security is a moving target; the plugin tells you what it verified and what it didn't.

License

MIT.

About

No description, website, or topics provided.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages