devflow-kit 0.3.3 → 0.4.0

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.
@@ -0,0 +1,514 @@
1
+ ---
2
+ name: input-validation
3
+ description: Automatically enforce input validation at system boundaries when handling user input, API endpoints, or external data. Use when creating API routes, processing form data, or integrating with external services. Enforces parse-don't-validate pattern.
4
+ allowed-tools: Read, Grep, Glob, AskUserQuestion
5
+ ---
6
+
7
+ # Input Validation Skill
8
+
9
+ ## Purpose
10
+
11
+ Enforce security-critical validation at all system boundaries:
12
+ 1. **Parse-don't-validate** - Use schema validation, not manual checks
13
+ 2. **Boundary enforcement** - Validate at entry points only
14
+ 3. **Type safety** - Leverage type system after validation
15
+ 4. **Security first** - Prevent injection, overflow, and malformed data
16
+
17
+ ## When This Skill Activates
18
+
19
+ Automatically triggers when:
20
+ - Creating API endpoints or routes
21
+ - Processing user-submitted data (forms, uploads, etc.)
22
+ - Integrating with external APIs
23
+ - Accepting configuration or environment variables
24
+ - Handling database queries with user input
25
+ - Processing command-line arguments
26
+
27
+ ## Core Principle: Parse, Don't Validate
28
+
29
+ **CRITICAL**: Use schema validation libraries, not manual checks.
30
+
31
+ ```typescript
32
+ // ❌ VIOLATION: Manual validation scatters checks
33
+ function createUser(data: any): User {
34
+ if (!data.email || typeof data.email !== 'string') {
35
+ throw new Error('Invalid email');
36
+ }
37
+ if (!data.age || typeof data.age !== 'number' || data.age < 0) {
38
+ throw new Error('Invalid age');
39
+ }
40
+ if (!data.name || data.name.length > 100) {
41
+ throw new Error('Invalid name');
42
+ }
43
+ // ... more manual checks
44
+
45
+ return { email: data.email, age: data.age, name: data.name };
46
+ }
47
+
48
+ // ✅ CORRECT: Schema validation at boundary
49
+ import { z } from 'zod';
50
+
51
+ const UserSchema = z.object({
52
+ email: z.string().email().max(255),
53
+ age: z.number().int().min(0).max(150),
54
+ name: z.string().min(1).max(100)
55
+ });
56
+
57
+ type User = z.infer<typeof UserSchema>;
58
+
59
+ function createUser(data: unknown): Result<User, ValidationError> {
60
+ const validation = UserSchema.safeParse(data);
61
+
62
+ if (!validation.success) {
63
+ return {
64
+ ok: false,
65
+ error: new ValidationError('Invalid user data', validation.error)
66
+ };
67
+ }
68
+
69
+ // After this point, data is guaranteed valid User type
70
+ return { ok: true, value: validation.data };
71
+ }
72
+ ```
73
+
74
+ ## Boundary Detection
75
+
76
+ ### API Endpoints (Critical Boundary)
77
+
78
+ ```typescript
79
+ // ❌ VIOLATION: No validation at API boundary
80
+ app.post('/api/users', async (req, res) => {
81
+ const user = await createUser(req.body); // Trusting external data!
82
+ res.json(user);
83
+ });
84
+
85
+ // ✅ CORRECT: Validation at boundary
86
+ const CreateUserRequestSchema = z.object({
87
+ body: UserSchema
88
+ });
89
+
90
+ app.post('/api/users', async (req, res) => {
91
+ const validation = CreateUserRequestSchema.safeParse(req);
92
+
93
+ if (!validation.success) {
94
+ return res.status(400).json({
95
+ error: 'Validation failed',
96
+ details: validation.error.issues
97
+ });
98
+ }
99
+
100
+ // Now req.body is safely typed as User
101
+ const result = await createUser(validation.data.body);
102
+
103
+ if (!result.ok) {
104
+ return res.status(500).json({ error: result.error.message });
105
+ }
106
+
107
+ res.json(result.value);
108
+ });
109
+ ```
110
+
111
+ ### External API Integration (Boundary)
112
+
113
+ ```typescript
114
+ // ❌ VIOLATION: Trusting external API response
115
+ async function fetchUserData(userId: string): Promise<UserData> {
116
+ const response = await fetch(`https://api.example.com/users/${userId}`);
117
+ const data = await response.json();
118
+ return data; // No validation!
119
+ }
120
+
121
+ // ✅ CORRECT: Validate external data
122
+ const ExternalUserSchema = z.object({
123
+ id: z.string().uuid(),
124
+ name: z.string(),
125
+ email: z.string().email(),
126
+ // Define exact structure we expect
127
+ });
128
+
129
+ async function fetchUserData(userId: string): Promise<Result<UserData, Error>> {
130
+ try {
131
+ const response = await fetch(`https://api.example.com/users/${userId}`);
132
+ const rawData = await response.json();
133
+
134
+ const validation = ExternalUserSchema.safeParse(rawData);
135
+
136
+ if (!validation.success) {
137
+ return {
138
+ ok: false,
139
+ error: new Error('External API returned invalid data')
140
+ };
141
+ }
142
+
143
+ return { ok: true, value: validation.data };
144
+ } catch (error) {
145
+ return { ok: false, error: error as Error };
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### Environment Variables (Boundary)
151
+
152
+ ```typescript
153
+ // ❌ VIOLATION: Trusting environment variables
154
+ const config = {
155
+ port: process.env.PORT, // Could be undefined or invalid
156
+ dbUrl: process.env.DATABASE_URL, // No validation
157
+ apiKey: process.env.API_KEY // Could be empty or malformed
158
+ };
159
+
160
+ // ✅ CORRECT: Validate configuration
161
+ const ConfigSchema = z.object({
162
+ port: z.string().regex(/^\d+$/).transform(Number).pipe(z.number().min(1).max(65535)),
163
+ dbUrl: z.string().url().startsWith('postgresql://'),
164
+ apiKey: z.string().min(32).max(128)
165
+ });
166
+
167
+ function loadConfig(): Result<Config, Error> {
168
+ const validation = ConfigSchema.safeParse({
169
+ port: process.env.PORT,
170
+ dbUrl: process.env.DATABASE_URL,
171
+ apiKey: process.env.API_KEY
172
+ });
173
+
174
+ if (!validation.success) {
175
+ return {
176
+ ok: false,
177
+ error: new Error(`Invalid configuration: ${validation.error.message}`)
178
+ };
179
+ }
180
+
181
+ return { ok: true, value: validation.data };
182
+ }
183
+
184
+ // Application initialization
185
+ const configResult = loadConfig();
186
+ if (!configResult.ok) {
187
+ console.error('Failed to load configuration:', configResult.error);
188
+ process.exit(1);
189
+ }
190
+
191
+ const config = configResult.value; // Type-safe, validated config
192
+ ```
193
+
194
+ ### Database Queries (SQL Injection Prevention)
195
+
196
+ ```typescript
197
+ // ❌ VIOLATION: Direct string interpolation (SQL injection risk)
198
+ async function getUserByEmail(email: string): Promise<User> {
199
+ const query = `SELECT * FROM users WHERE email = '${email}'`;
200
+ return db.query(query);
201
+ }
202
+
203
+ // ❌ VIOLATION: No input validation before query
204
+ async function searchUsers(searchTerm: string): Promise<User[]> {
205
+ return db.query('SELECT * FROM users WHERE name LIKE $1', [`%${searchTerm}%`]);
206
+ }
207
+
208
+ // ✅ CORRECT: Validate input + parameterized query
209
+ const EmailSchema = z.string().email().max(255);
210
+ const SearchTermSchema = z.string().min(1).max(100).regex(/^[a-zA-Z0-9\s-]+$/);
211
+
212
+ async function getUserByEmail(email: unknown): Promise<Result<User, Error>> {
213
+ const validation = EmailSchema.safeParse(email);
214
+
215
+ if (!validation.success) {
216
+ return { ok: false, error: new Error('Invalid email format') };
217
+ }
218
+
219
+ try {
220
+ // Parameterized query prevents SQL injection
221
+ const user = await db.query('SELECT * FROM users WHERE email = $1', [validation.data]);
222
+ return { ok: true, value: user };
223
+ } catch (error) {
224
+ return { ok: false, error: error as Error };
225
+ }
226
+ }
227
+
228
+ async function searchUsers(searchTerm: unknown): Promise<Result<User[], Error>> {
229
+ const validation = SearchTermSchema.safeParse(searchTerm);
230
+
231
+ if (!validation.success) {
232
+ return { ok: false, error: new Error('Invalid search term') };
233
+ }
234
+
235
+ try {
236
+ const users = await db.query(
237
+ 'SELECT * FROM users WHERE name ILIKE $1',
238
+ [`%${validation.data}%`]
239
+ );
240
+ return { ok: true, value: users };
241
+ } catch (error) {
242
+ return { ok: false, error: error as Error };
243
+ }
244
+ }
245
+ ```
246
+
247
+ ## Validation Libraries
248
+
249
+ Recommended schema validation libraries by language:
250
+
251
+ **TypeScript/JavaScript**:
252
+ - Zod (recommended)
253
+ - Yup
254
+ - joi
255
+ - io-ts
256
+
257
+ **Python**:
258
+ - Pydantic (recommended)
259
+ - marshmallow
260
+ - dataclasses with validation
261
+
262
+ **Go**:
263
+ - go-playground/validator
264
+ - ozzo-validation
265
+
266
+ **Rust**:
267
+ - serde with validation
268
+ - validator crate
269
+
270
+ ## Validation Report Format
271
+
272
+ When validation issues detected:
273
+
274
+ ```markdown
275
+ 🚨 INPUT VALIDATION ISSUES DETECTED
276
+
277
+ ## 🔴 CRITICAL - Missing Boundary Validation
278
+ **File**: src/api/routes/users.ts:45
279
+ **Issue**: API endpoint accepts unvalidated user input
280
+ **Security Risk**: HIGH - Injection attacks, data corruption possible
281
+
282
+ **Current Code**:
283
+ ```typescript
284
+ app.post('/api/users', async (req, res) => {
285
+ const user = await createUser(req.body); // NO VALIDATION
286
+ res.json(user);
287
+ });
288
+ ```
289
+
290
+ **Required Fix**:
291
+ ```typescript
292
+ const UserRequestSchema = z.object({
293
+ body: z.object({
294
+ email: z.string().email().max(255),
295
+ name: z.string().min(1).max(100),
296
+ age: z.number().int().min(0).max(150)
297
+ })
298
+ });
299
+
300
+ app.post('/api/users', async (req, res) => {
301
+ const validation = UserRequestSchema.safeParse(req);
302
+
303
+ if (!validation.success) {
304
+ return res.status(400).json({ error: validation.error });
305
+ }
306
+
307
+ const result = await createUser(validation.data.body);
308
+ // ... handle result
309
+ });
310
+ ```
311
+
312
+ **Impact**: Prevents malicious input, ensures data integrity
313
+
314
+ ## 🔴 CRITICAL - Manual Validation Instead of Schema
315
+ **File**: src/services/validation.ts:23
316
+ **Issue**: Manual type checking instead of schema validation
317
+ **Problem**: Scattered validation logic, incomplete checks
318
+
319
+ **Current Code**:
320
+ ```typescript
321
+ if (!data.email || typeof data.email !== 'string') {
322
+ throw new Error('Invalid email');
323
+ }
324
+ if (!data.age || typeof data.age !== 'number') {
325
+ throw new Error('Invalid age');
326
+ }
327
+ // ... 15 more manual checks
328
+ ```
329
+
330
+ **Required Fix**:
331
+ ```typescript
332
+ const UserSchema = z.object({
333
+ email: z.string().email().max(255),
334
+ age: z.number().int().min(0).max(150),
335
+ name: z.string().min(1).max(100),
336
+ // All validation rules in one place
337
+ });
338
+
339
+ const validation = UserSchema.safeParse(data);
340
+ if (!validation.success) {
341
+ return { ok: false, error: validation.error };
342
+ }
343
+ ```
344
+
345
+ **Impact**: Centralized validation, type safety, better error messages
346
+
347
+ ## 🔴 CRITICAL - SQL Injection Risk
348
+ **File**: src/database/queries.ts:67
349
+ **Issue**: String interpolation in SQL query
350
+ **Security Risk**: CRITICAL - SQL injection possible
351
+
352
+ **Current Code**:
353
+ ```typescript
354
+ const query = `SELECT * FROM users WHERE email = '${email}'`;
355
+ ```
356
+
357
+ **Required Fix**:
358
+ ```typescript
359
+ // 1. Validate input
360
+ const validation = EmailSchema.safeParse(email);
361
+ if (!validation.success) {
362
+ return { ok: false, error: new Error('Invalid email') };
363
+ }
364
+
365
+ // 2. Use parameterized query
366
+ const query = 'SELECT * FROM users WHERE email = $1';
367
+ const result = await db.query(query, [validation.data]);
368
+ ```
369
+
370
+ **Impact**: Prevents SQL injection attacks (critical security issue)
371
+
372
+ ## 🟡 HIGH - External API Response Not Validated
373
+ **File**: src/integrations/payment.ts:89
374
+ **Issue**: Trusting external API response without validation
375
+ **Risk**: Application crash if API changes structure
376
+
377
+ **Current Code**:
378
+ ```typescript
379
+ const data = await response.json();
380
+ return data.amount; // No validation
381
+ ```
382
+
383
+ **Required Fix**:
384
+ ```typescript
385
+ const PaymentResponseSchema = z.object({
386
+ amount: z.number().positive(),
387
+ currency: z.string().length(3),
388
+ status: z.enum(['success', 'failed', 'pending'])
389
+ });
390
+
391
+ const validation = PaymentResponseSchema.safeParse(await response.json());
392
+ if (!validation.success) {
393
+ return { ok: false, error: new Error('Invalid payment response') };
394
+ }
395
+
396
+ return { ok: true, value: validation.data.amount };
397
+ ```
398
+
399
+ ## 📊 Summary
400
+ - **Critical**: 8 validation issues (6 missing, 2 SQL injection risks)
401
+ - **High**: 4 external data issues
402
+ - **Security Risk**: CRITICAL (SQL injection possible)
403
+ - **Files affected**: 7
404
+
405
+ ## 🛑 SECURITY GATE FAILED
406
+
407
+ These validation gaps create serious security vulnerabilities:
408
+ 1. SQL injection possible in 2 locations
409
+ 2. Unvalidated user input in 6 API endpoints
410
+ 3. External data trusted without validation
411
+
412
+ **DO NOT deploy until these are fixed.**
413
+
414
+ ## ✅ Required Actions
415
+
416
+ 1. **Immediate** (Security Critical):
417
+ - Fix SQL injection risks (2 locations)
418
+ - Add validation to all API endpoints
419
+
420
+ 2. **High Priority**:
421
+ - Validate all external API responses
422
+ - Validate environment variables on startup
423
+
424
+ 3. **Standard**:
425
+ - Replace manual validation with schemas
426
+ - Add validation tests
427
+
428
+ ## 📚 Implementation Guide
429
+
430
+ **Step 1**: Install validation library
431
+ ```bash
432
+ npm install zod # or appropriate library
433
+ ```
434
+
435
+ **Step 2**: Define schemas for all boundaries
436
+ ```typescript
437
+ // src/validation/schemas.ts
438
+ export const schemas = {
439
+ createUser: UserSchema,
440
+ updateUser: UpdateUserSchema,
441
+ searchQuery: SearchQuerySchema,
442
+ // ... all input shapes
443
+ };
444
+ ```
445
+
446
+ **Step 3**: Apply at boundaries
447
+ ```typescript
448
+ // Validate at entry point
449
+ const validation = schema.safeParse(input);
450
+ // Check result and proceed
451
+ ```
452
+
453
+ **Step 4**: Add tests
454
+ ```typescript
455
+ // Verify validation catches invalid input
456
+ ```
457
+ ```
458
+
459
+ ## Validation Checklist
460
+
461
+ Before declaring endpoint/integration complete:
462
+
463
+ - ✅ All user input validated with schema
464
+ - ✅ External API responses validated
465
+ - ✅ Environment variables validated on startup
466
+ - ✅ Database queries use parameterized statements
467
+ - ✅ File uploads validated (type, size, content)
468
+ - ✅ URL parameters validated
469
+ - ✅ Query strings validated
470
+ - ✅ Request headers validated (if used for logic)
471
+
472
+ ## Integration Points
473
+
474
+ This skill works with:
475
+
476
+ **pattern-check**: Ensures validation uses Result types
477
+ **code-smell**: Catches fake/incomplete validation
478
+ **test-design**: Validates boundary tests exist
479
+ **error-handling**: Ensures validation errors handled consistently
480
+
481
+ ## Security Principles
482
+
483
+ 1. **Trust Nothing**: All external data is potentially malicious
484
+ 2. **Validate Once**: At the boundary, then trust typed data
485
+ 3. **Fail Secure**: Invalid input = reject, not accept with warning
486
+ 4. **Clear Errors**: Help legitimate users fix issues
487
+ 5. **No Bypass**: No "skip validation" flags or backdoors
488
+
489
+ ## Success Criteria
490
+
491
+ Input validation passes when:
492
+ - ✅ All boundaries identified and validated
493
+ - ✅ Schema validation used (not manual checks)
494
+ - ✅ No SQL injection risks
495
+ - ✅ External data validated before use
496
+ - ✅ Configuration validated on startup
497
+ - ✅ Validation errors return Result types
498
+ - ✅ Tests cover invalid input scenarios
499
+
500
+ ## Example Scenario
501
+
502
+ ```
503
+ User: "Add API endpoint to create orders"
504
+ → input-validation activates
505
+ → Analyzes: New API endpoint = boundary
506
+ → Checks: Is request body validated?
507
+ → Reports: Missing validation
508
+ → Blocks: Until schema validation added
509
+ → Verifies: Validation implemented correctly
510
+ → Confirms: SQL queries parameterized
511
+ → Approves: Safe to proceed
512
+ ```
513
+
514
+ This prevents shipping security vulnerabilities.