Skip to content
Agentmart
Submit a tool

security-audit

Run a structured security audit of a web application or API.

Skill by cslev

IndexedMIT
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/cslev-security-audit

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Run a structured security audit of a web application or API. Fourteen concrete vulnerability patterns, each with a detection command, a reproduction step, the false alarms that look identical, and what a regression test must assert. Triggers on /security-audit, "security audit", "security review", "audit this codebase", "pen-test", "harden this app", or a specific class such as authentication bypass, replay attacks, CSRF, secrets in logs, race conditions, timing attacks, or denial of service. Use this INSTEAD of an open-ended "find vulnerabilities" pass — it runs defined phases, proves each finding with a reproduction, and verifies the tests that protect it. Do NOT use for writing security documentation or policies; that is ordinary writing.

Use cases: backend, testing, security, writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/security-audit

What's inside

Skill name
security-audit
Description
Run a structured security audit of a web application or API. Fourteen concrete vulnerability patterns, each with a detection command, a reproduction step, the false alarms that look identical, and what a regression test must assert. Triggers on /security-audit, "security audit", "security review", "audit this codebase", "pen-test", "harden this app", or a specific class such as authentication bypass, replay attacks, CSRF, secrets in logs, race conditions, timing attacks, or denial of service. Use this INSTEAD of an open-ended "find vulnerabilities" pass — it runs defined phases, proves each finding with a reproduction, and verifies the tests that protect it. Do NOT use for writing security documentation or policies; that is ordinary writing.
License
MIT
Instructions
435 lines in SKILL.md · view on GitHub

Before installing

  • Instructions call the network

Files (1)

  • SKILL.md

SKILL.md (first lines)

# Web Application Security Audit

An open-ended instruction to "find vulnerabilities" produces plausible findings, most of
them wrong and none demonstrated. Work these phases in order instead. Each check is narrow
enough to run without judgement and specific enough that a negative result means something.

Examples are Python/FastAPI because that is where these were catalogued; every pattern has a
direct equivalent in Express, Flask, Rails and Django. Items marked **Observed** are real
mistakes found in production code, including several made by the auditor while auditing.

Add these exclusions to every command, or results are unreadable:

```
--exclude-dir=node_modules --exclude-dir=.venv --exclude-dir=__pycache__
--exclude-dir=dist --exclude-dir=build --exclude-dir=.git
```

---

## Phase 0 — Map the surface before looking for bugs

Do not start grepping for bugs. Roughly half the findings fall out of the map.

```bash
grep -rnE '@(app|router)\.(get|post|put|patch|delete)\(' --include='*.py' .   # routes
grep -rnE "getenv|environ\[|process\.env" --include='*.py' --include='*.js' . # config
grep -rn "async def" --include='*.py' .                                       # async handlers
```

Answer five questions **in writing**:

| Question | What it exposes |
|---|---|
| What authenticates a request, and can the client influence it? | Header-spoofing auth bypass (1.1) |
| Does any credential travel in a request **body** rather than a header? | Replay, transport and logging exposure (1.4) |
| What is *ambient* authority — cookies, source IP, network position? | CSRF where `SameSite` does not apply (1.11) |
| Which endpoints change state or move money? | Serialisation (1.6) and CSRF (1.11) |
| What is written to logs, and who can influence its contents? | 1.2, 1.10, 1.13 |

For each route, record **whether the auth guard runs before any data lookup**. A guard after
the lookup is an enumeration oracle: `404` versus `401` reveals which identifiers exist.

---

## Phase 1 — The pattern checks

### 1.1 Client-supplied headers trusted for identity

```bash
grep -rnE "X-Forwarded-For|X-Real-IP|X-Forwarded-Proto|HTTP_X_FORWARDED" --include='*.py' .
```

For each hit: **is the direct peer verified as a known proxy first?** If not, attacker-
controlled input is being used as identity.

**Proof.** `curl -H 'X-Forwarded-For: <an allowlisted ip>' http://host/`

**False alarm.** Used only for display or logging. Worth fixing (log forgery), not a bypass.

**Fix.** Honour forwarded headers only from a configured trusted-proxy list, and take the
**rightmost** hop that is not itself a trusted proxy — proxies *append*, so the leftmost
value is the one the client chose.

**Check the framework layer too.** Many servers rewrite the client address from
`X-Forwarded-For` before your code runs (Uvicorn's `ProxyHeadersMiddleware` does this by
default for loopback peers). An application check is meaningless if the framework already
overwrote the value it inspects.

### 1.2 Secrets reaching logs

```bash
# Direct: a secret-named variable at a log call site
grep -rnE "(password|secret|token|api_key|passphrase|private_key)" --include='*.py' . \
  | grep -iE "logger\.|logging\.|print\(|console\."

# Indirect — THE ONE THAT GETS MISSED: a whole structure dumped
grep -rnE "(logger|logging|console)\.[a-z]+\(.*(json\.dumps|JSON\.stringify|repr|vars|__dict__|payload|body|config)" \
  --include='*.py' --include='*.js' .
```

> **Observed.** The first grep returned clean while `logger.info(f"payload: {json.dumps(data)}")`
> was writing an API key to disk — the secret was *inside a dict*, not named at the call
> site. Always run the second grep.

**Proof.** Capture log output during a request; assert the literal secret is absent. **Set
the capture level to DEBUG first** — at the default `WARNING` nothing is captured and the
absence assertion passes for the wrong reason.

**Check the notification path.** If ERROR-level logs are forwarded to chat/email/push, every
leaked secret is also transmitted off-host.

### 1.3 Secrets partially rendered in a UI

```bash
grep -rnE "\[:[0-9]+\].*\[-[0-9]+:\]|substring\([0-9]|slice\([0-9]" templates/ src/ views/
```

**Proof.** Render the page, then compute the longest substring of the real secret appearing
anywhere in the output. More than ~4 characters is too much.

**Subtlety.** Use a fixed-width mask: `abc...xyz` leaks the length, `••••••••1234` does not.

### 1.4 A bearer credential inside the request body

Common for webhooks. Implies **three** findings — check all:

| Consequence | Check | Fix |
|---|---|---|
| Replayable forever | Is the request required to be recent *and* unique? | Freshness window + idempotency key |
| Credential crosses the wire in cleartext | Is TLS enforced on that endpoint? | Refuse non-TLS delivery |
| Credential lands in logs | See 1.2 | Redact |

**Proof of replay.** Capture one valid request; send the exact bytes again.

**Sizing trap.** An idempotency entry must outlive the freshness of what it protects. If the
window accepts timestamps ±W, a request dated `+W` in the future is accepted now and stays
fresh until `W` past its own timestamp — up to **2W** after acceptance. A TTL of `W` leaves
it replayable in the gap. Size the cache at `2W`.

From cslev/skillet, MIT.

Signals

Stars
0
Forks
0
Open issues
0
Last commit (this folder)
Aug 20, 2026 (2 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from cslev

Cite this page

security-audit skill. Agent Mart. https://agentmart.dev/skills/cslev-security-audit (last updated 2026-10-24)