IN

itsrealaj/never-get-hacked-skill

Developer tools
10 stars Quality 57 Trend 26MIT

NEVER get hacked again while vibecoding!

Overview

End-to-end web app security for people who build by prompting. It meets a project wherever it actually is — idea, half-built, live with users, or on fire — audits what exists on disk instead of what the user says exists, and returns a prioritised plan with the fixes written as working code. Never answer a security question from priors. A builder's description of their app is unreliable — not because they lie, but because they don't know which details matter, and because AI-generated code contains things they never saw written. A finding you verified beats ten you predicted. The corollary is the part most tools get wrong: You can say a specific gate passed. You can say a specific check found nothing. Those are different claims, and the difference is the whole point. Stage and architecture lane, both read off the filesystem. The skill does not ask — self-reports are systematically optimistic, and the files know. If security/data-map.

README

End-to-end web app security for people who build by prompting.

It meets a project wherever it actually is — idea, half-built, live with users, or on fire — audits what exists on disk instead of what the user says exists, and returns a prioritised plan with the fixes written as working code.


The one rule

Audit before you advise. Every time. No exceptions.

Never answer a security question from priors. A builder’s description of their app is unreliable — not because they lie, but because they don’t know which details matter, and because AI-generated code contains things they never saw written. A finding you verified beats ten you predicted.

The corollary is the part most tools get wrong:

Never tell the user they are secure.

You can say a specific gate passed. You can say a specific check found nothing. Those are different claims, and the difference is the whole point.


How it works

1. Locate the project. Stage and architecture lane, both read off the filesystem. The skill does not ask — self-reports are systematically optimistic, and the files know. If security/data-map.yaml is absent, Stage 0 is incomplete no matter how much code exists.

2. Audit, cheapest tier first. Two offline scripts and one consent-gated live probe, then the differential authorization test that no scanner can do. It stops and reports early if Tier 0 finds something critical — a user with an open database does not need Tier 3 style notes.

3. Filter before reporting. Every draft finding runs through a false-positive catalog first. This is not optional, and it is the step that decides whether the report is useful or noise.

4. Report by chain, not by severity column. Every real breach in the research record is a chain. The solo-founder threat model has no SOC, no WAF, and a time-to-detect of whenever they next look, so composite risk behaves like the product of exploitabilities, not the max.

The stage ladder

Eleven stages, each with a mechanically verifiable exit gate, plus an out-of-band incident track. Stages 0–2 are cheap and are the ones that get skipped.

Stage Name Exit gate proves
0 Data decisions Three files exist; no field lacks a reader
1 Accounts Hardware keys on five root accounts; agent scoped
2 Scaffold No secret in git or bundle; lockfile committed; install scripts off
3 Data model External curl with the public key returns nothing
4 Features Differential two-account test passes on every route
5 Pre-deploy Build artifact grepped; headers set; debug routes gone
6 First URL Previews gated; .env / .git / _src 404; DNS clean
7 Real users Rate limits with shared state; spend caps with actions
8 Money Webhook signature verified; entitlement only from the webhook
9 Steady state Dependabot + cooldown + weekly slot + tested restore
10 Decommission DNS first, then resources, then revoke; nothing resolves
E Incident Evidence preserved before anything is touched

Stage 3 is bolded because that is where almost every published breach in the research record actually lives. None of them needed an exploit. They needed a curl.

The three architecture lanes

Getting the lane wrong is the single worst failure mode of this skill, so it is decided before any advice is given.

Lane Shape Consequence
A Server-rendered framework, managed auth, Postgres, server-only DAL Standard defenses apply
B Client-only SPA talking straight to Supabase/Firebase, no server module Most standard defenses are inexpressible, not merely missing
C Python backend — FastAPI, Django, Flask A different footgun set entirely

Telling a Lane B user to add authorization to your API route or validate with Zod on the server is advice they cannot execute. There is no server. It reads as competent and it is useless. Lane B gets its own impossible-vs-substitute table, plus the seven observable triggers that mean the client-only architecture has to end.


Install

git clone https://github.com/ItsRealAJ/Never-Get-Hacked-Skill.git
cp -r Never-Get-Hacked-Skill/never-get-hacked ~/.claude/skills/never-get-hacked

For one project instead of every project, copy it into .claude/skills/never-get-hacked inside the repo. Keep the directory name — it must match the name: field in SKILL.md.

Invoke it with /never-get-hacked, or let it trigger on its own. It fires on is my app secure, review my security, I’m about to deploy, I think I leaked a key, I think I’ve been hacked, and on adding auth, payments, file upload, or AI features.


The scripts

Three bash scripts. curl for the live probe, nothing else — no install step, no dependencies.

