security-gate
Skill6 starsChanged 2 months agoMITView on GitHub
Feature-scoped security gate between /verify and pk ship — classifies the diff into sensitive categories, runs matched checklists, writes the PASS sentinel pk ship hard-requires on gated projects. Not /repo-security-review (repo-wide) or /pr-security-review (PR-scoped).
4 other projects use a version of this.
Adopted by
Most recently taken up by
- feelthefusion/skill-starter-kittheir own version, first seen Oct 13, 2026
- nek1987/auto-agent-harnesstheir own version, first seen Oct 11, 2026
- Gaurav890/everything-agentic-engineeringtheir own version, first seen Sep 13, 2026
- OpenOps-Studio/vibe-driven-devtheir own version, first seen Mar 30, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- nek1987/auto-agent-harness★ 4same name
- Gaurav890/everything-agentic-engineering★ 1same name
- OpenOps-Studio/vibe-driven-dev★ 1same name
- feelthefusion/skill-starter-kit★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository32 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- brainstorm-reviewSkill
- brainstormSkill
- conceptSkill
- defineSkill
- financial-reviewSkill
- light-spec-reviseSkill
- light-specSkill
- linear-hygieneSkill
- linearSkill
- phase-planSkill
- pipekit-helpSkill
- pipekit-updateSkill
- pk-bugSkill
- pk-exitSkill
- pk-expressSkill
- pr-fixSkill
- prod-readySkill
- pr-security-reviewSkill
- release-changelogSkill
- repo-security-reviewSkill
- review-planSkill
- roadmap-createSkill
- roadmap-reviewSkill
- spec-preflightSkill
- startupSkill
- strategy-createSkill
- strategy-syncSkill
- sync-linearSkill
- tpmSkill
- verifySkill
- workSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
18 rules. 0 are common, 4 are distinctive, and 14 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
14
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 58
Read the file at Security categories (default resources/security-categories.md).
- Very specific: line 64
If pipekit/templates/security-categories.template.md exists (synced projects), copy it to <path>.
- Distinctive: line 66
Do not invent project specifics — leave clearly-marked placeholders.
- Distinctive: line 92
if available, the Linear issue's Scope / Dependencies (supplementary — the diff is authoritative).
- Very specific: line 94
If ISSUE is set and Linear is reachable, post a comment — pk issue comment "$ISSUE" "Starting security gate review — {YYYY-MM-DD}".
- Distinctive: line 121
If no category matches, skip to Step 5 with status PASS (no sensitive category touched) — no review subagents needed.
- Very specific: line 154
Write <Security gate report path>/Security_Gate_<ISSUE>_<YYYY-MM-DD>.md (omit <ISSUE>_ if none):
- Very specific: line 200
Write it on every PASS — including the no-category-matched instant PASS.
- Distinctive: line 201
Never: write it on FAIL, WARNINGS-you'd-block-on, or an indeterminate surface (Step 1's could-not-run).
- Very specific: line 206
If ISSUE resolved and Linear reachable: post a comment — the Summary + Classification blocks + the report path, piped into pk issue comment …
- Very specific: line 220
Evidence-based, adversarially verified: Every finding (and every all-clear) cites file:line.
- Very specific: line 222
Framework here, signals in the project: Never hardcode a repo's auth primitive, rate-limiter path, or PII tables — they live in the …
- Very specific: line 226
No Linear state transitions — pk ship owns Building → UAT.
- Very specific: line 227
No code modifications — sub-agents are read-only (Read, Bash, Grep, Glob).
- Very specific: line 228
No per-task running — that's /verify.
- Very specific: line 229
No whole-repo audit — that's /repo-security-review.
- Very specific: line 230
No blocking of its own — the skill writes the PASS sentinel.
- Very specific: line 231
No session-log writes — /pk-exit owns the session log.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
/security-gate
North star:
/verifyproves the code is correct./security-gateproves a security-sensitive change was actually reviewed for its category's failure modes before it ships to UAT. Most features touch no sensitive category and pass instantly; the ones that do don't reach UAT unreviewed.
You are a security engineer running the feature-scoped security gate at the ship seam — pk ship (the source state is Building for phase work, In Progress for ad-hoc work) → UAT. You first classify what the change touches; if it touches a sensitive category (auth, payments, user input, external APIs, file storage, PII), you run that category's checklist against the feature's diff and produce an evidence-based verdict. If it touches none, the gate is a clean PASS — that is the common case, and it must be cheap.
This is the portable framework. The concrete, project-specific signals (which paths/keywords mean "auth" in this repo, where the rate limiter lives, which tables hold PII, what the project's auth primitive is called) live in a project category-definitions file you read at runtime — never hardcode them here. A project that hasn't written one gets scaffolded a template and stops.
Three security surfaces, three jobs — don't conflate them:
| Skill | Scope | Cadence | This gate? |
/security-gate (this) | the feature's diff, category-triggered | once per feature, at Building → UAT | — |
/repo-security-review | the whole repo | periodic / pre-release | no |
/pr-security-review | a PR, antagonistic | on demand for risky PRs | no |
It is the per-feature counterpart to /verify: /verify runs every task and is fast; /security-gate runs once per feature and only does real work when a sensitive category is touched. It is advisory this release — it reports and comments, it does not move Linear state and does not block pk ship. See § Future.
Where this sits in the pipeline
pk next → pk branch → /work → /verify ──(Flag check F)──► pk ship (→ UAT) → …
│
└─ /verify auto-runs this gate when a
categories file exists, BEFORE auto-ship.
None matched → instant PASS (no flag).
Matched → category review → verdict flag./verify answers "is the code correct?"; /security-gate answers "if this change is security-sensitive, was it reviewed for its category's failure modes?" — before the change reaches UAT (pk ship).
It runs automatically, from inside /verify. The /work → /verify → pk ship rollover is uninterrupted (/verify auto-invokes pk ship on a clean Pass), so there is no human seam between /verify and pk ship for a standalone step to occupy. Instead /verify's Flag check F (v4.4.0) runs this gate: when the project has a Security categories file, /verify classifies the diff and, on a category match, runs the category review and surfaces the verdict as a human-decision flag — which pauses auto-ship for the user to RECONCILE (exactly like the migration self-review). A no-match adds no flag and the rollover proceeds. This skill is the canonical methodology Flag check F follows; you can also invoke /security-gate standalone at any time (re-running after a fix, or on a branch verified earlier).
Triggers
/security-gate— primary; resolves the feature issue + diff from the current branch/security-gate <ISSUE-ID>— explicit- "run the security gate" / "does this need security review?"
Config (read from method.config.md)
| Key | Purpose | Default |
| Security categories | Path to the project category-definitions file | resources/security-categories.md |
| Security gate report path | Where to write the gate report | Reports/ |
| Integration branch | The base the feature diffs against | dev |
| Project display name | Report header + comment | (project name) |
Linear is best-effort and advisory. Resolve the feature issue, post a comment with the verdict, and do not change its workflow state — pk ship owns the Building → UAT transition. Look up the issue via the project's Linear route — pk issue show "$ISSUE" --fields id,title,state,description (verb table: sop/Linear_SOP.md § Linear transport; the mcp route's equivalent is listed there); if Linear is unreachable, write the report anyway and tell the user to paste the verdict. Never hardcode Linear UUIDs.
Step 0 — Load the category-definitions file (gate)
Read the file at Security categories (default resources/security-categories.md).
If it doesn't exist, STOP and tell the user:
No security-category definitions found at
<path>. This skill is the framework; the project supplies the category signals.Then scaffold one and stop for the user to fill it in:
- If
pipekit/templates/security-categories.template.mdexists (synced projects), copy it to<path>. - Otherwise create
<path>directly with the template's sections — one per category (Auth, Payments, User input, External APIs, File storage, PII), each with the project's path globs, keywords, and validation specifics, plus a Not applicable section for categories the project genuinely has none of (e.g. "Payments — no billing in this product"). - Do not invent project specifics — leave clearly-marked placeholders.
- If
The definitions file is the substance; this skill is the discipline + report shape.
Step 1 — Resolve the feature and the changed surface
CURRENT=$(git branch --show-current)
ISSUE="${1:-}"
[ -z "$ISSUE" ] && ISSUE=$(echo "$CURRENT" | grep -oE '[A-Z]+-[0-9]+' | head -1)
BASE=$(pk config "Integration branch" "dev") # match /verify + /work; falls to the repo's default branch
# Changed surface = this feature's COMMITTED diff against the integration branch
# (three-dot: changes on the branch only, not base churn). Fetch first so a stale or
# missing origin ref doesn't silently narrow the diff.
git fetch -q origin "$BASE" 2>/dev/null || true # best-effort; worktrees share the object store
CHANGED=$(git diff --name-only "origin/$BASE...HEAD" 2>/dev/null)
[ -z "$CHANGED" ] && CHANGED=$(git diff --name-only "$BASE...HEAD" 2>/dev/null)
CHANGED=$(printf '%s\n' "$CHANGED" | grep -v '^Reports/') # exclude the gate's own past reportsFail safe on an indeterminate surface. If CHANGED is empty after both committed-diff attempts, do not classify it as "no sensitive category" — the surface couldn't be determined (stale/missing origin/$BASE, detached HEAD, or the work isn't committed yet). Stop and tell the user: "Could not determine the changed surface against <BASE> — commit the work and ensure origin/<BASE> is fetched, then re-run." An empty diff is a could-not-run, not a PASS — a silent miss here ships an unreviewed sensitive change. (Do not fall back to git diff HEAD working-tree changes: after /work commits, that is empty and would mask a fully-committed sensitive feature as a clean pass.)
Gather, as the classification input:
- the changed file list (above) — the ground truth of what this feature touches,
- the diff hunks for those files (so the classifier reads the actual code, not just paths),
- if available, the Linear issue's Scope / Dependencies (supplementary — the diff is authoritative).
If ISSUE is set and Linear is reachable, post a comment — pk issue comment "$ISSUE" "Starting security gate review — {YYYY-MM-DD}". Do not change the issue's state. If ISSUE can't be resolved, proceed anyway and skip the comment.
Print one line:
Security gate: <ISSUE | (no issue)> · Base: <BASE> · Changed files: <n> · Defs: <path>Step 2 — Classify the change (sub-agent, evidence-based)
Spawn a read-only classifier sub-agent (subagent_type: "general-purpose", execution tier per method.config.md § Model Policy — default sonnet, effort medium — allowed-tools: Read, Bash, Grep, Glob). Give it the changed file list, the diff, and the project's category definitions. It returns, for each of the six categories, matched / not-matched with evidence (file:line, the matched keyword/glob, or the spec line).
Classify from opened files, never from a filename alone. A path under auth/ that only changed a comment is not an auth change; a payments string in a test fixture is not a payments change. The classifier opens the cited hunk and confirms the change actually exercises the category. Err toward matching when genuinely ambiguous — a false match costs one extra checklist run; a false miss ships an unreviewed sensitive change.
The six categories:
| Category | Signals (project file refines these) |
| Auth | login, session, password, OAuth/JWT, token issuance, requireAuth/guard changes, RLS policy edits |
| Payments | Stripe/billing/checkout/subscription/refund, webhook handlers for a payment provider, money math |
| User input | forms, file uploads, search, comments — any user-supplied string that reaches the DB, a query, a template, or the shell |
| External APIs | webhooks, third-party integrations, SSO callbacks, outbound calls with a secret |
| File storage | S3 / Supabase storage / any bucket, upload/download URLs, public-bucket changes |
| PII | emails, names, addresses, phone numbers, anything GDPR-relevant — new columns, logs, or exports of personal data |
Account for all six categories — silence is not a pass. The classifier must return a verdict for every category as one of: matched (run the checklist), N/A (the definitions file lists it under Not applicable, with the reason), or not-matched (evidence-cited: the diff touches nothing in this category). If a category has no signals defined in the project file and isn't listed N/A, evaluate it against the generic signal table above and mark it not-matched (no project signals defined — generic-only) so an incomplete definitions file is visible in the report, never an invisible pass.
If no category matches, skip to Step 5 with status PASS (no sensitive category touched) — no review subagents needed. (This is distinct from the indeterminate surface in Step 1, which is a could-not-run, not a PASS.)
Step 3 — Per-category review (parallel sub-agents)
For each matched category, spawn a read-only review sub-agent (general-purpose, execution tier per method.config.md § Model Policy — default sonnet, effort medium — allowed-tools: Read, Bash, Grep, Glob) scoped to the feature diff. Run them in parallel. Each runs its category checklist and returns findings with file:line evidence, a severity (Critical/High/Medium/Low) and a confidence — and, where the category checklist is clean, an explicit cited "verified, no issue" verdict (coverage before filtering, per /repo-security-review).
Per-category checklist (the project file refines the specifics):
| Category | What the review verifies |
| Auth | session fixation; token leakage (URL/log/client); password handling (hashing, never logged); login path rate-limited against brute force; authorization checked at the query, not just the UI |
| Payments | webhook signature verification; idempotency keys on charge/refund; amounts server-computed not client-trusted; PII/card data redacted in logs; refund/credit paths authorized |
| User input | input sanitized at the boundary; parameterized queries (no string-built SQL); file-type/size validation on uploads; no eval/template injection; output encoded |
| External APIs | secrets from the vault not source; request signing / mutual auth where required; error surface contained (no upstream error body leaked to the client); timeouts + retry bounds |
| File storage | signed URLs (not public objects) for private data; bucket ACLs least-privilege; mime-type + size validation on upload; no path traversal in object keys |
| PII | storage location appropriate (not a public bucket / client-readable table); retention/deletion pathway exists; not emitted to logs or analytics; access authorized at the query |
Skip any category the definitions file lists under Not applicable (record it as N/A — <reason>, not as a pass).
Step 4 — Adversarial verification, reconcile, rate
Run an adversarial pass over the candidate findings (mirroring /repo-security-review and sop/Completion_Claims_SOP.md § 3. DOUBT): for each finding, try to refute it — open the cited code and check it's actually reachable and exploitable given auth, RLS, and the real call sites. Mark each confirmed / refuted / needs-info. Keep refuted items in an appendix with the reason; only confirmed and needs-info feed the verdict.
Headline status (a single read-only gate run can't iterate-to-fix, so the rubric is on confirmed severity, not an "addressed" loop):
- FAIL — any confirmed Critical or High finding.
- WARNINGS — only Medium/Low confirmed (or needs-info the user judged acceptable).
- PASS — no category matched, OR every matched category's checklist clean (no confirmed Critical/High).
This gate is hard as of v4.17.0: on a project with a Security categories file, pk ship refuses without a HEAD-matching PASS sentinel (Step 5 writes it). A FAIL writes no sentinel — close the findings and re-run the gate; the escape hatches are pk ship --force-secgate (logs a Linear audit comment) or PK_SECGATE_BYPASS=1 (emergency; logs to Logs/SecurityGate/bypass.log). A plain pk ship --force waives only the verify gate, not this one (v4.20.0).
Step 5 — Generate the report + sentinel
Write <Security gate report path>/Security_Gate_<ISSUE>_<YYYY-MM-DD>.md (omit <ISSUE>_ if none):
# Security Gate — {ISSUE} · {Project display name}
**Base:** {BASE} · **Date:** {YYYY-MM-DD} · **Changed files:** {n}
## Summary
- **Status:** PASS / FAIL / WARNINGS
- **Categories matched:** {list, or "none — no sensitive category touched"}
- **Confirmed findings:** Critical {n} · High {n} · Medium {n} · Low {n}
## Classification
| Category | Matched | Evidence |
|----------|---------|----------|
| Auth | yes/no/N/A | `<file>:<line>` / keyword |
| Payments | … | … |
| User input | … | … |
| External APIs | … | … |
| File storage | … | … |
| PII | … | … |
## Findings (confirmed + needs-info)
### Critical
{list with file:line, or "None"}
### High
{list, or "None"}
### Medium / Low
{list, or "None"}
## Refuted (appendix)
{finding + why refuted, or "None"}
## Next actions
{specific, ordered — what to fix before pk ship, or "Clear to ship"}On PASS — and only on PASS — also write the sentinel pk ship reads (v4.17.0 hard gate):
_gate_head=$(git rev-parse HEAD)
_gate_dir="Logs/SecurityGate/$(date +%Y%m%d)/${ISSUE}"
mkdir -p "$_gate_dir"
printf '# secgate-complete\n\nissue: %s\nstatus: PASS\nsha: %s\ncategories: %s\n' \
"$ISSUE" "$_gate_head" "${MATCHED_CATEGORIES:-none}" > "$_gate_dir/secgate-complete.md"- Write it on every PASS — including the no-category-matched instant PASS.
pk shipcannot classify the diff itself; the sentinel is how it knows the classifier ran and came back clean at this HEAD. - Never write it on FAIL, WARNINGS-you'd-block-on, or an indeterminate surface (Step 1's could-not-run). A missing sentinel is the blocking signal.
- The
sha:is the gated HEAD — any commit after the gate (including a fix for a finding) correctly invalidates it; re-run the gate on the new HEAD.
Step 6 — Post the Linear comment + present to user
- If
ISSUEresolved and Linear reachable: post a comment — the Summary + Classification blocks + the report path, piped intopk issue comment "$ISSUE" -. Do not change state. - Present to the user: overall status, matched categories, confirmed findings by severity, and the explicit next-action —
Clear to shipif PASS, orHold — close {n} finding(s) before pk shipif FAIL.
Severity rubric
- Critical — directly exploitable: auth bypass, unverified payment webhook, SQL injection from user input, a private object served publicly, PII in a public store. Ship-stopping.
- High — exploitable under conditions: missing idempotency on a charge path, login with no brute-force limit, an upstream error body leaked to the client, a token in a log.
- Medium — defense-in-depth gap with real impact (validation present but incomplete; redaction partial).
- Low — minor hardening / observability gap.
Key principles
- Classify before you review. The gate's first job is to decide whether a review is owed. Most features owe none — keep that path instant. The expensive checklist only runs on a confirmed category match.
- Feature-scoped, not repo-wide. This reviews this diff for its categories —
/repo-security-reviewowns the whole-repo audit. Don't re-audit the world here. - Evidence-based, adversarially verified. Every finding (and every all-clear) cites
file:line; every candidate is refutation-tested before it counts. No vibes. - Hard-gated, once per feature. It reports, comments, and (on PASS) writes the sentinel
pk shiprequires; it does not transition Linear state and does not run per task. - Framework here, signals in the project. Never hardcode a repo's auth primitive, rate-limiter path, or PII tables — they live in the definitions file and
method.config.md.
What this skill does NOT do
- No Linear state transitions —
pk shipowns Building → UAT; this only comments. - No code modifications — sub-agents are read-only (
Read, Bash, Grep, Glob). - No per-task running — that's
/verify. This runs once, beforepk ship. - No whole-repo audit — that's
/repo-security-review. This is the feature diff. - No blocking of its own — the skill writes the PASS sentinel;
pk shipdoes the refusing (v4.17.0). The skill never aborts a ship itself. - No session-log writes —
/pk-exitowns the session log.
Overlap with /prod-ready (rate limiting)
Rate limiting appears in both gates, by design, at different layers:
/security-gateverifies this feature's sensitive path is correct in isolation — e.g. the login route is itself rate-limited against brute force (an Auth-category check)./prod-readyverifies the app throttles public traffic at all — that a new public endpoint is covered by the platform's rate-limit middleware.
Same word, different concern. Both run; neither subsumes the other. (Documented from the other side in sop/Production_Readiness_SOP.md § Overlap.)
The hard gate (shipped v4.17.0)
The fast-follow documented since v4.4.0 is built: this skill writes a sha-matched sentinel on PASS (Logs/SecurityGate/<date>/<issue>/secgate-complete.md, mirroring /verify's verify-complete.md), and pk ship refuses to push / open the PR when the project's Security categories file exists and no sentinel matches HEAD. Escapes: pk ship --force-secgate (logs a Linear audit comment) or PK_SECGATE_BYPASS=1 (logs to Logs/SecurityGate/bypass.log); pk ship --force waives only the verify gate (v4.20.0). Projects without a categories file are unaffected — the same file that arms this skill arms the ship gate, so opting in is one file.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

