Plan-only trust-boundary audit for missing validation, injection, XSS, and forged requests across forms/APIs/webhooks. Use when "validate my inputs", "is my app injection-safe?", "check my forms", or "can someone forge requests?". Apply fixes only after plan approval.
Scanned 9/11/2026
Install to Claude Code
npx -y skills add kensaurus/cursor-kenji --skill plan-input-validation --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Plan Input Validation?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/kensaurus-plan-input-validation)More formats (shields.io, HTML) on the badges page.
---
name: plan-input-validation
description: >
Plan-only trust-boundary audit for missing validation, injection, XSS,
and forged requests across forms/APIs/webhooks. Use when "validate my
inputs", "is my app injection-safe?", "check my forms", or "can someone
forge requests?". Apply fixes only after plan approval.
license: MIT
---
# Input-Validation & Trust-Boundary Audit + Hardening Plan
**Degree of freedom: HIGH** — map trust boundaries, score gaps, emit a plan.
Stay **plan-only**. No Zod, sanitizers, or webhook edits until approved.
## This skill vs neighbors
| Skill | Owns |
|---|---|
| **plan-input-validation** (this) | Trust-boundary / injection plan |
| `enhance-web-forms` | Apply production form quality |
| `plan-security-audit` | OWASP umbrella burndown |
**Role:** Senior application security engineer (trust-boundary lens).
**Task:** Map every point untrusted data enters, score validate/sanitize/authenticate
gaps, phase remediations, emit `plan-input-validation.md`. **Audit & plan only — no
code changes until each phase is approved.**
**Walk every boundary. Find what's trusted that shouldn't be. Change nothing until approved.**
## How to reason (every plan item)
1. **Propose** — schema, sanitize, signature verify, or allowlist
2. **Risk** — forgeable money/data, stored XSS, or injection
3. **Keep-working** — boundaries that already validate + authenticate
4. **Phase** — Forgeable paths → XSS → Schema pass → Uploads (do not execute)
## Worked example
> **Propose:** verify the Stripe webhook with `constructEvent` on the raw body; reject empty secrets; return 400 on bad signatures.
> **Risk:** empty-signing-secret bypass — anyone can forge `invoice.paid` and credit quota.
> **Keep-working:** checkout session creation already uses the server-side secret.
> **Phase:** Phase 1 — Forgeable money/data paths.
> **Note:** origin proof ≠ safe to interpolate into SQL/HTML.
AI agents write code that works on the inputs you showed them. Two signature patterns
recur: `dangerouslySetInnerHTML` without DOMPurify (XSS), and webhook handlers without
real signature verification — the reported empty-signing-secret bypass class, where an
empty secret lets any attacker forge valid signatures and credit unlimited quota
without payment.
This skill is the **audit-and-plan** half. Execution goes to `backend-patterns` /
`backend-error-handling` / `audit-security` after you approve each phase.
---
## When this fires
Trigger phrases: *"validate my inputs"*, *"is this injection-safe"*, *"check my
forms / API"*, *"XSS"*, *"dangerouslySetInnerHTML"*, *"my Stripe webhook"*, *"can
requests be forged"*, *"sanitize user content"*, *"pre-launch input hardening"*.
Do **not** fire for: row-level access (`plan-rls-audit`), credential exposure
(`plan-secrets-audit`), or broad architecture review (`plan-security-audit`).
This skill owns the *boundary where untrusted data enters*.
---
## The four boundary classes [HIGH freedom]
### 1 · Form & API input
- **No schema validation** — bodies/params/query without Zod (or equivalent).
- **Type-coerced trust** — `Number(req.body.amount)` with no bounds.
- **Missing field-level checks** — email format, length caps, enum membership.
- **Mass assignment** — spreading `req.body` into DB insert/update (`role`,
`is_admin`, `credits`).
- **SQL/RPC injection** — string-interpolated queries or raw user input into SQL.
### 2 · Rendered untrusted content (XSS)
- **`dangerouslySetInnerHTML` / `v-html` / `innerHTML`** without DOMPurify.
- **URL/attribute injection** — `javascript:` URIs, unvalidated redirects.
- **Stored XSS** — content saved now, rendered raw later.
### 3 · Webhooks & forged requests *(Stripe-aware)*
- **Signature not verified** — or verified against **empty secret**
(empty-secret bypass class).
- **Raw-body mistake** — `JSON.stringify(req.body)` instead of raw bytes.
- **No idempotency** — Stripe at-least-once retries double-process.
- **Cross-gateway trust** — fulfilling without checking callback source.
- **Missing timestamp tolerance** — replay window open.
- **Returns 500 not 400** on bad signature → infinite Stripe retries.
### 4 · File uploads & other boundaries
- **No type/size/MIME validation**; trusting client content-type.
- **Path traversal** in filenames; **SSRF** in user-supplied URLs.
- **Concurrency** — read-modify-write races without DB constraints.
---
## Procedure [HIGH freedom — plan only]
1. **Map boundaries.** Enumerate every untrusted entry point. Skip absent ones.
2. **Test each.** For every boundary: validated? sanitized? authenticated?
3. **Score.** Severity = reachability × impact.
4. **Phase** into shippable groups mapped to execution skills.
5. **Emit `plan-input-validation.md`. End the turn. Do not edit code.**
---
## Guardrails
- **Plan only.** No Zod schemas, sanitizers, or webhook config changes.
- **Validate at the boundary, not after.**
- **Signature ≠ safety.** Origin proof ≠ safe to interpolate into SQL/HTML.
- **Don't trust the client copy.** Browser-only checks are UX, not security.
- **Stack-specific raw-body note.** Call out Next.js + Stripe raw-body requirement.
- **Minimal quoting** of source.
## Self-critique before the burndown [LOW freedom — do not skip]
1. **evidenced-not-assumed** — every boundary cites path:line; skip classes that do not exist
2. **plan-only** — no Zod schemas, DOMPurify, or webhook config
3. **severity/phase justified** — unauthenticated write / forgeable money is Critical
4. **right-owner** — RLS row access → `plan-rls-audit`; leaked keys → `plan-secrets-audit`; OWASP umbrella → `plan-security-audit`
5. **no-false-safety** — browser-only checks are UX; signature ≠ sanitization; empty webhook secret is a bypass
---
## Report template — `plan-input-validation.md`
```markdown
# Input-Validation & Trust-Boundary Audit — <repo>
_Audit-only. Nothing changes until each phase is approved._
## Scope
- Boundaries found: forms / API / webhooks / uploads / rendered content
- Stack: Supabase ☐ Stripe ☐ Next.js ☐ | Assumptions: …
## Verdict
| Boundary class | Findings | Critical | Unauthenticated write reachable? |
|----------------|----------|----------|----------------------------------|
| Form & API | n | n | … |
| Rendered (XSS) | n | n | … |
| Webhooks | n | n | … |
| Uploads/other | n | n | … |
## Findings
| # | Boundary | path:line | Missing: validate/sanitize/authenticate | Sev | Direction |
|---|----------|-----------|-----------------------------------------|-----|-----------|
| I1 | Stripe webhook | api/webhooks/stripe.ts:12 | authenticate (no constructEvent) | Crit | verify raw body against secret; 400 on fail |
| I2 | comment render | Comment.tsx:30 | sanitize (dangerouslySetInnerHTML) | High | DOMPurify before render |
| I3 | profile update | actions/profile.ts:8 | validate (mass assignment) | High | Zod allowlist; drop role/credits |
## Phased burndown
- **Phase 1 — Forgeable money/data paths** → `backend-patterns` — webhooks, mass-assign
- **Phase 2 — XSS / rendered content** → `backend-error-handling` / `enhance-web-ux` — I2…
- **Phase 3 — Schema validation pass** → `backend-patterns` — Zod at every boundary
- **Phase 4 — Uploads & concurrency** → `audit-security` — files, races
## Execution handoff
Approve a phase to run it. Re-run after; for webhooks, verify with Stripe CLI
fixtures (real signed events) not mocked payloads.
```
---
## Chains with
- **Security spine** — entry layer (**this skill**); data access: `plan-rls-audit`;
credentials: `plan-secrets-audit`.
- **Execution:** `backend-patterns`, `backend-error-handling`, `audit-security`,
`audit-fe-api`.
- **Verify:** `test-red-team` + Stripe CLI signed webhook fixtures.
> Plan with a strong model; execute with `composer-2.5-execution.mdc` riding
> along. The plan says *which* boundaries are open; the rule constrains *how*
> they're closed.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!