qa-sec
Skill2 starsChanged 46 days agoMITView on GitHub
Defensive security review of a web app you own, driven by agents that operate the real app — audit headers, cookies, token handling and the JS bundle passively; hunt broken access control across roles (forced browsing, IDOR/BOLA, privilege escalation, session invalidation); and probe input handling and business-logic abuse with benign markers. Detection-first — it proves a hole exists and stops, never extracting data, persisting payloads or stress-testing. Use when asked to "check the security of the app", "look for vulnerabilities", "is this app safe", "audit access control / permissions", "find security holes before the client does", or to add a security pass to an existing qa-explore setup. Sibling of qa-explore; same engine, different question.
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 67
It does not replace a pentest: No authenticated fuzzing at depth, no chained exploitation, no infrastructure, no social engineering.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
qa-sec
qa-explore asks is this broken?. qa-sec asks can someone abuse this? — on an app you own, so you can fix it.
It is a review, not an attack: every pass finds a hole, proves it exists, and stops there.
The passes
Surface map what is actually exposed: inputs, ids, uploads, redirects, privileged actions
Passive (always) headers/CSP/CORS · cookies + token lifecycle · secrets & source maps in the
bundle · leaky error pages · HIGH/CRITICAL dependency advisories
Access-control (the expensive bugs) forced browsing · hidden-but-not-blocked actions · IDOR/BOLA on
ids · mass assignment · session/tenant boundaries — one agent per role
Input & logic (safe-active only) reflection · injection error signals · traversal · open redirect ·
SSRF · upload type enforcement · server-side validation · BUSINESS-LOGIC abuse
Verify an independent skeptic reproduces each serious finding, in both directionsFindings come out in the same shape qa-explore produces, so /qa-gate blocks on them (its blockOnAccessControl is on by default) and the report/fix loop works unchanged.
Engine (invoke via the Workflow tool; do not inline)
${CLAUDE_PLUGIN_ROOT}/skills/qa-sec/engine/qa-sec.workflow.js- Config: reuses the project
qa.config.json(baseUrl, login, roles, allowedHosts, shotsDir, domainNotes, e2eDir, sourceHints) plus asecurityblock.
Before it runs — the two gates
security.authorizedmust betrue, withsecurity.scopenaming the system and who authorised it. Without it the engine returns immediately and does nothing. This is a live system: someone owns that decision, and the config is where it is recorded.- Scope is
allowedHosts(defaults to thebaseUrlhost). Agents may not send a request anywhere else, follow a finding off-host, or touch another machine. A third-party host that turns up is recorded as out-of-scope, not probed.
Then, as with qa-explore: state the resolved target, the intrusiveness and the scope, and get a go-ahead before the run — louder if it looks like production.
Intrusiveness
security.intrusiveness | What it sends | Good for |
passive (default) | nothing beyond normal use — it logs in, browses, and reads the headers, cookies, tokens, bundles and errors the app already serves | anything, any time, including production |
safe-active | the above plus benign markers into inputs the app exposes and calls to endpoints the app itself calls, with altered parameters | staging / pre-prod, or production with the owner's explicit say-so |
Under both, these are hard rules the engine writes into every agent's prompt: stop at proof; never read real data beyond the one record that proves the exposure, redacted; never modify, delete, or plant anything that persists; no load/stress/timing attacks; login attempts capped (security.maxLoginAttempts, default 8) and only to check that lockout exists; no credentials other than the configured ones; benign markers only — no destructive SQL, no shell payloads.
How to run
- Resolve config. Read
qa.config.jsonfor the target,login,allowedHosts,shotsDir,domainNotesande2eDir, plus thesecurityblock. Checksecurity.authorizedandscope; if they are missing, ask the user to confirm they own this system and write it into the config rather than passing the flag yourself.
1b. Check the evidence dir. node <qa-explore engine>/evidence.mjs check <shotsDir> — the HARs this run captures hold session tokens, and shotsDir must be on disk, not a tmpfs.
- Declare a second role. The access-control pass is much stronger with two accounts (one lower-privileged): most of what it hunts is one user reaching another user's data. With a single role it still runs, but it says in its notes what it could not test. If the project has no second account, offer to have one created.
- Run.
Workflow({ scriptPath: "<engine>/qa-sec.workflow.js", args: <config> }). Returns one entry per pass, each withchecked(including the clean results) andfindings. - Synthesize. Write
<shotsDir>/INFORME-SEGURIDAD.md: group by proven + confirmed first, then severity, then the hardening list. For each finding give the impact in one sentence, the redacted repro, and the fix. Keep thecheckedlists — "CSRF is protected, SameSite=Lax + token on every mutation" is as useful to the reader as a finding. Call out anythingproven: falseas what it is: a suspicion that needs a deeper look. - Report — carefully. If a
trackeris configured, file security findings as confidential issues (GitLabconfidential: true; GitHub: a private security advisory, or a private repo — never a public issue). Never attach the HAR or storageState: they contain live session tokens. Screenshots only, and only after checking them. - Prune the evidence. Same step as qa-explore (
evidence.mjs prune), and it matters more here: the HARs hold tokens and possibly personal data. Keep only what a confirmed finding needs. - LEARN. A finding rejected as intentional goes into
security.knownAccepted(with the reason) so the next run does not re-raise it — the security equivalent ofdomainNotes. An accepted risk is a decision, and it belongs in writing.
Notes
- Cadence:
passiveon every release (it is cheap and catches regressions in headers/cookies/bundle);safe-activeagainst staging before a release, or after a change to auth, permissions, uploads or payments. - It does not replace a pentest. No authenticated fuzzing at depth, no chained exploitation, no infrastructure, no social engineering. It catches the flat, common, expensive things — the ones that are embarrassing to be told about by someone else.
- Access control is the one to care about. Headers and cookies are worth fixing but rarely the breach; one user reading another's data is. That is why it gets its own pass and why
/qa-gatetreats it as non-negotiable. - Dependencies are read from the repo, not the app (
npm audit --omit=dev,osv-scanner,trivy), and only HIGH/CRITICAL affecting shipped code.
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.

