Secure coding
Security review and secure design for any application on any stack. Use when writing or reviewing auth, multi-tenancy, database access, file uploads, CSP and headers, outbound HTTP, CI/CD pipelines, secrets handling, or dependency updates — and whenever the user asks about vulnerabilities, threat models, hardening, penetration findings, "is this secure", or shipping to real users for the first time.From its SKILL.md
npx -y skills add KirtanUgreja/shipsafe --skill secure-codingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.8 KB, as published. Nobody here has run it
Secure coding
The threat model, and the controls that follow from it. Structural controls over filters, everywhere the choice exists.
When this applies
Load this when a change touches anything on the left, and read the reference on the right before writing the code — not after.
| The change touches | Read |
|---|---|
| Who you are defending against, and where the boundaries are | references/threat-model.md |
| Shipping fast with AI assistance, first outside user | references/common-failures.md |
| Login, sessions, tenants, row-level security, PII, retention | references/data-and-tenancy.md |
| Rendered output, CSP, uploads, CORS, custom domains, caching | references/web-and-content.md |
| Outbound fetches, headless browsers, queues, quotas, failure paths | references/infrastructure.md |
| Adding, updating, or auditing a dependency or framework | references/supply-chain.md |
| CI, deploy credentials, infrastructure state, published packages | references/build-and-deploy.md |
| Disclosure, incident response, patch cadence, the release gate | references/operations.md |
| Writing the tests that keep a control from being removed later | references/testing.md |
| Anchoring to a published taxonomy | references/owasp-mapping.md |
If the application sends untrusted content to a language model, or renders, executes, or acts on model output, also load the ai-app-security skill.
The non-negotiables
Twelve lines. If a change violates one, the change is wrong, not the rule.
- Prefer a structural control over a filter. A blocklist is a claim you enumerated every bad input. Prefer an architecture where the attack has nowhere to land: network isolation over URL parsing, a schema with no dangerous field over detecting dangerous output, an absent role over a correct policy.
- Availability degrades open. Authorization degrades closed. A viewer getting a placeholder has been given nothing. A request proceeding because the quota check timed out has.
- Authorization is enforced where the data is read, never in middleware, a route guard, or a UI affordance alone. Those are optimizations.
- Every privileged query carries its own explicit filter. Where a role
bypasses row-level security, the
whereclause is the access control. - No credential the browser holds is load-bearing. Assume everything in a client bundle is public, including ids, endpoints, and flags.
- Every external input is parsed into a known shape at entry, exactly
once. Never deserialize into arbitrary classes — no
pickle, noyaml.load, no unrestricted object reconstruction. - Untrusted bytes are parsed in the least privileged process you have. A parser CVE is a different severity depending on what the container holds.
- Secrets are server-side, out of logs, out of error reports, out of the build output — verified by grepping for key shapes, not known prefixes.
- Deploy credentials are scoped to one repository, one purpose, one environment. No wildcard, no omitted subject condition.
- The dependency you install is the delivery mechanism. Lockfile committed, frozen installs, install scripts off, release-age gate on — in every ecosystem the project uses, not just the one you inspect.
- Personal data is classified when the column is created, not when someone asks.
- A control without a test is a control that will be removed. Prove the failure case fails — by making the check throw, not by reasoning about it.
Rules 2, 3, and 4 describe bugs that look like correct code in review. Read
them again before approving a catch.
Structural or recognition-based
Sort every control you write into one of these columns. The left column holds against something that never gets bored; the right one degrades.
| Structural — holds | Recognition-based — degrades |
|---|---|
| A schema with no field a command fits in | Scanning output for dangerous strings |
| A sandbox with no same-origin, so there is no storage or DOM | Reviewing generated code for network calls |
| A worker that cannot route to your network or metadata service | An SSRF blocklist |
| An anonymous role with zero grants | Per-table policies written by hand |
| A link whose destination must already exist in your own data | Scheme allowlisting on that link |
| Content served where script cannot execute at all | The sanitizer that cleaned it |
The right column is the cheap second layer and it is worth having. It is not the control. When a bypass happens, ask which structural control was missing — not which pattern to add.
Maturity tiers
Building controls in the wrong order wastes effort on threats that do not exist yet. These are a default, not a fact: a prototype holding health records skips straight to tier 3. Adjust to your situation, deliberately.
| Tier | You are here when | What becomes necessary |
|---|---|---|
| 1 — Prototype | No real users, no real data, one operator | Patched framework and parsers · secrets out of the client bundle · fail closed on anything that grants · a CI pipeline that cannot be used to skip both |
| 2 — First real users | Someone who is not you can log in | Authentication by a maintained library · rate limits on auth · upload handling · output escaping and CSP · a published disclosure channel · deletion that works |
| 3 — Multi-tenant or money | Two customers, or a payment | Tenant isolation with a test suite written before the second account exists · server-side entitlement checks · quotas and a spend kill switch · SSRF isolation for anything that fetches · PII retention and cascade delete |
| 4 — Enterprise | Procurement asks for a document | Published subprocessor list · incident-response runbook with named owners · scoped CI credentials with reviewers · evidence the gates run |
Three controls belong to tier 1 and never move: keeping what parses hostile bytes patched, failing closed, and a build pipeline that cannot bypass either. None waits for a feature to arrive.
What this skill will not tell you
Nothing here makes an application unhackable, and a document claiming otherwise would make you less safe. The goal is that the attacks which succeed produce outcomes you can live with, and that the ones you cannot live with have no path at all. A threat model claiming completeness is the one that gets believed and then breached.