scripts/audit-repo.sh [path]                 # read-only, no network, safe on any repo
scripts/audit-bundle.sh [build-dir]          # what the attacker actually reads
scripts/probe-live.sh  --i-own-this     # only against a target you own

Each takes --brief for one line per finding when output budget is the constraint. Nothing is skipped in brief mode — same checks, same finding IDs, same counts; re-run without it to get every word back.

audit-repo.sh — static, offline, safe on a stranger’s repo

Read-only by construction: no writes to the target, no network, no execution of anything in the repo, and its only temp directory is removed on exit. Always exits 0 — it is an informational tool, not a gate.

never-get-hacked :: audit-repo.sh v1.0 (--brief)
target   : ./demo-app  |  4 files  |  matcher grep  |  READ-ONLY, no network

=== INVENTORY
  stack        : supabase
  lane         : A — server tier present: src/app/api
  git          : no repo — SECRET-05 history checks skipped

=== TIER 0 — fifteen-minute triage
  [CRIT] BAAS-04 service_role referenced — check every one of these is server-only
         :: ./.env:3 ./src/app/api/orders/route.ts:2
  [CRIT] BAAS-04 Bundled JWT decodes to role=service_role — this key carries BYPASSRLS
         :: ./.env:3 -> role=service_role
  [HIGH] AUTHZ-10 No test directory at all — there is no differential authorization test
  [ ok ] no Firebase rules files in the repo

=== TIER 1 — secrets and the client bundle
  [CRIT] SECRET-01 A public build-time env prefix in front of a secret-sounding name :: ./.env:4
  [HIGH] SECRET-04 Production source maps enabled in the build config :: ./next.config.js:1

  [NOTE] NOTVULN-01..04  Public-by-design keys are present. THIS IS NOT A FINDING.
        Governing rule: flag the MISSING CONTROL, not the visible key. Reporting one of
        these as a 'critical leak' is the fastest way to make the real finding get ignored.

=== SUMMARY
  CRITICAL 4   HIGH 5   MEDIUM 2   notes 1
  11 findings across 4 files. Each is a CHECK that fired, not a confirmed exploit.
  Order fixes by CHAIN, not by the severity column.

Then it prints the section that most tools leave out:

=== WHAT THIS SCRIPT DID NOT CHECK
  1. AUTHORIZATION — static analysis structurally cannot find broken access control.
     THE #1 FAILURE CLASS AND THIS SCRIPT IS BLIND TO IT.
  2. THE LIVE DEPLOYMENT — no network request was made.
  3. THE SHIPPED BUNDLE — a secret absent from source can be inlined into the build.
  4. BUSINESS LOGIC — no grep expresses "the user should not be able to do this."
  5. THE DATABASE ITSELF — RLS state and grants live in Postgres; migrations show intent.
  6. GIT HISTORY — working tree only. A secret removed later is still in history.
  7. DEPENDENCY VULNERABILITIES — no CVE database consulted, nothing installed.
  8. ACCOUNTS AND BILLING — MFA, spend caps with an ACTION not an alert, restore tests.

Silence reads as a clean bill of health, so the skill never leaves silence where a gap exists.

audit-bundle.sh — the artifact, not the source

Audits built output, because that is what the attacker downloads. A secret absent from your source can still be inlined into the bundle at build time.

It decodes JWTs rather than grepping for them, which is the entire trick: the anon key and the service_role key are the same shape, the same length, and differ only by one claim inside the payload. Grepping for the literal string service_role hits harmless SDK source and misses the actual token. The role claim is the check.

Exit Meaning
0 No critical finding
2 A real secret is in browser-reachable output
3 No build output found — nothing was audited

probe-live.sh — external recon against your own deployment

Runs the attacker’s own recon pipeline against your site, in the order an attacker would, so you find what they would find first.

It refuses to run without both a target and --i-own-this, and prints an authorisation banner before any request. Requests are strictly sequential with short timeouts, so it never floods and cannot be mistaken for an attack on your own infrastructure.

Tier What runs
SAFE Surface mapping, header fingerprinting, exposed-path probing, bundle mining, CORS reflection. Passive reads, indistinguishable from normal traffic.
LOGGED The Next.js middleware-bypass check (CVE-2025-29927). No writes, but it appears in your access logs.
STAGING-ONLY Write probes and rate-limit flooding are not run. The script prints what they are and tells you to run them against staging.

What no scanner can do

Static analysis structurally cannot find broken access control. It requires knowing the intended policy, and no grep expresses this user should not be able to read that row. Since broken access control is the #1 real-world failure class, every scanner above is blind to the thing most likely to sink the app.

