claude-code-kit 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_code_kit-0.7.0.dist-info/METADATA +384 -0
- claude_code_kit-0.7.0.dist-info/RECORD +209 -0
- claude_code_kit-0.7.0.dist-info/WHEEL +4 -0
- claude_code_kit-0.7.0.dist-info/entry_points.txt +4 -0
- claude_code_kit-0.7.0.dist-info/licenses/LICENSE +21 -0
- claude_kit/__init__.py +10 -0
- claude_kit/__main__.py +8 -0
- claude_kit/_payload/agents/acceptance-reviewer.md +60 -0
- claude_kit/_payload/agents/auditor.md +76 -0
- claude_kit/_payload/agents/dependency-scanner.md +84 -0
- claude_kit/_payload/agents/developer.md +187 -0
- claude_kit/_payload/agents/devils-advocate.md +62 -0
- claude_kit/_payload/agents/devops-engineer.md +134 -0
- claude_kit/_payload/agents/e2e-tester.md +152 -0
- claude_kit/_payload/agents/em-reviewer.md +105 -0
- claude_kit/_payload/agents/incident-responder.md +64 -0
- claude_kit/_payload/agents/merge-reviewer.md +194 -0
- claude_kit/_payload/agents/observability-engineer.md +94 -0
- claude_kit/_payload/agents/orchestrator.md +551 -0
- claude_kit/_payload/agents/owasp-reviewer.md +76 -0
- claude_kit/_payload/agents/policy-validator.md +63 -0
- claude_kit/_payload/agents/pr-raiser.md +138 -0
- claude_kit/_payload/agents/risk-classifier.md +50 -0
- claude_kit/_payload/agents/sdlc-code-reviewer.md +196 -0
- claude_kit/_payload/agents/secret-scanner.md +70 -0
- claude_kit/_payload/agents/security-reviewer.md +80 -0
- claude_kit/_payload/agents/senior-backend-dev.md +199 -0
- claude_kit/_payload/agents/senior-frontend-dev.md +181 -0
- claude_kit/_payload/agents/senior-tester.md +206 -0
- claude_kit/_payload/agents/spec-doc-writer.md +331 -0
- claude_kit/_payload/agents/story-planner.md +56 -0
- claude_kit/_payload/agents/technical-architect.md +139 -0
- claude_kit/_payload/agents/tester.md +193 -0
- claude_kit/_payload/agents/ui-designer.md +73 -0
- claude_kit/_payload/agents/unit-tester.md +119 -0
- claude_kit/_payload/catalog/mcp.yaml +54 -0
- claude_kit/_payload/catalog/org.yaml +145 -0
- claude_kit/_payload/catalog/profiles.yaml +96 -0
- claude_kit/_payload/catalog/stacks.yaml +96 -0
- claude_kit/_payload/commands/init.md +36 -0
- claude_kit/_payload/commands/sdlc.md +18 -0
- claude_kit/_payload/commands/status.md +20 -0
- claude_kit/_payload/hooks/hooks.json +58 -0
- claude_kit/_payload/hooks/scripts/audit-log.sh +18 -0
- claude_kit/_payload/hooks/scripts/guard-secrets.sh +26 -0
- claude_kit/_payload/hooks/scripts/lint-fix.sh +38 -0
- claude_kit/_payload/hooks/scripts/load-continuity.sh +32 -0
- claude_kit/_payload/hooks/scripts/load-learnings.sh +40 -0
- claude_kit/_payload/hooks/scripts/type-check.sh +23 -0
- claude_kit/_payload/hooks/scripts/validate-frontmatter.sh +34 -0
- claude_kit/_payload/hooks/scripts/validate-settings.sh +21 -0
- claude_kit/_payload/hooks/scripts/warn-large-edits.sh +24 -0
- claude_kit/_payload/hooks/scripts/warn-missing-tests.sh +24 -0
- claude_kit/_payload/hooks/scripts/warn-sensitive-files.sh +30 -0
- claude_kit/_payload/hooks/scripts/warn-shared-modules.sh +33 -0
- claude_kit/_payload/rules/agent-guardrails.md +83 -0
- claude_kit/_payload/rules/agent-memory.md +106 -0
- claude_kit/_payload/rules/agent-resilience.md +61 -0
- claude_kit/_payload/rules/autonomy-levels.md +30 -0
- claude_kit/_payload/rules/code-organization.md +312 -0
- claude_kit/_payload/rules/continuity.md +84 -0
- claude_kit/_payload/rules/design-patterns.md +422 -0
- claude_kit/_payload/rules/devops-observability.md +57 -0
- claude_kit/_payload/rules/documentation.md +326 -0
- claude_kit/_payload/rules/evals.md +62 -0
- claude_kit/_payload/rules/frontend-best-practices.md +157 -0
- claude_kit/_payload/rules/goal-setting-and-monitoring.md +72 -0
- claude_kit/_payload/rules/human-in-the-loop.md +64 -0
- claude_kit/_payload/rules/linting-and-formatting.md +220 -0
- claude_kit/_payload/rules/mandatory-workflow.md +309 -0
- claude_kit/_payload/rules/model-tiers.md +34 -0
- claude_kit/_payload/rules/quality-gates.md +107 -0
- claude_kit/_payload/rules/rarv-cycle.md +31 -0
- claude_kit/_payload/rules/reasoning-techniques.md +62 -0
- claude_kit/_payload/rules/responsive-and-accessibility.md +353 -0
- claude_kit/_payload/rules/risk-classification.md +36 -0
- claude_kit/_payload/rules/testing.md +417 -0
- claude_kit/_payload/rules/tool-design.md +66 -0
- claude_kit/_payload/skills/_references/accessibility-checklist.md +160 -0
- claude_kit/_payload/skills/_references/orchestration-patterns.md +405 -0
- claude_kit/_payload/skills/_references/performance-checklist.md +153 -0
- claude_kit/_payload/skills/_references/security-checklist.md +134 -0
- claude_kit/_payload/skills/_references/testing-patterns.md +236 -0
- claude_kit/_payload/skills/accessibility-review/SKILL.md +56 -0
- claude_kit/_payload/skills/api-and-interface-design/SKILL.md +294 -0
- claude_kit/_payload/skills/api-integration/SKILL.md +348 -0
- claude_kit/_payload/skills/archive-sprint/SKILL.md +31 -0
- claude_kit/_payload/skills/backlog/SKILL.md +41 -0
- claude_kit/_payload/skills/backlog/item-template.md +20 -0
- claude_kit/_payload/skills/browser-testing-with-devtools/SKILL.md +302 -0
- claude_kit/_payload/skills/ci-cd-and-automation/SKILL.md +402 -0
- claude_kit/_payload/skills/code-review-and-quality/SKILL.md +347 -0
- claude_kit/_payload/skills/code-simplification/SKILL.md +331 -0
- claude_kit/_payload/skills/component-design/SKILL.md +171 -0
- claude_kit/_payload/skills/consolidate-learnings/SKILL.md +55 -0
- claude_kit/_payload/skills/context-engineering/SKILL.md +321 -0
- claude_kit/_payload/skills/debugging-and-error-recovery/SKILL.md +300 -0
- claude_kit/_payload/skills/decision/SKILL.md +46 -0
- claude_kit/_payload/skills/decision/adr-template.md +36 -0
- claude_kit/_payload/skills/deprecation-and-migration/SKILL.md +207 -0
- claude_kit/_payload/skills/documentation-and-adrs/SKILL.md +299 -0
- claude_kit/_payload/skills/doubt-driven-development/SKILL.md +243 -0
- claude_kit/_payload/skills/execute/SKILL.md +27 -0
- claude_kit/_payload/skills/frontend-ui-engineering/SKILL.md +328 -0
- claude_kit/_payload/skills/git-workflow-and-versioning/SKILL.md +300 -0
- claude_kit/_payload/skills/idea-refine/SKILL.md +178 -0
- claude_kit/_payload/skills/idea-refine/examples.md +238 -0
- claude_kit/_payload/skills/idea-refine/frameworks.md +99 -0
- claude_kit/_payload/skills/idea-refine/refinement-criteria.md +113 -0
- claude_kit/_payload/skills/idea-refine/scripts/idea-refine.sh +15 -0
- claude_kit/_payload/skills/incident-postmortem/SKILL.md +74 -0
- claude_kit/_payload/skills/incremental-implementation/SKILL.md +245 -0
- claude_kit/_payload/skills/interview-me/SKILL.md +221 -0
- claude_kit/_payload/skills/load-testing/SKILL.md +83 -0
- claude_kit/_payload/skills/manual-test/SKILL.md +516 -0
- claude_kit/_payload/skills/performance-optimization/SKILL.md +277 -0
- claude_kit/_payload/skills/planning-and-task-breakdown/SKILL.md +223 -0
- claude_kit/_payload/skills/playwright-verification/SKILL.md +205 -0
- claude_kit/_payload/skills/refresh-docs/SKILL.md +63 -0
- claude_kit/_payload/skills/remember/SKILL.md +96 -0
- claude_kit/_payload/skills/scope/SKILL.md +52 -0
- claude_kit/_payload/skills/scope/scope-template.md +82 -0
- claude_kit/_payload/skills/sdlc/SKILL.md +83 -0
- claude_kit/_payload/skills/security-and-hardening/SKILL.md +368 -0
- claude_kit/_payload/skills/security-verification/SKILL.md +209 -0
- claude_kit/_payload/skills/shipping-and-launch/SKILL.md +309 -0
- claude_kit/_payload/skills/smoke-test/SKILL.md +78 -0
- claude_kit/_payload/skills/source-driven-development/SKILL.md +195 -0
- claude_kit/_payload/skills/spec-driven-development/SKILL.md +200 -0
- claude_kit/_payload/skills/sprint/SKILL.md +67 -0
- claude_kit/_payload/skills/sprint/sprint-template.md +90 -0
- claude_kit/_payload/skills/test-driven-development/SKILL.md +383 -0
- claude_kit/_payload/skills/threat-model/SKILL.md +60 -0
- claude_kit/_payload/skills/triage/SKILL.md +87 -0
- claude_kit/_payload/skills/ui-ux-design/SKILL.md +71 -0
- claude_kit/_payload/skills/unit-test/SKILL.md +237 -0
- claude_kit/_payload/skills/using-agent-skills/SKILL.md +180 -0
- claude_kit/_payload/templates/CLAUDE.md +238 -0
- claude_kit/_payload/templates/CLAUDE.stack.md.tmpl +53 -0
- claude_kit/_payload/templates/CONTINUITY.template.md +35 -0
- claude_kit/_payload/templates/README.claude-sdlc.md.tmpl +219 -0
- claude_kit/_payload/templates/agent-memory/MEMORY.md +30 -0
- claude_kit/_payload/templates/agent-memory/api/.gitkeep +0 -0
- claude_kit/_payload/templates/agent-memory/architecture/.gitkeep +0 -0
- claude_kit/_payload/templates/agent-memory/debugging/.gitkeep +0 -0
- claude_kit/_payload/templates/agent-memory/gotchas/.gitkeep +0 -0
- claude_kit/_payload/templates/agent-memory/patterns/.gitkeep +0 -0
- claude_kit/_payload/templates/agent-memory/performance/.gitkeep +0 -0
- claude_kit/_payload/templates/artifacts/adr.md +18 -0
- claude_kit/_payload/templates/artifacts/feature-spec.md +29 -0
- claude_kit/_payload/templates/artifacts/release-plan.md +23 -0
- claude_kit/_payload/templates/artifacts/runbook.md +24 -0
- claude_kit/_payload/templates/artifacts/security-review.md +23 -0
- claude_kit/_payload/templates/artifacts/test-plan.md +22 -0
- claude_kit/_payload/templates/org/README.md +53 -0
- claude_kit/_payload/templates/org/agents/data-workflow-agent.md +59 -0
- claude_kit/_payload/templates/org/agents/founder-prototype-agent.md +61 -0
- claude_kit/_payload/templates/org/agents/internal-tools-builder.md +63 -0
- claude_kit/_payload/templates/org/agents/pm-copilot.md +60 -0
- claude_kit/_payload/templates/org/agents/support-ticket-engineer.md +63 -0
- claude_kit/_payload/templates/org/packs/devops-and-release/README.md +46 -0
- claude_kit/_payload/templates/org/packs/devops-and-release/pack.yaml +32 -0
- claude_kit/_payload/templates/org/packs/engineering-core/README.md +46 -0
- claude_kit/_payload/templates/org/packs/engineering-core/pack.yaml +44 -0
- claude_kit/_payload/templates/org/packs/non-engineer-builder/README.md +53 -0
- claude_kit/_payload/templates/org/packs/non-engineer-builder/pack.yaml +39 -0
- claude_kit/_payload/templates/org/packs/onboarding-and-docs/README.md +49 -0
- claude_kit/_payload/templates/org/packs/onboarding-and-docs/pack.yaml +26 -0
- claude_kit/_payload/templates/org/packs/product-to-code/README.md +50 -0
- claude_kit/_payload/templates/org/packs/product-to-code/pack.yaml +34 -0
- claude_kit/_payload/templates/org/packs/quality-and-review/README.md +53 -0
- claude_kit/_payload/templates/org/packs/quality-and-review/pack.yaml +40 -0
- claude_kit/_payload/templates/org/packs/security-and-compliance/README.md +50 -0
- claude_kit/_payload/templates/org/packs/security-and-compliance/pack.yaml +36 -0
- claude_kit/_payload/templates/org/rules/ai-working-agreement.md +45 -0
- claude_kit/_payload/templates/org/rules/ambiguity-resolution.md +36 -0
- claude_kit/_payload/templates/org/rules/branch-and-pr-policy.md +41 -0
- claude_kit/_payload/templates/org/rules/compliance-policy.md +50 -0
- claude_kit/_payload/templates/org/rules/non-engineer-safe-coding.md +37 -0
- claude_kit/_payload/templates/org/rules/pii-policy.md +46 -0
- claude_kit/_payload/templates/org/rules/production-data-policy.md +35 -0
- claude_kit/_payload/templates/org/rules/prompt-to-task-conversion.md +30 -0
- claude_kit/_payload/templates/org/rules/prototype-boundaries.md +40 -0
- claude_kit/_payload/templates/org/rules/secrets-policy.md +34 -0
- claude_kit/_payload/templates/org/skills/customer-issue-to-fix/SKILL.md +61 -0
- claude_kit/_payload/templates/org/skills/feature-from-idea/SKILL.md +56 -0
- claude_kit/_payload/templates/org/skills/prompt-to-safe-task/SKILL.md +59 -0
- claude_kit/_payload/templates/org/skills/prototype-to-production/SKILL.md +61 -0
- claude_kit/_payload/templates/org/skills/repo-onboarding/SKILL.md +60 -0
- claude_kit/_payload/templates/settings.json +53 -0
- claude_kit/_payload/templates/stacks/backend/python/fastapi/rules/fastapi-patterns.md +64 -0
- claude_kit/_payload/templates/stacks/db/mongodb/agents/migration-specialist.md +61 -0
- claude_kit/_payload/templates/stacks/db/mongodb/agents/mongodb-specialist.md +59 -0
- claude_kit/_payload/templates/stacks/db/mongodb/rules/mongodb-patterns.md +39 -0
- claude_kit/_payload/templates/stacks/db/postgres/agents/db-performance-reviewer.md +66 -0
- claude_kit/_payload/templates/stacks/db/postgres/agents/migration-specialist.md +56 -0
- claude_kit/_payload/templates/stacks/db/postgres/agents/postgres-specialist.md +58 -0
- claude_kit/_payload/templates/stacks/db/postgres/rules/database-performance.md +64 -0
- claude_kit/_payload/templates/stacks/db/postgres/rules/postgres-patterns.md +43 -0
- claude_kit/_payload/templates/stacks/frontend/react/rules/react-patterns.md +63 -0
- claude_kit/catalog.py +476 -0
- claude_kit/cli.py +327 -0
- claude_kit/hooks.py +246 -0
- claude_kit/models.py +205 -0
- claude_kit/prompts.py +209 -0
- claude_kit/render.py +146 -0
- claude_kit/scaffold.py +492 -0
- claude_kit/upgrader.py +294 -0
- claude_kit/validator.py +197 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-and-hardening
|
|
3
|
+
description: Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security and Hardening
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Security-first development for any stack. Treat every external input as hostile, every secret as sacred, and every authorization check as mandatory — and in a multi-tenant system (if applicable), treat **every tenant-scoped query without proper scoping as a data breach**. Security isn't a phase; it's a constraint on every line that touches user data, auth, or external systems.
|
|
11
|
+
|
|
12
|
+
Companion rules: `.claude/rules/code-organization.md` (auth & dependency patterns), `.claude/rules/design-patterns.md` (service/repository boundaries), `.claude/rules/documentation.md` (security documentation). The `security-reviewer` agent (Phase 5.4 in `.claude/rules/mandatory-workflow.md`) enforces what this skill teaches.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- Building anything that accepts user input
|
|
17
|
+
- Implementing authentication or authorization
|
|
18
|
+
- Storing or transmitting sensitive data
|
|
19
|
+
- Integrating with external APIs or services
|
|
20
|
+
- Adding file uploads, webhooks, or callbacks
|
|
21
|
+
- Handling PII
|
|
22
|
+
|
|
23
|
+
## The Three-Tier Boundary System
|
|
24
|
+
|
|
25
|
+
### Always Do (No Exceptions)
|
|
26
|
+
|
|
27
|
+
- **Validate all external input** with typed schemas at the boundary (route handlers) — use the project's validation framework to enforce constraints, types, enums for constrained strings
|
|
28
|
+
- **Scope every tenant-restricted query appropriately** — filter by tenant/organization/account ID for multi-tenant systems
|
|
29
|
+
- **Parameterize all queries** — use the ORM's binding mechanism or prepared statements; never string interpolation with user input
|
|
30
|
+
- **Hash passwords with a strong algorithm** — argon2id, bcrypt, or scrypt; never MD5/SHA for new code
|
|
31
|
+
- **Keep async paths fully async** (if applicable) — async database sessions, async HTTP clients, async cache clients (blocking calls stall the event loop)
|
|
32
|
+
- **Set session cookies securely** — `HttpOnly=True`, `SameSite="lax"`, `Secure=True` in production
|
|
33
|
+
- **Restrict CORS** to an explicit origin allowlist
|
|
34
|
+
- **Rate-limit auth endpoints** (register, login, forgot/reset)
|
|
35
|
+
- **Run dependency audits** before every release
|
|
36
|
+
|
|
37
|
+
### Ask First (Requires Human Approval)
|
|
38
|
+
|
|
39
|
+
- Adding or changing authentication / session logic
|
|
40
|
+
- Storing new categories of sensitive data (PII)
|
|
41
|
+
- Adding new external service integrations
|
|
42
|
+
- Changing CORS origins or cookie/session settings
|
|
43
|
+
- Adding file upload handlers
|
|
44
|
+
- Modifying rate limiting or throttling
|
|
45
|
+
- Granting elevated roles (admin, superuser, etc.)
|
|
46
|
+
|
|
47
|
+
### Never Do
|
|
48
|
+
|
|
49
|
+
- **Never commit secrets** — config goes through environment variables or a secrets manager (gitignored)
|
|
50
|
+
- **Never log sensitive data** — no passwords, hashes, full session ids, tokens, or PII in logs
|
|
51
|
+
- **Never trust client-side validation** as a security boundary
|
|
52
|
+
- **Never run a query on a scoped model without the appropriate tenant filter** (multi-tenant systems)
|
|
53
|
+
- **Never use debug print statements** in app code, or render user input as raw HTML without sanitization
|
|
54
|
+
- **Never store auth tokens in browser localStorage** — sessions are cookie-based
|
|
55
|
+
- **Never expose stack traces** or internal errors to clients
|
|
56
|
+
|
|
57
|
+
## OWASP Top 10 Prevention
|
|
58
|
+
|
|
59
|
+
### 1. Injection (SQL / Command)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
# BAD — string-built SQL
|
|
63
|
+
stmt = text(f"SELECT * FROM users WHERE email = '{email}'")
|
|
64
|
+
|
|
65
|
+
# GOOD — ORM with bound params, async
|
|
66
|
+
stmt = select(User).where(User.email == email.lower())
|
|
67
|
+
result = await db.execute(stmt)
|
|
68
|
+
user = result.scalar_one_or_none()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// BAD — string-built query (example for SQL-like ORMs)
|
|
73
|
+
const query = `SELECT * FROM users WHERE email = '${email}'`;
|
|
74
|
+
|
|
75
|
+
// GOOD — parameterized query
|
|
76
|
+
const user = await db.query('SELECT * FROM users WHERE email = $1', [email]);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Broken Authentication
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# Password hashing — argon2id
|
|
83
|
+
from argon2 import PasswordHasher
|
|
84
|
+
|
|
85
|
+
_ph = PasswordHasher() # argon2id defaults
|
|
86
|
+
|
|
87
|
+
def hash_password(password: str) -> str:
|
|
88
|
+
return _ph.hash(password)
|
|
89
|
+
|
|
90
|
+
def verify_password(password: str, password_hash: str) -> bool:
|
|
91
|
+
from argon2.exceptions import VerifyMismatchError
|
|
92
|
+
try:
|
|
93
|
+
return _ph.verify(password_hash, password)
|
|
94
|
+
except VerifyMismatchError:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# Session cookie (set on login) — flags from settings
|
|
98
|
+
response.set_cookie(
|
|
99
|
+
key=settings.SESSION_COOKIE_NAME,
|
|
100
|
+
value=session_id,
|
|
101
|
+
httponly=True,
|
|
102
|
+
samesite="lax",
|
|
103
|
+
secure=not settings.DEBUG, # Secure in production
|
|
104
|
+
max_age=settings.SESSION_TTL_SECONDS,
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 3. Cross-Site Scripting (XSS) — frontend
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
// BAD — renders user input as HTML
|
|
112
|
+
<div dangerouslySetInnerHTML={{ __html: userInput }} />
|
|
113
|
+
|
|
114
|
+
// GOOD — framework auto-escapes by default (React, Vue, Angular)
|
|
115
|
+
<div>{userInput}</div>
|
|
116
|
+
|
|
117
|
+
// If you MUST render HTML, sanitize first
|
|
118
|
+
import DOMPurify from "dompurify";
|
|
119
|
+
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4. Broken Access Control — authn + authz + tenant isolation
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
@router.patch("/v1/tasks/{task_id}", response_model=TaskRead)
|
|
126
|
+
async def update_task(
|
|
127
|
+
task_id: uuid.UUID,
|
|
128
|
+
payload: TaskUpdate,
|
|
129
|
+
current_user: User = Depends(require_auth), # authenticated
|
|
130
|
+
db: AsyncSession = Depends(get_db_session),
|
|
131
|
+
) -> TaskRead:
|
|
132
|
+
# Tenant isolation: scope the lookup to the caller's org — never just by id
|
|
133
|
+
stmt = select(Task).where(
|
|
134
|
+
Task.id == task_id,
|
|
135
|
+
Task.organization_id == current_user.organization_id,
|
|
136
|
+
)
|
|
137
|
+
task = (await db.execute(stmt)).scalar_one_or_none()
|
|
138
|
+
if task is None:
|
|
139
|
+
raise HTTPException(status.HTTP_404_NOT_FOUND, "task not found")
|
|
140
|
+
...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// Example for Node.js / Express-style backend
|
|
145
|
+
app.patch('/v1/tasks/:taskId', authenticate, async (req, res) => {
|
|
146
|
+
const task = await db.tasks.findOne({
|
|
147
|
+
id: req.params.taskId,
|
|
148
|
+
organizationId: req.user.organizationId // tenant scoping
|
|
149
|
+
});
|
|
150
|
+
if (!task) {
|
|
151
|
+
return res.status(404).json({ error: 'task not found' });
|
|
152
|
+
}
|
|
153
|
+
...
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 5. Security Misconfiguration
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
# CORS — allowlist from settings, never "*" with credentials
|
|
161
|
+
app.add_middleware(
|
|
162
|
+
CORSMiddleware,
|
|
163
|
+
allow_origins=settings.CORS_ORIGINS, # e.g. ["http://localhost:3000"]
|
|
164
|
+
allow_credentials=True,
|
|
165
|
+
allow_methods=["*"],
|
|
166
|
+
allow_headers=["*"],
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Security headers (middleware)
|
|
170
|
+
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
171
|
+
response.headers["X-Frame-Options"] = "DENY"
|
|
172
|
+
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
|
173
|
+
# Strict-Transport-Security + Content-Security-Policy at the edge / in prod
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 6. Sensitive Data Exposure
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
# Never expose password_hash / tokens — separate Read schema
|
|
180
|
+
class UserRead(UserBase):
|
|
181
|
+
id: uuid.UUID
|
|
182
|
+
organization_id: uuid.UUID
|
|
183
|
+
created_at: datetime
|
|
184
|
+
model_config = ConfigDict(from_attributes=True)
|
|
185
|
+
# password_hash deliberately absent
|
|
186
|
+
|
|
187
|
+
# Secrets only via environment or settings framework
|
|
188
|
+
from config.settings import settings
|
|
189
|
+
key = settings.SECRET_KEY # never hardcoded
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Input Validation Patterns
|
|
193
|
+
|
|
194
|
+
### Schema Validation at Boundaries
|
|
195
|
+
|
|
196
|
+
Use the project's schema validation framework (e.g., Pydantic for Python, Zod/Yup for TypeScript, JSR-303 for Java):
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
# Python example
|
|
200
|
+
from pydantic import BaseModel, EmailStr, Field, field_validator
|
|
201
|
+
|
|
202
|
+
class UserCreate(BaseModel):
|
|
203
|
+
email: EmailStr
|
|
204
|
+
password: str = Field(min_length=12, max_length=128)
|
|
205
|
+
first_name: str = Field(min_length=1, max_length=100)
|
|
206
|
+
|
|
207
|
+
@field_validator("password")
|
|
208
|
+
@classmethod
|
|
209
|
+
def password_strength(cls, v: str) -> str:
|
|
210
|
+
if not any(c.isupper() for c in v):
|
|
211
|
+
raise ValueError("must contain an uppercase letter")
|
|
212
|
+
if not any(c.isdigit() for c in v):
|
|
213
|
+
raise ValueError("must contain a digit")
|
|
214
|
+
return v
|
|
215
|
+
# The framework returns 422 automatically on validation failure.
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// TypeScript example with Zod
|
|
220
|
+
import { z } from 'zod';
|
|
221
|
+
|
|
222
|
+
const UserCreateSchema = z.object({
|
|
223
|
+
email: z.string().email(),
|
|
224
|
+
password: z.string().min(12).max(128).refine(
|
|
225
|
+
(val) => /[A-Z]/.test(val) && /[0-9]/.test(val),
|
|
226
|
+
{ message: "must contain uppercase letter and digit" }
|
|
227
|
+
),
|
|
228
|
+
firstName: z.string().min(1).max(100),
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### File Upload Safety
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
ALLOWED_TYPES = {"image/jpeg", "image/png", "image/webp"}
|
|
236
|
+
MAX_SIZE = 5 * 1024 * 1024 # 5 MB
|
|
237
|
+
|
|
238
|
+
def validate_upload(content_type: str, size: int) -> None:
|
|
239
|
+
if content_type not in ALLOWED_TYPES:
|
|
240
|
+
raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, "file type not allowed")
|
|
241
|
+
if size > MAX_SIZE:
|
|
242
|
+
raise HTTPException(status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, "file too large (max 5MB)")
|
|
243
|
+
# Don't trust the extension — verify magic bytes if it matters
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Triaging Dependency Audit Results
|
|
247
|
+
|
|
248
|
+
Not every finding is an emergency. Decision tree:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
Vulnerability reported (dependency audit)
|
|
252
|
+
├── Severity: critical or high
|
|
253
|
+
│ ├── Is the vulnerable code path reachable in your app?
|
|
254
|
+
│ │ ├── YES --> Fix immediately (upgrade / patch / replace)
|
|
255
|
+
│ │ └── NO (dev-only dep, unused path) --> Fix soon, not a release blocker
|
|
256
|
+
│ └── Is a fix available?
|
|
257
|
+
│ ├── YES --> Upgrade to the patched version (flag major bumps as breaking)
|
|
258
|
+
│ └── NO --> Workaround, replace the dep, or allowlist with a review date
|
|
259
|
+
├── Severity: moderate
|
|
260
|
+
│ ├── Reachable in prod? --> Fix next release cycle
|
|
261
|
+
│ └── Dev-only? --> Track in backlog
|
|
262
|
+
└── Severity: low --> Fix during regular dependency updates
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Editing dependency manifests requires user approval — the `dependency-scanner` recommends, the developer lane applies. Document any deferral with a reason and a review date.
|
|
266
|
+
|
|
267
|
+
## Rate Limiting (Cache-backed)
|
|
268
|
+
|
|
269
|
+
Apply rate limiting to authentication and public endpoints:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
# Example with Redis-backed rate limiting
|
|
273
|
+
@router.post("/v1/auth/login", response_model=...)
|
|
274
|
+
async def login(
|
|
275
|
+
payload: LoginRequest,
|
|
276
|
+
_: None = Depends(rate_limit("auth:login", max_calls=10, window_seconds=900)), # 10 / 15 min
|
|
277
|
+
db: AsyncSession = Depends(get_db_session),
|
|
278
|
+
) -> ...:
|
|
279
|
+
...
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- Key unauthenticated endpoints by **client IP**; authenticated by **user id**.
|
|
283
|
+
- Cover `register`, `login`, `forgot-password`, `reset-password`.
|
|
284
|
+
|
|
285
|
+
## Secrets Management
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
.env files:
|
|
289
|
+
├── .env.example → committed (placeholder values only)
|
|
290
|
+
├── .env → NOT committed (real secrets)
|
|
291
|
+
└── .env.*.local → NOT committed
|
|
292
|
+
|
|
293
|
+
.gitignore must include: .env, .env.local, .env.*.local, *.pem, *.key
|
|
294
|
+
All config is read via the project's settings framework — never scattered environment reads.
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
# Before committing — check for staged secrets
|
|
299
|
+
git diff --cached | grep -iE "password|secret|api_key|token|SECRET_KEY|DATABASE_URL"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Security Review Checklist
|
|
303
|
+
|
|
304
|
+
```markdown
|
|
305
|
+
### Authentication
|
|
306
|
+
- [ ] Passwords hashed with a strong algorithm (argon2id, bcrypt, scrypt)
|
|
307
|
+
- [ ] Session cookies HttpOnly + SameSite=Lax + Secure(prod)
|
|
308
|
+
- [ ] Login + forgot/reset rate-limited
|
|
309
|
+
- [ ] Password-reset tokens expire
|
|
310
|
+
|
|
311
|
+
### Authorization & Tenancy
|
|
312
|
+
- [ ] Every endpoint uses the auth chain (authentication + authorization)
|
|
313
|
+
- [ ] EVERY tenant-scoped query filters by tenant/organization/account ID (multi-tenant systems)
|
|
314
|
+
- [ ] Users cannot reach another tenant's resources (no IDOR)
|
|
315
|
+
- [ ] Create schemas don't accept server-owned fields (id, tenant_id, etc.)
|
|
316
|
+
|
|
317
|
+
### Input
|
|
318
|
+
- [ ] All request bodies validated with typed schemas and constraints
|
|
319
|
+
- [ ] Queries parameterized (no string interpolation with user input)
|
|
320
|
+
- [ ] Frontend: no raw HTML rendering with user data
|
|
321
|
+
|
|
322
|
+
### Data & Logging
|
|
323
|
+
- [ ] No secrets in code or git history (environment variables / secrets manager)
|
|
324
|
+
- [ ] Read schemas exclude password_hash / tokens
|
|
325
|
+
- [ ] Logs never contain passwords, hashes, session ids, or PII
|
|
326
|
+
|
|
327
|
+
### Infrastructure
|
|
328
|
+
- [ ] CORS origins allowlist (no "*")
|
|
329
|
+
- [ ] Security headers set (X-Content-Type-Options, X-Frame-Options, HSTS, CSP)
|
|
330
|
+
- [ ] Dependency audits clean of Critical/High
|
|
331
|
+
- [ ] Error responses don't leak internals
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
See `.claude/skills/_references/security-checklist.md` for the full pre-commit checklist.
|
|
335
|
+
|
|
336
|
+
## Common Rationalizations
|
|
337
|
+
|
|
338
|
+
| Rationalization | Reality |
|
|
339
|
+
|---|---|
|
|
340
|
+
| "This is an internal tool, security doesn't matter" | Internal tools get compromised. Attackers target the weakest link. |
|
|
341
|
+
| "We'll add security later" | Retrofitting is 10x harder. Add it now. |
|
|
342
|
+
| "No one would try to exploit this" | Automated scanners will find it. Obscurity is not security. |
|
|
343
|
+
| "The framework handles security" | Frameworks provide tools, not guarantees — raw queries and missing tenant filters still leak. |
|
|
344
|
+
| "It's just a prototype" | Prototypes become production. Security habits from day one. |
|
|
345
|
+
|
|
346
|
+
## Red Flags
|
|
347
|
+
|
|
348
|
+
- A query on a scoped model with no tenant/organization/account filter (multi-tenant systems)
|
|
349
|
+
- Raw SQL or string interpolation building queries; user input in system commands
|
|
350
|
+
- Secrets in source, config files, or commit history
|
|
351
|
+
- Endpoints missing authentication / authorization enforcement
|
|
352
|
+
- CORS origins set to `*`, or no rate limiting on auth endpoints
|
|
353
|
+
- Logs that include passwords, tokens, or PII
|
|
354
|
+
- Blocking calls in async request paths (if async architecture)
|
|
355
|
+
- Stack traces or internal errors returned to clients
|
|
356
|
+
|
|
357
|
+
## Verification
|
|
358
|
+
|
|
359
|
+
After implementing security-relevant code:
|
|
360
|
+
|
|
361
|
+
- [ ] Run dependency audit — no Critical/High vulnerabilities
|
|
362
|
+
- [ ] No secrets in source or git history
|
|
363
|
+
- [ ] Every tenant-scoped query filters by tenant/organization/account ID (multi-tenant systems)
|
|
364
|
+
- [ ] All input validated via typed schemas at the boundary
|
|
365
|
+
- [ ] Auth + authz enforced on every protected endpoint
|
|
366
|
+
- [ ] Passwords hashed with strong algorithm; session cookies HttpOnly/SameSite/Secure
|
|
367
|
+
- [ ] Logs contain no secrets/PII; error responses don't expose internals
|
|
368
|
+
- [ ] Rate limiting active on auth endpoints
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-verification
|
|
3
|
+
description: Verify and enforce input sanitization and security best practices across all user input surfaces — forms, textareas, query params, URL params, and external data, for any web stack.
|
|
4
|
+
argument-hint: [component, page, or "all"]
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Run security verification on: $ARGUMENTS.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Identify all input surfaces**: Search the target code for every point where user input enters the application.
|
|
13
|
+
|
|
14
|
+
### Input Sources to Check
|
|
15
|
+
Adjust patterns to match your project's framework/language:
|
|
16
|
+
```bash
|
|
17
|
+
# Search for all input surfaces (examples for web frontends)
|
|
18
|
+
grep -rn '<Input\|<input\|<textarea\|<Textarea' src/
|
|
19
|
+
grep -rn 'useForm\|useSearchParams\|useParams\|queryParam' src/ # React/React Router
|
|
20
|
+
grep -rn 'params\|request.args\|request.form' . # Flask/Django/FastAPI
|
|
21
|
+
grep -rn 'dangerouslySetInnerHTML\|innerHTML\|outerHTML' src/
|
|
22
|
+
grep -rn 'window.location\|document.cookie\|localStorage\|sessionStorage' src/
|
|
23
|
+
grep -rn 'eval\|new Function\|document.write' src/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. **Audit each input source against the checklist**:
|
|
27
|
+
|
|
28
|
+
### Form Inputs (`<input>`, `<Input>`, or framework equivalents)
|
|
29
|
+
| Check | Status | Fix |
|
|
30
|
+
|-------|--------|-----|
|
|
31
|
+
| Schema validation on submit (Zod/Joi/Yup/Pydantic/etc.) | | Add validation schema with `.trim().min(1)` or equivalent |
|
|
32
|
+
| `maxLength` attribute/constraint set | | Add length limit |
|
|
33
|
+
| HTML entities escaped (auto in most modern frameworks) | | Verify template engine auto-escapes |
|
|
34
|
+
| No raw value used in `href` or `src` | | Validate URL format before use |
|
|
35
|
+
| Input type matches expected data | | Use semantic types (email, number, etc.) |
|
|
36
|
+
|
|
37
|
+
### Textareas (multi-line input fields)
|
|
38
|
+
| Check | Status | Fix |
|
|
39
|
+
|-------|--------|-----|
|
|
40
|
+
| Schema validation on submit with max length | | Add `.trim().max(N)` or equivalent |
|
|
41
|
+
| `maxLength` attribute/constraint set | | Add length limit |
|
|
42
|
+
| Content not rendered as raw HTML | | Use text rendering, never HTML injection |
|
|
43
|
+
|
|
44
|
+
### Form Submissions (validation libraries: react-hook-form, Formik, WTForms, etc.)
|
|
45
|
+
| Check | Status | Fix |
|
|
46
|
+
|-------|--------|-----|
|
|
47
|
+
| Schema validation attached to form handler | | Add validator integration |
|
|
48
|
+
| All fields validated before processing | | Use framework's validation wrapper |
|
|
49
|
+
| Error messages shown for invalid input | | Display validation errors to user |
|
|
50
|
+
| Submit button disabled during submission | | Use framework's submission state |
|
|
51
|
+
|
|
52
|
+
### URL Query Parameters (framework routing/request parsing)
|
|
53
|
+
| Check | Status | Fix |
|
|
54
|
+
|-------|--------|-----|
|
|
55
|
+
| Parsed with safe API (URLSearchParams, framework request parsers) | | Never parse `window.location` or raw query strings manually |
|
|
56
|
+
| Values validated/cast before use | | Coerce to expected type with validation |
|
|
57
|
+
| Default values for missing params | | Use `.get('key', default)` or equivalent |
|
|
58
|
+
| Not used in `href`/`src` without validation | | Validate URL format |
|
|
59
|
+
|
|
60
|
+
### URL Path Parameters (framework routing)
|
|
61
|
+
| Check | Status | Fix |
|
|
62
|
+
|-------|--------|-----|
|
|
63
|
+
| Type-checked after extraction | | Cast/validate params after extraction |
|
|
64
|
+
| Validated against expected pattern | | Check format/range before use |
|
|
65
|
+
| Not interpolated into URLs unsafely | | Use URL encoding (encodeURIComponent or equivalent) |
|
|
66
|
+
|
|
67
|
+
### Dangerous Patterns (universal across web stacks)
|
|
68
|
+
| Pattern | Risk | Fix |
|
|
69
|
+
|---------|------|-----|
|
|
70
|
+
| `dangerouslySetInnerHTML` (React), `{% autoescape off %}` (Jinja), `{!! $var !!}` (Blade) | XSS | Remove or sanitize with DOMPurify/bleach/equivalent |
|
|
71
|
+
| `innerHTML` / `outerHTML` | XSS | Use framework's safe template rendering |
|
|
72
|
+
| `document.write` | XSS | Never use |
|
|
73
|
+
| `eval()` / `new Function()` / `exec()` / `system()` | Code injection | Never use with user input |
|
|
74
|
+
| `window.location = userInput` | Open redirect | Validate against allowlist |
|
|
75
|
+
| Template literals in `href` without validation | XSS via `javascript:` protocol | Validate URL scheme |
|
|
76
|
+
|
|
77
|
+
3. **Check for OWASP Top 10 frontend/API concerns**:
|
|
78
|
+
|
|
79
|
+
| # | Vulnerability | Check |
|
|
80
|
+
|---|--------------|----------------|
|
|
81
|
+
| A03 | Injection (XSS, SQL, Command) | No raw HTML rendering of user input; parameterized queries; no shell command interpolation |
|
|
82
|
+
| A05 | Security Misconfiguration | No secrets in client-side code or version control |
|
|
83
|
+
| A07 | Cross-Site Scripting | All outputs escaped, no unsafe HTML injection |
|
|
84
|
+
| A08 | Software Integrity | Dependencies from trusted sources, audit tools clean (npm audit, pip-audit, etc.) |
|
|
85
|
+
| A09 | Logging Failures | No sensitive data in logs (passwords, tokens, PII) |
|
|
86
|
+
|
|
87
|
+
4. **Verify sanitization utilities exist**: Check the project's utility module for sanitization helpers. If missing, add stack-appropriate versions:
|
|
88
|
+
|
|
89
|
+
**Example (TypeScript/JavaScript):**
|
|
90
|
+
```typescript
|
|
91
|
+
// Sanitize user input for display
|
|
92
|
+
export function sanitizeInput(input: string): string {
|
|
93
|
+
return input.trim().replace(/[<>]/g, '');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Validate URL is safe (no javascript: protocol)
|
|
97
|
+
export function isSafeUrl(url: string): boolean {
|
|
98
|
+
try {
|
|
99
|
+
const parsed = new URL(url, window.location.origin);
|
|
100
|
+
return ['http:', 'https:'].includes(parsed.protocol);
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Encode value for URL parameter
|
|
107
|
+
export function safeParam(value: string): string {
|
|
108
|
+
return encodeURIComponent(value.trim());
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Example (Python):**
|
|
113
|
+
```python
|
|
114
|
+
from urllib.parse import urlparse, quote
|
|
115
|
+
from bleach import clean
|
|
116
|
+
|
|
117
|
+
def sanitize_input(input: str) -> str:
|
|
118
|
+
return clean(input.strip(), tags=[], strip=True)
|
|
119
|
+
|
|
120
|
+
def is_safe_url(url: str) -> bool:
|
|
121
|
+
try:
|
|
122
|
+
parsed = urlparse(url)
|
|
123
|
+
return parsed.scheme in {'http', 'https'}
|
|
124
|
+
except Exception:
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def safe_param(value: str) -> str:
|
|
128
|
+
return quote(value.strip(), safe='')
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
5. **Report findings**: Output a security audit table:
|
|
132
|
+
|
|
133
|
+
| File | Line | Input Source | Vulnerability | Severity | Fix |
|
|
134
|
+
|------|------|-------------|---------------|----------|-----|
|
|
135
|
+
|
|
136
|
+
Severity levels:
|
|
137
|
+
- **Critical**: Direct XSS vector or code injection
|
|
138
|
+
- **High**: Missing validation on user input that affects application state
|
|
139
|
+
- **Medium**: Missing sanitization on display-only input
|
|
140
|
+
- **Low**: Missing length limit or type attribute
|
|
141
|
+
|
|
142
|
+
6. **Apply fixes**: For each finding, apply the recommended fix. Prioritize Critical and High first.
|
|
143
|
+
|
|
144
|
+
7. **Verify**: Run the project's linter, type checker, and build to confirm the changes are valid.
|
|
145
|
+
|
|
146
|
+
## Quick Reference: Secure Input Pattern (React + Zod example)
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { useForm } from 'react-hook-form';
|
|
150
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
151
|
+
import { z } from 'zod';
|
|
152
|
+
import { Input } from '@/components/ui';
|
|
153
|
+
|
|
154
|
+
const schema = z.object({
|
|
155
|
+
name: z.string().trim().min(1, 'Name is required').max(100),
|
|
156
|
+
email: z.string().trim().email('Invalid email').max(255),
|
|
157
|
+
notes: z.string().trim().max(1000).optional(),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
type FormData = z.infer<typeof schema>;
|
|
161
|
+
|
|
162
|
+
function SecureForm() {
|
|
163
|
+
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
|
|
164
|
+
resolver: zodResolver(schema),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const onSubmit = (data: FormData) => {
|
|
168
|
+
// data is validated and typed — safe to use
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
173
|
+
<Input {...register('name')} maxLength={100} />
|
|
174
|
+
{errors.name && <p className="text-sm text-red-600">{errors.name.message}</p>}
|
|
175
|
+
|
|
176
|
+
<Input {...register('email')} type="email" maxLength={255} />
|
|
177
|
+
{errors.email && <p className="text-sm text-red-600">{errors.email.message}</p>}
|
|
178
|
+
|
|
179
|
+
<textarea {...register('notes')} maxLength={1000} />
|
|
180
|
+
{errors.notes && <p className="text-sm text-red-600">{errors.notes.message}</p>}
|
|
181
|
+
|
|
182
|
+
<button type="submit" disabled={isSubmitting}>Submit</button>
|
|
183
|
+
</form>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Quick Reference: Secure Input Pattern (Python/FastAPI + Pydantic example)
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
192
|
+
|
|
193
|
+
class UserCreate(BaseModel):
|
|
194
|
+
name: str = Field(min_length=1, max_length=100)
|
|
195
|
+
email: EmailStr = Field(max_length=255)
|
|
196
|
+
notes: str | None = Field(None, max_length=1000)
|
|
197
|
+
|
|
198
|
+
@router.post("/users", response_model=UserRead, status_code=201)
|
|
199
|
+
async def create_user(payload: UserCreate) -> UserRead:
|
|
200
|
+
# payload is validated and typed — safe to use
|
|
201
|
+
# Pydantic auto-trims and validates
|
|
202
|
+
...
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## References
|
|
206
|
+
|
|
207
|
+
- OWASP Top 10: https://owasp.org/www-project-top-ten/
|
|
208
|
+
- OWASP XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
|
|
209
|
+
- Validation libraries: Zod (TS/JS), Joi (JS), Pydantic (Python), WTForms (Python), Bean Validation (Java)
|