From ship-it-ops
Apply application-security principles (auth, input validation, injection, XSS/output encoding, CSRF/origin, crypto, secrets, supply chain, PII/logging, resource exhaustion, path traversal, deserialization/SSRF) when writing or reviewing security-sensitive Python, TypeScript/JavaScript, or Java code. Invoke explicitly for security reviews or as the delegation target from ship-reviewed-prs SC persona. Do not invoke for pure styling, shell scripts, one-off prototypes, or non-security code review (use ship-clean-code).
How this skill is triggered — by the user, by Claude, or both
Slash command
/ship-it-ops:ship-secure-codeThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill applies application-security principles to help you write code that resists the OWASP Top 10 + CWE Top 25 attack classes and to review existing code for the same. It operates in **review mode** only — it does not auto-remediate vulnerable code. (Auto-remediation will be added in a future version once the rule set is proven; producing patches for security bugs without a human in the l...
examples/fix-example.mdexamples/review-example.mdlang-java.mdlang-python.mdlang-typescript.mdoverrides.example.mdreference-categories.mdreference.mdtests/README.mdtests/fixture-1-xss/expected-output.mdtests/fixture-1-xss/input.mdtests/fixture-2-sqli/expected-output.mdtests/fixture-2-sqli/input.mdtests/fixture-3-missing-authz/expected-output.mdtests/fixture-3-missing-authz/input.mdThis skill applies application-security principles to help you write code that resists the OWASP Top 10 + CWE Top 25 attack classes and to review existing code for the same. It operates in review mode only — it does not auto-remediate vulnerable code. (Auto-remediation will be added in a future version once the rule set is proven; producing patches for security bugs without a human in the loop is a footgun.) Sibling skills handle non-security concerns: ship-clean-code (file quality), ship-tested-code (test design), ship-debugged-code (root cause), ship-reviewed-prs (PR-level orchestration).
Start with these 3 rules and internalize them before learning the rest:
"javascript:" will be defeated; one that requires ^https?:// won't.The detailed reference files (reference.md, reference-categories.md) assume familiarity with OWASP Top 10 and the language-specific framework conventions.
/ship-secure-code <path|file>, "security review", "secure code review", "audit this for security", or invocation from ship-reviewed-prs SC delegation.If asked to write security-sensitive code (e.g., new auth handler, new crypto wrapper, new input validator), the skill does not apply directly — use ship-clean-code with security context, then run this skill to review the output. We separate write from review intentionally: a single mode that does both tends to produce code that looks defended (long, lots of try/catch, lots of validation) without actually being defended.
These 12 rules apply to ALL security review:
Identify the trust boundary before reading the code. Anything crossing it (HTTP input, file upload, queue message, env var, external API response, cookie, postMessage payload, IPC) is untrusted. All findings hang off this map. If the code under review doesn't make the boundary explicit, that's the first finding.
Validators must enumerate what's allowed and reject everything else. A regex that excludes known-bad input is a denylist; rewrite or reject.
Input validation rejects malformed data. Output encoding makes data safe for the context it lands in (HTML, SQL, shell, header, URL, log). Both are required. If only input validation is present, the code is one input-channel away from compromise.
A user being logged in (AuthN) doesn't mean they have permission for the resource (AuthZ). IDOR/BOLA is the most common security bug: an endpoint that requires login but doesn't check whether the requested resource belongs to the requesting user. SEC1 fires on both.
Choose libraries and configurations that are secure by default. Bcrypt over SHA-256, parameterized queries over string concat, secrets.token_urlsafe over Math.random(). The skill flags code that opts out of secure defaults without justification.
Even when a primary control is present, an adjacent layer must also work. CSP plus output encoding plus input validation; auth middleware plus per-resource AuthZ plus rate limiting. The skill checks the secondary layer explicitly.
Every component (process, service account, IAM role, database user, container) runs with the minimum permissions needed. Findings on over-privileged access live under SEC1.
On error, deny access. A try/catch that swallows an auth failure and continues is worse than no try/catch. Same for crypto verify failures, signature checks, allowlist lookups.
Use the language's standard library or a vetted package (bcrypt, argon2, NaCl, Web Crypto API). Never roll your own cipher, hash construction, or MAC. The skill flags any code under crypto/ or security/ that constructs primitives by hand.
Hardcoded credentials, API keys, tokens, certificates, and connection strings are categorically wrong. SEC7 fires on any literal that matches secret patterns regardless of context.
SECn.1 findings (must-fix) block merge. SECn.2 findings (should-fix) ship with mitigation plans. SECn.3-5 are advisory. The skill computes tier from the finding ID and the surrounding context (user-controlled input vs. trusted-internal); no LLM negotiation.
Include a Confidence section naming what was reviewed, what was not (binaries, generated code, vendored deps), and what's the residual risk. A confident "must fix" pairs with the specific data-flow path that drove the finding.
| ID | Label | Covers | Tier-1 examples (must-fix) |
|---|---|---|---|
| SEC1 | AUTH | Missing AuthN; missing AuthZ on routes; IDOR (missing tenant/owner checks); broken session handling; over-privileged service accounts | Admin route with no auth middleware; Model.findById(id) without tenant filter; jwt.decode(token, verify=False) |
| SEC2 | INPUT-VALIDATION | Missing parameterization, allowlist patterns, schema validation at boundary | HTTP route accepting request.body without zod.parse / Pydantic / JSR-380 |
| SEC3 | INJECTION | SQL, NoSQL, OS command, LDAP, XPath, log, header, template (SSTI), prototype pollution | db.query(f"... {id}"); subprocess.run(cmd, shell=True); Object.assign({}, untrusted) |
| SEC4 | XSS / OUTPUT-ENCODING | dangerouslySetInnerHTML; raw HTML in children/slot; javascript: URLs; unsafe href; unescaped Handlebars/EJS/Jinja | <div dangerouslySetInnerHTML={{__html: userInput}} />; {{{userBio}}} |
| SEC5 | CSRF / ORIGIN | Missing CSRF token; postMessage without origin check; iframe without sandbox; weak CSP; CORS * + credentials: true | window.addEventListener('message', handler) with no event.origin check; app.use(cors({origin: '*', credentials: true})) |
| SEC6 | CRYPTO | MD5/SHA1 for passwords; deprecated ciphers; algorithms: ['none'] JWT; Math.random() for security; hardcoded IV; ECB mode | hashlib.md5(password); Cipher.getInstance("AES") (ECB default) |
| SEC7 | SECRETS | Hardcoded keys; committed .env; token in URL; runtime/lockfile leakage | const API_KEY = "sk_live_..."; .env not gitignored; requests.get(f"...?token={TOKEN}") |
| SEC8 | SUPPLY-CHAIN | New dep age/downloads; postinstall scripts; non-registry lockfile sources; peer-dep drift; typosquats | New pkg added 2 weeks ago with postinstall; request vs requests typo |
| SEC9 | PII / LOGGING | console.log(user); unredacted request logging; analytics with PII; error logs leaking response bodies | logger.info("user: %s", user) where user includes email/SSN |
| SEC10 | RESOURCE-EXHAUSTION | ReDoS (nested-quantifier regex); unbounded Promise.all; missing rate limits; missing body-size caps | /^(a+)+$/; Promise.all(items.map(fetch)) on untrusted list |
| SEC11 | PATH-TRAVERSAL / FILE-OPS | path.join with user input; ../ not validated; archive extraction (zip slip) | open(os.path.join(dir, req.body.filename)); tar.extractall() without filter |
| SEC12 | DESERIALIZATION / SSRF | pickle.loads/eval/Function; fetch(userControlledUrl); open redirect; XXE | pickle.loads(req.body); requests.get(req.GET['url']) without allowlist |
Full per-category rubric — antipatterns, canonical fixes, false-positive notes, cross-references — lives in reference-categories.md.
Each finding ID has a tier sub-tag computed from the data-flow context:
The full tier definitions per finding ID are in reference-categories.md.
| State | Decision |
|---|---|
| Any unsuppressed *.1 (must-fix) finding | REQUEST_CHANGES |
| Only *.2 findings | COMMENT |
| Only .3-.5 findings | COMMENT |
| Zero findings | APPROVE (or NO_FINDINGS when run standalone) |
ship-secure-code does not have its own submission semantics — when run standalone, it produces a structured report. When run as the delegation target from ship-reviewed-prs, the parent skill maps the report to its own decision matrix (SECn.1 → SC1-AUTH-MISSING/SC2-INJECTION/etc. severity).
## Security Review: [scope]
### Confidence
<2-4 sentences: trust boundary identified, what was reviewed, what was not
reviewed (binaries, vendored deps, generated code), residual risk.>
### Critical (must fix before merge)
- **[SEC3.1-INJECTION] api/users.ts:42**: <data flow path: source → sink>. → <fix>.
- **[SEC1.1-AUTH-MISSING-AUTHZ] api/orders.ts:18**: <description>. → <fix>.
### Important (should fix)
- **[SEC10.2-REDOS] utils/parser.ts:55**: <regex>. → <fix>.
### Advisory (defense in depth)
- **[SEC5.4-MISSING-CSP] middleware.ts:8**: <description>. → <fix>.
### What's Good
- <substantive observation about a defense done well — not boilerplate>
Rules for the output:
SECn.t-LABEL) — tier is part of the ID, not a separate field.password: "test1234" in a *.test.* file under the test bucket isn't SEC7 if the variable name signals test usage and the file path is a test path.YOUR_API_KEY_HERE in *.md is not SEC7.scripts/, tools/, dev/ that explicitly says "dev only" gets advisory-tier findings only — no blocking findings on convenience scripts.crypto.createHash('md5') on a file content for cache-key purposes is not SEC6. Check the surrounding semantic.If the code under review already contains an old vulnerability not introduced by this PR/diff:
(pre-existing) marker.Newly-introduced vulnerabilities (this PR adds them) are full-tier per the matrix.
Before applying security rules, check for override files in this order:
overrides.md next to this SKILL.md (team-wide overrides bundled with the skill).claude/ship-secure-code-overrides.md in the user's project root (project-specific overrides)Use overrides for:
A template is at overrides.example.md.
Phased rollout recommended:
Track: tier-1 findings per PR (should trend toward zero); false-positive rate per category (if any category fires noisily, demote it via overrides).
ship-reviewed-prs — PR-level orchestrator. Its SC persona delegates to this skill for depth. When working a PR end-to-end, run ship-reviewed-prs first; it will tell you which files to run this skill on.ship-clean-code — File-level code-quality review (naming, SRP, error handling). P2-SEC is its surface-level security check; this skill is the depth target. Run clean-code first to fix structure, then run secure-code to find vulnerabilities.ship-tested-code — Test design. Security fixes need regression tests; secure-code reviews flag missing tests as advisory, then defer to tested-code.ship-debugged-code — Use when a security bug is being fixed and you want a regression test designed around the root cause.For deeper analysis, load supporting reference files alongside this SKILL.md:
reference.md — Methodology, sources (OWASP Top 10, ASVS, CWE), cross-cutting principles, anti-overlap with sibling skills.reference-categories.md — SEC1-SEC12 deep rubric: antipatterns, canonical fixes, false-positive notes, cross-references.lang-python.md, lang-typescript.md, lang-java.md — Language-specific patterns for each SEC category.examples/review-example.md — End-to-end review output sample on a vulnerable Next.js + Express app.examples/fix-example.md — Walkthrough of a single finding from identification through fix and test.tests/ — Self-test fixtures (sample vulnerable input + expected report).Paths are relative to this SKILL.md. Load on-demand when doing thorough reviews or when the user asks for detailed guidance on a specific topic.
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub ship-it-ops/ship-code --plugin ship-secure-code