So the skill runs a differential test instead: two users, two tenants, replay every request as owner / other-tenant / anonymous, and diff the responses. references/authz-verification.md ships the harness, a Playwright policy matrix wired as a required CI check, a route-coverage inventory diff that catches the one route that missed the guard, and a pgTAP suite — because RLS tests lie by default, and a green pgTAP run can sit next to a leaking app if a service-role route bypasses the policy your SQL tests were checking.


The false-positive filter

The fastest way to make a security skill worthless is to tell someone their NEXT_PUBLIC_SUPABASE_ANON_KEY is a critical leak. It is not. It was never secret. And once you have cried wolf, the finding that actually matters gets ignored with it.

Governing rule: flag the missing control, not the visible key.

Thirteen catalogued not-findings, each paired with the control you must verify instead — publishable Supabase and Firebase keys, Stripe and Clerk pk_, PostHog and Sentry and Mapbox, schema enumeration, a JWT in the browser, dangerouslySetInnerHTML inside a real sanitizer, a CORS error in the console, low-severity dependency noise, a public repo. The fourteenth entry is the list of things the research deliberately does not claim.

Two traps that catch naive tooling in both directions:

  • A Gemini API key carries the same AIza prefix as a Firebase web key, and must never ship to a client. Check what the key is restricted to, not what it starts with.
  • An sb_secret_ key returning 401 to a browser User-Agent is a guardrail against accidents, not a mitigation — and equally, that 401 is not evidence the key is safe. curl sends no browser User-Agent.

The 15 killers

Ranked by what actually takes these apps down for this population, not by CVSS.


What’s in the repo

never-get-hacked/
├─ SKILL.md                              the always-loaded core: the rule and the four steps
├─ references/
│  ├─ stage-detection.md                 classify a project without interrogating the user
│  ├─ false-positives.md                 14 not-findings and the control each one requires
│  ├─ top-killers.md                     the 15, with exploitation chains
│  ├─ stack-and-lanes.md                 lane A/B/C, and the client-only ceiling
│  ├─ gates.md                           mechanically verifiable exit gate per stage
│  ├─ authz-verification.md              the differential harness, CI matrix, pgTAP suite
│  ├─ migration-paths.md                 four starting points, non-destructive paths out
│  └─ incident-and-challenge.md          the breach runbook, and hosting a hack challenge
└─ scripts/
   ├─ audit-repo.sh                      static, offline, read-only
   ├─ audit-bundle.sh                    the built artifact
   └─ probe-live.sh                      external probe, consent-gated

About 9,500 lines. SKILL.md is the only file always in context; every reference loads on demand.


The common asks

“I built a site in plain HTML — how do I deploy it?” No reflexive rewrite. Static HTML with no backend and no secrets is a legitimate and safe architecture, and a needless framework migration adds attack surface. The finding is a key in a `` tag, a form posting somewhere unvalidated, or a client-side admin mode. Often the right answer is static plus one serverless function, not Next.js.

“Is my app secure?” Never answered directly. Audit, then report what passed and what did not.

“I think I leaked a key.” Rotate first, then remove from history — and know that removing it from history is not enough, because it was already scraped. But run the false-positive filter first: half of these reports are publishable keys that were never secret, and rotating a Supabase anon key as remediation is a tell that the real problem, missing RLS, went unfixed.

“I think I’ve been hacked.” Evidence first. Five minutes of preservation before anything is touched — Vercel Hobby keeps runtime logs for one hour, so the window closes on its own.

“I want to run a public hack challenge.” There is a part for that, and it is meant to be read before the challenge goes live: a separate account rather than a separate project, a separate registrable domain rather than a subdomain, hard provider caps with actions rather than alerts, and a rehearsed sub-60-second kill switch.


Hard rules the skill holds itself to

  1. Audit before advising. Always.
  2. Never say “you’re secure.” Name the check that passed.
  3. Get the lane right before recommending anything. Lane B cannot run server-side defenses.
  4. Run the false-positive filter before every report.
  5. Never invent a CVE, version, statistic, or advisory. Fact-checking during development caught fabricated CVE quotes and an invented report edition. An unsourced specific is worse than a general statement.
  6. Preserve evidence before remediating a suspected live breach.
  7. State what you did not check. Every time.
  8. Authorization is verified by testing, not by reading. Two accounts, replayed requests, diffed responses.

Scope and safety

audit-repo.sh and audit-bundle.sh are read-only, make no network calls, and execute nothing in the target. They are safe to run against a repository you did not write.

probe-live.sh sends real HTTP requests and refuses to start without an explicit --i-own-this attestation. Run it only against a deployment you own or are authorised in writing to test. The destructive tiers are deliberately not implemented — the script tells you what they are and sends you to staging.

License

MIT — see LICENSE.

View the full README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add itsrealaj/never-get-hacked-skill
New to MCP? Start with the mcpskill.org guide →