Skip to content
Back to skills

Pentest

BSecurity

Use for black-box audits of deployed web or Telegram mini apps; not load testing. Triggers — "проверь на уязвимости", "pentest".

  • 9 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 23, 2026
developmentjavascriptrustgojavabashsqlnextjstestinggitapi

Works with

  • claude code
  • cursor
  • cli
  • api

Security analysis

B75/100
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro scans all 13 files and shows the line behind each finding

Scanned September 23, 2026

npx -y skills add OXI-717/ai-native-toolkit --skill pentest --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pentest?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pentest
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/oxi-717-pentest/badge)](https://www.skillsdirectory.com/skills/oxi-717-pentest)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pentest
description: Use for black-box audits of deployed web or Telegram mini apps; not load testing. Triggers — "проверь на уязвимости", "pentest".
---

# Pentest

Black-box security audit of a running web application, from the outside, via HTTP.

**Default language:** match the user's request language (Russian or English). Pass `--lang ru|en` to override. Reports live **outside any git repo** in `${PENTEST_OUTPUT_DIR:-$HOME/cc/_pentest}/{target-slug}-{YYYY-MM-DD}/` (override the root with `PENTEST_OUTPUT_DIR`, or pass `--into-cwd` for `{cwd}/pentest-output/…`).

**Assumption:** the user has authorization from the target's owner. Do not ask for legalese — trust the user. But never run this skill against a target the user hasn't explicitly named.

---

## Workflow

### Phase 0 — Scope

Before touching the target, confirm:

- **Target URL** — full `https://…` URL of the deployed app (the mini app entry URL, not `t.me/...`).
- **Aggression level** — default `L1`. Let the user override:
  - `L0` passive: public metadata only, zero probes of the app itself
  - `L1` safe (default): single-request probes, read-only, no mutations
  - `L2` authorized: mutations on the auditor's own test account (profile updates, intros, etc.)
  - `L3` destructive: real injection payloads, cross-tenant access attempts, rate-limit probing *without* load testing
- **Target type hint** — `telegram-miniapp` | `nextjs-vercel` | `supabase` | `generic`. Auto-detect from headers if not provided.
- **Language** — `ru` or `en` (default: match user's language).
- **Credentials needed?** — if the target needs auth to explore (most apps do), ask the user to paste the auth artifact once:
  - Telegram mini apps: the launch URL fragment (`https://…/#tgWebAppData=…`) captured from DevTools when the app opens
  - Cookie-based apps: a session cookie or token from the user's logged-in session
  - Store it in `secrets.txt` under the workspace, add `.gitignore` to exclude it
  - **Never** include the raw auth artifact in any report file.

### Phase 1 — Workspace

Pick the output root. **Default is outside any git repo** — pentest artifacts (vulnerability
details, auth secrets) must never risk landing in a commit, and `{cwd}` is often a repo (or a
locked read-only checkout, where the write just fails). Only `--into-cwd` writes next to the
current dir, and only when `{cwd}` is a normal writable work dir.

```bash
# default: $HOME/cc/_pentest (a non-repo data dir); override root with PENTEST_OUTPUT_DIR;
# --into-cwd => "${PWD}/pentest-output"
out_root="${PENTEST_OUTPUT_DIR:-$HOME/cc/_pentest}"
slug=$(echo "<target host>" | tr -cd 'a-z0-9-')
day=$(date +%Y-%m-%d)
workdir="${out_root}/${slug}-${day}"
mkdir -p "${workdir}"/{recon,bundle,scripts,findings,fixes}
echo 'secrets.txt
*.local.*
cookies.txt' > "${workdir}/.gitignore"
```

Write `${workdir}/README.md` with: target URL, scope, level, language, start time, operator.

**If `--into-cwd` is used**, `{cwd}/pentest-output/` lives inside a repo: before writing, confirm
`pentest-output/` is git-ignored in that repo (add it to the repo `.gitignore` if not), so a
vuln report is never accidentally committed.

### Phase 2 — Recon (L0 — always run)

Read `${CLAUDE_PLUGIN_ROOT:-.}/skills/pentest/references/checklist-generic.md` for the full command list. Minimum:

1. DNS (`A`, `AAAA`, `CNAME`, `NS`) and provider fingerprint
2. `curl -D headers_root.txt -o body_root.html <target>` — save headers + body
3. Probe well-known paths: `robots.txt`, `sitemap.xml`, `/.well-known/security.txt`, `favicon.ico`, `manifest.json`
3b. Run the exposed-artifacts sweep (checklist §3b): `.git/*`, `.env*`, logs, backups/dumps, deploy descriptors, AI-era metadata (`llms.txt`, `CLAUDE.md`, `AGENTS.md`, `.cursorrules`), framework actuators. **Mandatory:** build the SPA-fallback baseline first (hash of root body) so catch-all 200s don't produce false positives.
4. Grep the root HTML for asset references → full JS/CSS chunk inventory
5. Extract `og:*` meta, `<script src>`, `<link href>`, any inline data
6. Check security headers on both root and sample API endpoint: HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, CORS

Fingerprint the stack from response headers:

- `server: Vercel` + `x-matched-path` + `x-nextjs-*` → Next.js on Vercel → load `references/nextjs-vercel.md`
- URLs like `…supabase.co` → Supabase backend → load `references/supabase.md`
- `https://telegram.org/js/telegram-web-app.js` or `tgWebAppData` fragment → Telegram mini app → load `references/telegram-miniapp.md`

Apply stack-specific reference only when fingerprint matches. Don't read all references upfront.

### Phase 3 — Bundle analysis

Download all JS chunks referenced from the root HTML to `bundle/`. For each:

```bash
for f in bundle/*.js; do
  curl -sS -o "$f.map" "https://<host>/path/to/$(basename $f).map?<dpl>"
  [ -s "$f.map" ] && echo "source map exposed: $f"
done
```

Grep the bundle for:

- API routes: `/api/[^"]+`
- `fetch(` / `axios(` call sites
- Absolute URLs of third-party services
- Secrets patterns: `eyJ[a-zA-Z0-9_-]{40,}`, `sk_[a-z]+_[a-zA-Z0-9]{20,}`, `NEXT_PUBLIC_[A-Z_]+`, `SUPABASE_[A-Z_]*`, `api_key`, `service_role`
- Auth header name (`Authorization`, `x-telegram-init-data`, `X-Api-Key`, etc.) and how it's injected
- `/directory/${...}`, `/profile/${...}` — templated app-level routes

Save the full inventory to `recon/api-inventory.md` — this becomes the map for Phase 4.

**Client wiring audit.** Once the auth header is identified (say `x-telegram-init-data`), grep for `fetch(` calls that hit `/api/` endpoints but do NOT go through the app's auth wrapper (typically `apiFetch` or similar). Example: a `fetch("/api/referrals/attribute", {method:"POST", ...})` with no `x-telegram-init-data` header, while the server demands it, means the feature is functionally broken on the client. That's a reportable bug (info severity, «broken feature») even if it's not a security vulnerability. Flag these:

```bash
# find raw fetch() calls to /api
grep -hoE 'fetch\([^)]{0,300}/api/[^)]{0,300}\)' bundle/*.js | head -20
# compare: which ones go through apiFetch (auth wrapper)?
grep -hoE 'apiFetch\([^)]{0,300}\)' bundle/*.js | head -20
```

If a route appears in plain `fetch(` but is also listed as requiring auth in your Phase 4 map, note «client-wiring mismatch: server requires auth, client sends none — feature broken».

### Phase 4 — Endpoint probing (L1 default)

For every `/api/*` endpoint found:

1. **Unauthenticated GET** — record status code, size, body preview
2. **Authenticated GET** (with user's auth artifact) — record the same
3. Compare: endpoints that return 200 unauth AND contain PII are privacy leaks

Then for any auth/login endpoint (`/api/auth/*`, `/api/session`, etc.):

- Empty body → expected 400, flag 500 as an input-validation bug
- Missing credentials → expected 401
- Bad credentials → expected 401
- Credentials with tampered fields but valid signature → expected 401 (this tests the signature actually covers the tampered field)

If stack = `telegram-miniapp`, run the full auth test matrix from `references/telegram-miniapp.md` (empty initData, no hash, fake hash, tampered user_id, stale auth_date replay).

### Phase 5 — Authorization / IDOR (L1 default)

Collect any IDs visible in Phase 4 responses (userId, memberId, eventId, intro_id, etc.) and try:

- `GET /api/members/{otherUserId}` with your own auth — do you see their data? Expected: 200 (if public) or 404/403 (if protected)
- `GET /api/{thing}?userId={otherUserId}` — does the filter actually filter? Or is it silently ignored?
- `GET /api/{thing}?limit=999999`, `?limit=-1`, `?offset=abc` — input validation smoke. Note 500s as bugs.
- `?role=admin`, `?membershipStatus=banned` — does passing a privilege filter return different data?
- Path confusion: `/api/{thing}/..%2F..%2Fetc%2Fpasswd`, `/api/{thing}/%00.json`

**Do not** fetch another real user's sensitive data past proof-of-access. Stop at the first 200 that proves the vulnerability and record just the structure of the response, not the content. Redact names/emails/ids of real users in the report.

### Phase 6 — L2 (mutations on own account, only if user asked for L2)

On the auditor's own test account:

- `POST` endpoints: profile update, intro send, referral attribute, upload
- Test: server-side validation vs client-side. Can you send fields not shown in the client form?
- Test: are reserved/internal fields accepted? (`is_admin`, `role`, `membership_status`, `credits`, `tier`)
- Test: can you change another user's resource by sending their ID in the body? (IDOR on mutations)
- Test: file upload — MIME sniffing, path traversal in filename, SVG-with-script upload, oversize file

Always label each mutation request in `EVIDENCE.md` with what it did and which object it touched, so it's easy to clean up afterward.

### Phase 7 — L3 (destructive, only if user asked for L3)

- SQL/NoSQL injection payloads (`' OR 1=1--`, `'; DROP --`, `{"$ne": null}`) on fields that hit the DB
- SSRF via URL parameters (`url=http://169.254.169.254/latest/meta-data/`)
- XSS in stored fields (`<script>alert(1)</script>`, `javascript:alert(1)` in avatarUrl) — check where it reflects
- Prototype pollution via JSON body (`{"__proto__":{"isAdmin":true}}`)
- Cross-tenant: attempt to read/modify a non-test user's data. **Stop at proof-of-access, don't exfiltrate.**

Single requests only. **No load / brute-force / enumeration loops.** If a finding needs automation to be proven, write it up as a theoretical finding with the single-request PoC and note the limitation.

### Phase 8 — Generate deliverables

Three artifacts, two locations:

- `${workdir}/findings/REPORT.md` — human-readable, for the target's owner. Use `references/report-template.md` as the skeleton. Order findings by severity descending. Each finding has: description, why it matters, PoC steps, recommended fix (with code snippet). End with "What works well ✅" and "What I didn't test" sections.
- `${workdir}/findings/EVIDENCE.md` — raw request/response log, one section per finding. Use `references/evidence-template.md`. Redact real user PII. Keep auth artifacts out (they're in `secrets.txt`, .gitignored).
- `${workdir}/fixes/` (sibling of `findings/`, NOT nested) — machine-actionable per-finding fix plan, designed to be consumed by another coding agent:
  - `${workdir}/fixes/INDEX.md` — markdown table of all fixes sorted by priority with a one-line summary each
  - `${workdir}/fixes/NN-slug.md` — one file per finding, two-digit priority prefix (e.g. `01-auth-date-ttl.md`, `02-public-member-list.md`)
  - Template: `references/fix-template.md`

After writing, print the three absolute paths in chat: `${workdir}/findings/REPORT.md`, `${workdir}/findings/EVIDENCE.md`, `${workdir}/fixes/INDEX.md`. Don't dump report contents into chat.

---

## Consuming `fixes/` with another agent

The per-finding fix files are self-contained. To apply fixes in a different session, user can say:

> "Read `fixes/INDEX.md` and apply the fixes one by one, running tests between each."

Each fix file has:

- `severity`, `title`, `location` (URL/file), `summary`
- `patch` — concrete code change (or pseudo-patch if the repo is not available)
- `verification` — commands or HTTP probes that confirm the fix landed

---

## Watch mode (scheduled deterministic sweeps)

The interactive phases above are the deep audit. Alongside them, the plugin ships a
non-interactive scanner for regular runs against a fixed registry of surfaces:

- `scanner/scan.py` — per surface: root page, canary-based catch-all baseline,
  exposed-artifacts sweep, registered unauth endpoint probes, JS bundle download,
  API route discovery from bundles, and a secret scan over every fetched byte.
  Output: `run.json` + `FINDINGS.md`; exit code 2/1/0 = critical/high/clean.
  `--baseline <prev-run>` diffs probe maps (static hashed assets excluded).
- `scanner/secret_patterns.py` — detector library. **Incident → pattern loop:** when a
  real leak happens, add the detector here FIRST (with a comment naming the incident),
  so every later run catches the class. Born-detectors: Telegram bot token /
  `api.telegram.org/file/bot<token>` URLs (real incident 2026-09-16).
- `scanner/surfaces.example.yaml` — registry format: host, tier (1 nightly / 2 weekly /
  3 rare), `endpoints` with `expect: public|auth`, `access: public|contour`
  (contour-only hosts get findings downgraded one level — they are unreachable from
  the public internet but visible to the whole tailnet).
- `scanner/test_secret_patterns.py` — detector regression tests; extend with every new
  pattern.

Watch runs obey the same limits as L1: single GET per path, `--delay` between
requests, no mutations, no load. Findings with secrets are stored redacted + hashed,
never raw.

---

## Rules

1. **Never** run load / stress tests here — route the user to `loadtest` instead. A burst of 100 requests in a tight loop is load testing, not probing.
2. **Redact every human name, email, phone, and telegram handle by default** before writing to `REPORT.md` or `EVIDENCE.md`. Even if a name appears in the target's own public listing (e.g., the owner's name in their own member directory), redact. The only exception is a name explicitly named by the user as «OK to keep»: «you can keep Aleksey's name» — otherwise mask to `<redacted-name>`, `<redacted-email>`, `<redacted-uuid>`. Default wins; opt-out is per-name.
3. **Never** write the raw auth artifact to any file outside `secrets.txt`. Never paste it in chat.
4. **Never** exploit a finding past proof-of-access. If you prove `GET /api/admin/*` returns 200, stop — don't iterate through the list.
5. **Every** mutation (L2+) must be labeled in EVIDENCE.md with (a) endpoint, (b) body sent, (c) object affected, (d) reversibility note.
6. **Never escalate levels without a fresh user confirmation in the current turn.** If the user invoked L1 and mid-run you find something that looks «L2-interesting», record it as an L1 finding with the single-request PoC and a note «L2 probe needed to confirm». Do **not** escalate on your own initiative even when the next probe would obviously land.
7. **Stop and ask** the user before running L2 on any endpoint marked «mutation» if it looks like it touches billing/payment/external service integration.
8. **Never** use the Claude-only `AskUserQuestion`, `TeamCreate`, `TaskCreate` APIs — this skill runs in both Claude Code and Codex.

---

## Stack-specific references

Load only when the fingerprint from Phase 2 matches:

| Stack | Reference | When |
|-------|-----------|------|
| Telegram mini app | `${CLAUDE_PLUGIN_ROOT:-.}/skills/pentest/references/telegram-miniapp.md` | initData / HMAC / Ed25519 / replay / TTL / self-referral / referral fraud |
| Next.js on Vercel | `${CLAUDE_PLUGIN_ROOT:-.}/skills/pentest/references/nextjs-vercel.md` | chunks, source maps, env exposure, `/api/*` conventions, edge vs serverless, x-vercel-* headers |
| Supabase backend | `${CLAUDE_PLUGIN_ROOT:-.}/skills/pentest/references/supabase.md` | anon key, RLS, storage buckets, PostgREST |
| Any web app | `${CLAUDE_PLUGIN_ROOT:-.}/skills/pentest/references/checklist-generic.md` | headers, well-known paths, exposed-artifacts sweep (§3b), CORS, method confusion |

Templates:

| File | Purpose |
|------|---------|
| `references/levels.md` | Definitions of L0-L3 with examples of what's in/out of scope |
| `references/report-template.md` | REPORT.md skeleton with severity table + section order |
| `references/evidence-template.md` | EVIDENCE.md skeleton with per-finding sections |
| `references/fix-template.md` | Single fix file template for coding agents |

Files in this skill

  • SKILL.md15.5 KB
  • agents/openai.yaml277 B
  • references/checklist-generic.md16.2 KB
  • references/evidence-template.md3.3 KB
  • references/fix-template.md5.9 KB
  • references/levels.md5.8 KB
  • references/nextjs-vercel.md9.3 KB
  • references/report-template.md4.4 KB
  • references/supabase.md6.3 KB
  • references/telegram-miniapp.md9.6 KB
  • scanner/scan.py20.6 KB
  • scanner/secret_patterns.py8.7 KB
  • scanner/surfaces.example.yaml914 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.