@mmerterden/multi-agent-pipeline 20.1.0 → 20.2.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/docs/facts.json +5 -5
  3. package/manifest.json +52 -31
  4. package/package.json +1 -1
  5. package/pipeline/skills/.skill-manifest.json +36 -20
  6. package/pipeline/skills/.skills-index.json +75 -9
  7. package/pipeline/skills/shared/README.md +13 -7
  8. package/pipeline/skills/shared/external/android-architecture/SKILL.md +71 -0
  9. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +142 -0
  10. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +314 -0
  11. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +432 -0
  12. package/pipeline/skills/shared/external/android-datastore/SKILL.md +236 -0
  13. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +297 -0
  14. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +249 -0
  15. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +270 -0
  16. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +62 -0
  17. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +255 -0
  18. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +257 -0
  19. package/pipeline/skills/shared/external/android-performance/SKILL.md +86 -602
  20. package/pipeline/skills/shared/external/android-performance/references/patterns.md +659 -0
  21. package/pipeline/skills/shared/external/android-security/SKILL.md +117 -430
  22. package/pipeline/skills/shared/external/android-security/references/patterns.md +690 -0
  23. package/pipeline/skills/shared/external/{android_ui_verification → android-ui-verification}/SKILL.md +1 -1
  24. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +35 -733
  25. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +299 -0
  26. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +255 -0
  27. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +167 -0
  28. package/pipeline/skills/shared/external/app-intents/SKILL.md +39 -174
  29. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +178 -0
  30. package/pipeline/skills/shared/external/compose-components/SKILL.md +48 -0
  31. package/pipeline/skills/shared/external/compose-components/references/patterns.md +200 -0
  32. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +66 -3
  33. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +191 -0
  34. package/pipeline/skills/shared/external/compose-testing/SKILL.md +107 -397
  35. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +631 -0
  36. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +121 -449
  37. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +715 -0
  38. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +143 -0
  39. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +27 -102
  40. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +42 -0
  41. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +94 -383
  42. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +640 -0
  43. package/pipeline/skills/shared/external/room-database/SKILL.md +101 -440
  44. package/pipeline/skills/shared/external/room-database/references/patterns.md +614 -0
  45. package/pipeline/skills/shared/external/storekit/SKILL.md +69 -343
  46. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +371 -0
  47. package/pipeline/skills/shared/external/widgetkit/SKILL.md +25 -101
  48. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +107 -0
  49. package/pipeline/skills/skills-index.md +8 -2
@@ -23,681 +23,83 @@ Guide developers in building secure APIs by implementing authentication, authori
23
23
  - Use when implementing rate limiting and throttling
24
24
  - Use when handling sensitive data in APIs
25
25
 
26
+ ## Reference Files
27
+
28
+ Load the reference that matches the axis under review; each holds the full runnable implementations plus the common pitfalls for that axis.
29
+
30
+ - `references/auth.md` - JWT token generation, verification middleware, route protection, token refresh, and the JWT-secret and missing-authorization pitfalls. Load when implementing or reviewing authentication or access control.
31
+ - `references/input-validation.md` - parameterized queries, ORM escaping, Zod schema validation, XSS output sanitization, and the weak-password and verbose-error pitfalls. Load when handling any user input, query, or file upload.
32
+ - `references/rate-limiting.md` - fixed-window and per-tier rate limiters, Helmet security headers, and rate-limit response headers. Load when protecting endpoints from abuse, brute force, or DDoS.
33
+
26
34
  ## How It Works
27
35
 
28
36
  ### Step 1: Authentication & Authorization
29
37
 
30
- I'll help you implement secure authentication:
31
38
  - Choose authentication method (JWT, OAuth 2.0, API keys)
32
39
  - Implement token-based authentication
33
40
  - Set up role-based access control (RBAC)
34
41
  - Secure session management
35
42
  - Implement multi-factor authentication (MFA)
36
43
 
44
+ Full implementations: `references/auth.md`.
45
+
37
46
  ### Step 2: Input Validation & Sanitization
38
47
 
39
- Protect against injection attacks:
40
48
  - Validate all input data
41
49
  - Sanitize user inputs
42
50
  - Use parameterized queries
43
51
  - Implement request schema validation
44
52
  - Prevent SQL injection, XSS, and command injection
45
53
 
54
+ Full implementations: `references/input-validation.md`.
55
+
46
56
  ### Step 3: Rate Limiting & Throttling
47
57
 
48
- Prevent abuse and DDoS attacks:
49
58
  - Implement rate limiting per user/IP
50
59
  - Set up API throttling
51
60
  - Configure request quotas
52
61
  - Handle rate limit errors gracefully
53
62
  - Monitor for suspicious activity
54
63
 
64
+ Full implementations: `references/rate-limiting.md`.
65
+
55
66
  ### Step 4: Data Protection
56
67
 
57
- Secure sensitive data:
58
68
  - Encrypt data in transit (HTTPS/TLS)
59
69
  - Encrypt sensitive data at rest
60
70
  - Implement proper error handling (no data leaks)
61
71
  - Sanitize error messages
62
- - Use secure headers
72
+ - Use secure headers (see Helmet setup in `references/rate-limiting.md`)
63
73
 
64
74
  ### Step 5: API Security Testing
65
75
 
66
- Verify security implementation:
67
76
  - Test authentication and authorization
68
77
  - Perform penetration testing
69
78
  - Check for common vulnerabilities (OWASP API Top 10)
70
79
  - Validate input handling
71
80
  - Test rate limiting
72
81
 
82
+ ## Illustrative Snippets
73
83
 
74
- ## Examples
75
-
76
- ### Example 1: Implementing JWT Authentication
77
-
78
- ```markdown
79
- ## Secure JWT Authentication Implementation
80
-
81
- ### Authentication Flow
82
-
83
- 1. User logs in with credentials
84
- 2. Server validates credentials
85
- 3. Server generates JWT token
86
- 4. Client stores token securely
87
- 5. Client sends token with each request
88
- 6. Server validates token
89
-
90
- ### Implementation
91
-
92
- #### 1. Generate Secure JWT Tokens
93
-
94
- \`\`\`javascript
95
- // auth.js
96
- const jwt = require('jsonwebtoken');
97
- const bcrypt = require('bcrypt');
98
-
99
- // Login endpoint
100
- app.post('/api/auth/login', async (req, res) => {
101
- try {
102
- const { email, password } = req.body;
103
-
104
- // Validate input
105
- if (!email || !password) {
106
- return res.status(400).json({
107
- error: 'Email and password are required'
108
- });
109
- }
110
-
111
- // Find user
112
- const user = await db.user.findUnique({
113
- where: { email }
114
- });
115
-
116
- if (!user) {
117
- // Don't reveal if user exists
118
- return res.status(401).json({
119
- error: 'Invalid credentials'
120
- });
121
- }
122
-
123
- // Verify password
124
- const validPassword = await bcrypt.compare(
125
- password,
126
- user.passwordHash
127
- );
128
-
129
- if (!validPassword) {
130
- return res.status(401).json({
131
- error: 'Invalid credentials'
132
- });
133
- }
134
-
135
- // Generate JWT token
136
- const token = jwt.sign(
137
- {
138
- userId: user.id,
139
- email: user.email,
140
- role: user.role
141
- },
142
- process.env.JWT_SECRET,
143
- {
144
- expiresIn: '1h',
145
- issuer: 'your-app',
146
- audience: 'your-app-users'
147
- }
148
- );
149
-
150
- // Generate refresh token
151
- const refreshToken = jwt.sign(
152
- { userId: user.id },
153
- process.env.JWT_REFRESH_SECRET,
154
- { expiresIn: '7d' }
155
- );
156
-
157
- // Store refresh token in database
158
- await db.refreshToken.create({
159
- data: {
160
- token: refreshToken,
161
- userId: user.id,
162
- expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
163
- }
164
- });
165
-
166
- res.json({
167
- token,
168
- refreshToken,
169
- expiresIn: 3600
170
- });
171
-
172
- } catch (error) {
173
- console.error('Login error:', error);
174
- res.status(500).json({
175
- error: 'An error occurred during login'
176
- });
177
- }
178
- });
179
- \`\`\`
180
-
181
- #### 2. Verify JWT Tokens (Middleware)
182
-
183
- \`\`\`javascript
184
- // middleware/auth.js
185
- const jwt = require('jsonwebtoken');
186
-
187
- function authenticateToken(req, res, next) {
188
- // Get token from header
189
- const authHeader = req.headers['authorization'];
190
- const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
191
-
192
- if (!token) {
193
- return res.status(401).json({
194
- error: 'Access token required'
195
- });
196
- }
197
-
198
- // Verify token
199
- jwt.verify(
200
- token,
201
- process.env.JWT_SECRET,
202
- {
203
- issuer: 'your-app',
204
- audience: 'your-app-users'
205
- },
206
- (err, user) => {
207
- if (err) {
208
- if (err.name === 'TokenExpiredError') {
209
- return res.status(401).json({
210
- error: 'Token expired'
211
- });
212
- }
213
- return res.status(403).json({
214
- error: 'Invalid token'
215
- });
216
- }
217
-
218
- // Attach user to request
219
- req.user = user;
220
- next();
221
- }
222
- );
223
- }
224
-
225
- module.exports = { authenticateToken };
226
- \`\`\`
227
-
228
- #### 3. Protect Routes
229
-
230
- \`\`\`javascript
231
- const { authenticateToken } = require('./middleware/auth');
232
-
233
- // Protected route
234
- app.get('/api/user/profile', authenticateToken, async (req, res) => {
235
- try {
236
- const user = await db.user.findUnique({
237
- where: { id: req.user.userId },
238
- select: {
239
- id: true,
240
- email: true,
241
- name: true,
242
- // Don't return passwordHash
243
- }
244
- });
245
-
246
- res.json(user);
247
- } catch (error) {
248
- res.status(500).json({ error: 'Server error' });
249
- }
250
- });
251
- \`\`\`
252
-
253
- #### 4. Implement Token Refresh
254
-
255
- \`\`\`javascript
256
- app.post('/api/auth/refresh', async (req, res) => {
257
- const { refreshToken } = req.body;
258
-
259
- if (!refreshToken) {
260
- return res.status(401).json({
261
- error: 'Refresh token required'
262
- });
263
- }
264
-
265
- try {
266
- // Verify refresh token
267
- const decoded = jwt.verify(
268
- refreshToken,
269
- process.env.JWT_REFRESH_SECRET
270
- );
271
-
272
- // Check if refresh token exists in database
273
- const storedToken = await db.refreshToken.findFirst({
274
- where: {
275
- token: refreshToken,
276
- userId: decoded.userId,
277
- expiresAt: { gt: new Date() }
278
- }
279
- });
280
-
281
- if (!storedToken) {
282
- return res.status(403).json({
283
- error: 'Invalid refresh token'
284
- });
285
- }
286
-
287
- // Generate new access token
288
- const user = await db.user.findUnique({
289
- where: { id: decoded.userId }
290
- });
291
-
292
- const newToken = jwt.sign(
293
- {
294
- userId: user.id,
295
- email: user.email,
296
- role: user.role
297
- },
298
- process.env.JWT_SECRET,
299
- { expiresIn: '1h' }
300
- );
301
-
302
- res.json({
303
- token: newToken,
304
- expiresIn: 3600
305
- });
306
-
307
- } catch (error) {
308
- res.status(403).json({
309
- error: 'Invalid refresh token'
310
- });
311
- }
312
- });
313
- \`\`\`
314
-
315
- ### Security Best Practices
316
-
317
- - ✅ Use strong JWT secrets (256-bit minimum)
318
- - ✅ Set short expiration times (1 hour for access tokens)
319
- - ✅ Implement refresh tokens for long-lived sessions
320
- - ✅ Store refresh tokens in database (can be revoked)
321
- - ✅ Use HTTPS only
322
- - ✅ Don't store sensitive data in JWT payload
323
- - ✅ Validate token issuer and audience
324
- - ✅ Implement token blacklisting for logout
325
- ```
326
-
327
-
328
- ### Example 2: Input Validation and SQL Injection Prevention
329
-
330
- ```markdown
331
- ## Preventing SQL Injection and Input Validation
332
-
333
- ### The Problem
334
-
335
- **❌ Vulnerable Code:**
336
- \`\`\`javascript
337
- // NEVER DO THIS - SQL Injection vulnerability
338
- app.get('/api/users/:id', async (req, res) => {
339
- const userId = req.params.id;
340
-
341
- // Dangerous: User input directly in query
342
- const query = \`SELECT * FROM users WHERE id = '\${userId}'\`;
343
- const user = await db.query(query);
344
-
345
- res.json(user);
346
- });
347
-
348
- // Attack example:
349
- // GET /api/users/1' OR '1'='1
350
- // Returns all users!
351
- \`\`\`
352
-
353
- ### The Solution
354
-
355
- #### 1. Use Parameterized Queries
356
-
357
- \`\`\`javascript
358
- // ✅ Safe: Parameterized query
359
- app.get('/api/users/:id', async (req, res) => {
360
- const userId = req.params.id;
361
-
362
- // Validate input first
363
- if (!userId || !/^\d+$/.test(userId)) {
364
- return res.status(400).json({
365
- error: 'Invalid user ID'
366
- });
367
- }
368
-
369
- // Use parameterized query
370
- const user = await db.query(
371
- 'SELECT id, email, name FROM users WHERE id = $1',
372
- [userId]
373
- );
374
-
375
- if (!user) {
376
- return res.status(404).json({
377
- error: 'User not found'
378
- });
379
- }
380
-
381
- res.json(user);
382
- });
383
- \`\`\`
384
-
385
- #### 2. Use ORM with Proper Escaping
386
-
387
- \`\`\`javascript
388
- // ✅ Safe: Using Prisma ORM
389
- app.get('/api/users/:id', async (req, res) => {
390
- const userId = parseInt(req.params.id);
391
-
392
- if (isNaN(userId)) {
393
- return res.status(400).json({
394
- error: 'Invalid user ID'
395
- });
396
- }
397
-
398
- const user = await prisma.user.findUnique({
399
- where: { id: userId },
400
- select: {
401
- id: true,
402
- email: true,
403
- name: true,
404
- // Don't select sensitive fields
405
- }
406
- });
407
-
408
- if (!user) {
409
- return res.status(404).json({
410
- error: 'User not found'
411
- });
412
- }
413
-
414
- res.json(user);
415
- });
416
- \`\`\`
417
-
418
- #### 3. Implement Request Validation with Zod
419
-
420
- \`\`\`javascript
421
- const { z } = require('zod');
422
-
423
- // Define validation schema
424
- const createUserSchema = z.object({
425
- email: z.string().email('Invalid email format'),
426
- password: z.string()
427
- .min(8, 'Password must be at least 8 characters')
428
- .regex(/[A-Z]/, 'Password must contain uppercase letter')
429
- .regex(/[a-z]/, 'Password must contain lowercase letter')
430
- .regex(/[0-9]/, 'Password must contain number'),
431
- name: z.string()
432
- .min(2, 'Name must be at least 2 characters')
433
- .max(100, 'Name too long'),
434
- age: z.number()
435
- .int('Age must be an integer')
436
- .min(18, 'Must be 18 or older')
437
- .max(120, 'Invalid age')
438
- .optional()
439
- });
440
-
441
- // Validation middleware
442
- function validateRequest(schema) {
443
- return (req, res, next) => {
444
- try {
445
- schema.parse(req.body);
446
- next();
447
- } catch (error) {
448
- res.status(400).json({
449
- error: 'Validation failed',
450
- details: error.errors
451
- });
452
- }
453
- };
454
- }
455
-
456
- // Use validation
457
- app.post('/api/users',
458
- validateRequest(createUserSchema),
459
- async (req, res) => {
460
- // Input is validated at this point
461
- const { email, password, name, age } = req.body;
462
-
463
- // Hash password
464
- const passwordHash = await bcrypt.hash(password, 10);
465
-
466
- // Create user
467
- const user = await prisma.user.create({
468
- data: {
469
- email,
470
- passwordHash,
471
- name,
472
- age
473
- }
474
- });
475
-
476
- // Don't return password hash
477
- const { passwordHash: _, ...userWithoutPassword } = user;
478
- res.status(201).json(userWithoutPassword);
479
- }
480
- );
481
- \`\`\`
482
-
483
- #### 4. Sanitize Output to Prevent XSS
484
-
485
- \`\`\`javascript
486
- const DOMPurify = require('isomorphic-dompurify');
487
-
488
- app.post('/api/comments', authenticateToken, async (req, res) => {
489
- const { content } = req.body;
490
-
491
- // Validate
492
- if (!content || content.length > 1000) {
493
- return res.status(400).json({
494
- error: 'Invalid comment content'
495
- });
496
- }
497
-
498
- // Sanitize HTML to prevent XSS
499
- const sanitizedContent = DOMPurify.sanitize(content, {
500
- ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
501
- ALLOWED_ATTR: ['href']
502
- });
503
-
504
- const comment = await prisma.comment.create({
505
- data: {
506
- content: sanitizedContent,
507
- userId: req.user.userId
508
- }
509
- });
510
-
511
- res.status(201).json(comment);
512
- });
513
- \`\`\`
514
-
515
- ### Validation Checklist
84
+ Verify a bearer token before the handler runs; full middleware in `references/auth.md`:
516
85
 
517
- - [ ] Validate all user inputs
518
- - [ ] Use parameterized queries or ORM
519
- - [ ] Validate data types (string, number, email, etc.)
520
- - [ ] Validate data ranges (min/max length, value ranges)
521
- - [ ] Sanitize HTML content
522
- - [ ] Escape special characters
523
- - [ ] Validate file uploads (type, size, content)
524
- - [ ] Use allowlists, not blocklists
86
+ ```javascript
87
+ const authHeader = req.headers['authorization'];
88
+ const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
89
+ if (!token) return res.status(401).json({ error: 'Access token required' });
525
90
  ```
526
91
 
92
+ Never build SQL from user input; parameterize instead (full patterns in `references/input-validation.md`):
527
93
 
528
- ### Example 3: Rate Limiting and DDoS Protection
529
-
530
- ```markdown
531
- ## Implementing Rate Limiting
532
-
533
- ### Why Rate Limiting?
534
-
535
- - Prevent brute force attacks
536
- - Protect against DDoS
537
- - Prevent API abuse
538
- - Ensure fair usage
539
- - Reduce server costs
540
-
541
- ### Implementation with Express Rate Limit
542
-
543
- \`\`\`javascript
544
- const rateLimit = require('express-rate-limit');
545
- const RedisStore = require('rate-limit-redis');
546
- const Redis = require('ioredis');
547
-
548
- // Create Redis client
549
- const redis = new Redis({
550
- host: process.env.REDIS_HOST,
551
- port: process.env.REDIS_PORT
552
- });
553
-
554
- // General API rate limit
555
- const apiLimiter = rateLimit({
556
- store: new RedisStore({
557
- client: redis,
558
- prefix: 'rl:api:'
559
- }),
560
- windowMs: 15 * 60 * 1000, // 15 minutes
561
- max: 100, // 100 requests per window
562
- message: {
563
- error: 'Too many requests, please try again later',
564
- retryAfter: 900 // seconds
565
- },
566
- standardHeaders: true, // Return rate limit info in headers
567
- legacyHeaders: false,
568
- // Custom key generator (by user ID or IP)
569
- keyGenerator: (req) => {
570
- return req.user?.userId || req.ip;
571
- }
572
- });
573
-
574
- // Strict rate limit for authentication endpoints
575
- const authLimiter = rateLimit({
576
- store: new RedisStore({
577
- client: redis,
578
- prefix: 'rl:auth:'
579
- }),
580
- windowMs: 15 * 60 * 1000, // 15 minutes
581
- max: 5, // Only 5 login attempts per 15 minutes
582
- skipSuccessfulRequests: true, // Don't count successful logins
583
- message: {
584
- error: 'Too many login attempts, please try again later',
585
- retryAfter: 900
586
- }
587
- });
588
-
589
- // Apply rate limiters
590
- app.use('/api/', apiLimiter);
591
- app.use('/api/auth/login', authLimiter);
592
- app.use('/api/auth/register', authLimiter);
593
-
594
- // Custom rate limiter for expensive operations
595
- const expensiveLimiter = rateLimit({
596
- windowMs: 60 * 60 * 1000, // 1 hour
597
- max: 10, // 10 requests per hour
598
- message: {
599
- error: 'Rate limit exceeded for this operation'
600
- }
601
- });
602
-
603
- app.post('/api/reports/generate',
604
- authenticateToken,
605
- expensiveLimiter,
606
- async (req, res) => {
607
- // Expensive operation
608
- }
609
- );
610
- \`\`\`
611
-
612
- ### Advanced: Per-User Rate Limiting
613
-
614
- \`\`\`javascript
615
- // Different limits based on user tier
616
- function createTieredRateLimiter() {
617
- const limits = {
618
- free: { windowMs: 60 * 60 * 1000, max: 100 },
619
- pro: { windowMs: 60 * 60 * 1000, max: 1000 },
620
- enterprise: { windowMs: 60 * 60 * 1000, max: 10000 }
621
- };
622
-
623
- return async (req, res, next) => {
624
- const user = req.user;
625
- const tier = user?.tier || 'free';
626
- const limit = limits[tier];
627
-
628
- const key = \`rl:user:\${user.userId}\`;
629
- const current = await redis.incr(key);
630
-
631
- if (current === 1) {
632
- await redis.expire(key, limit.windowMs / 1000);
633
- }
634
-
635
- if (current > limit.max) {
636
- return res.status(429).json({
637
- error: 'Rate limit exceeded',
638
- limit: limit.max,
639
- remaining: 0,
640
- reset: await redis.ttl(key)
641
- });
642
- }
643
-
644
- // Set rate limit headers
645
- res.set({
646
- 'X-RateLimit-Limit': limit.max,
647
- 'X-RateLimit-Remaining': limit.max - current,
648
- 'X-RateLimit-Reset': await redis.ttl(key)
649
- });
650
-
651
- next();
652
- };
653
- }
654
-
655
- app.use('/api/', authenticateToken, createTieredRateLimiter());
656
- \`\`\`
657
-
658
- ### DDoS Protection with Helmet
659
-
660
- \`\`\`javascript
661
- const helmet = require('helmet');
662
-
663
- app.use(helmet({
664
- // Content Security Policy
665
- contentSecurityPolicy: {
666
- directives: {
667
- defaultSrc: ["'self'"],
668
- styleSrc: ["'self'", "'unsafe-inline'"],
669
- scriptSrc: ["'self'"],
670
- imgSrc: ["'self'", 'data:', 'https:']
671
- }
672
- },
673
- // Prevent clickjacking
674
- frameguard: { action: 'deny' },
675
- // Hide X-Powered-By header
676
- hidePoweredBy: true,
677
- // Prevent MIME type sniffing
678
- noSniff: true,
679
- // Enable HSTS
680
- hsts: {
681
- maxAge: 31536000,
682
- includeSubDomains: true,
683
- preload: true
684
- }
685
- }));
686
- \`\`\`
687
-
688
- ### Rate Limit Response Headers
689
-
690
- \`\`\`
691
- X-RateLimit-Limit: 100
692
- X-RateLimit-Remaining: 87
693
- X-RateLimit-Reset: 1640000000
694
- Retry-After: 900
695
- \`\`\`
94
+ ```javascript
95
+ // Bad: `SELECT * FROM users WHERE id = '${userId}'`
96
+ // Good:
97
+ await db.query('SELECT id, email, name FROM users WHERE id = $1', [userId]);
696
98
  ```
697
99
 
698
100
  ## Best Practices
699
101
 
700
- ### ✅ Do This
102
+ ### Do This
701
103
 
702
104
  - **Use HTTPS Everywhere** - Never send sensitive data over HTTP
703
105
  - **Implement Authentication** - Require authentication for protected endpoints
@@ -712,7 +114,7 @@ Retry-After: 900
712
114
  - **Use Security Headers** - Implement Helmet.js
713
115
  - **Sanitize Error Messages** - Don't leak sensitive information
714
116
 
715
- ### ❌ Don't Do This
117
+ ### Don't Do This
716
118
 
717
119
  - **Don't Store Passwords in Plain Text** - Always hash passwords
718
120
  - **Don't Use Weak Secrets** - Use strong, random JWT secrets
@@ -727,112 +129,12 @@ Retry-After: 900
727
129
 
728
130
  ## Common Pitfalls
729
131
 
730
- ### Problem: JWT Secret Exposed in Code
731
- **Symptoms:** JWT secret hardcoded or committed to Git
732
- **Solution:**
733
- \`\`\`javascript
734
- // ❌ Bad
735
- const JWT_SECRET = 'my-secret-key';
736
-
737
- // ✅ Good
738
- const JWT_SECRET = process.env.JWT_SECRET;
739
- if (!JWT_SECRET) {
740
- throw new Error('JWT_SECRET environment variable is required');
741
- }
742
-
743
- // Generate strong secret
744
- // node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
745
- \`\`\`
746
-
747
- ### Problem: Weak Password Requirements
748
- **Symptoms:** Users can set weak passwords like "password123"
749
- **Solution:**
750
- \`\`\`javascript
751
- const passwordSchema = z.string()
752
- .min(12, 'Password must be at least 12 characters')
753
- .regex(/[A-Z]/, 'Must contain uppercase letter')
754
- .regex(/[a-z]/, 'Must contain lowercase letter')
755
- .regex(/[0-9]/, 'Must contain number')
756
- .regex(/[^A-Za-z0-9]/, 'Must contain special character');
757
-
758
- // Or use a password strength library
759
- const zxcvbn = require('zxcvbn');
760
- const result = zxcvbn(password);
761
- if (result.score < 3) {
762
- return res.status(400).json({
763
- error: 'Password too weak',
764
- suggestions: result.feedback.suggestions
765
- });
766
- }
767
- \`\`\`
768
-
769
- ### Problem: Missing Authorization Checks
770
- **Symptoms:** Users can access resources they shouldn't
771
- **Solution:**
772
- \`\`\`javascript
773
- // ❌ Bad: Only checks authentication
774
- app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
775
- await prisma.post.delete({ where: { id: req.params.id } });
776
- res.json({ success: true });
777
- });
778
-
779
- // ✅ Good: Checks both authentication and authorization
780
- app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
781
- const post = await prisma.post.findUnique({
782
- where: { id: req.params.id }
783
- });
784
-
785
- if (!post) {
786
- return res.status(404).json({ error: 'Post not found' });
787
- }
788
-
789
- // Check if user owns the post or is admin
790
- if (post.userId !== req.user.userId && req.user.role !== 'admin') {
791
- return res.status(403).json({
792
- error: 'Not authorized to delete this post'
793
- });
794
- }
795
-
796
- await prisma.post.delete({ where: { id: req.params.id } });
797
- res.json({ success: true });
798
- });
799
- \`\`\`
800
-
801
- ### Problem: Verbose Error Messages
802
- **Symptoms:** Error messages reveal system details
803
- **Solution:**
804
- \`\`\`javascript
805
- // ❌ Bad: Exposes database details
806
- app.post('/api/users', async (req, res) => {
807
- try {
808
- const user = await prisma.user.create({ data: req.body });
809
- res.json(user);
810
- } catch (error) {
811
- res.status(500).json({ error: error.message });
812
- // Error: "Unique constraint failed on the fields: (`email`)"
813
- }
814
- });
815
-
816
- // ✅ Good: Generic error message
817
- app.post('/api/users', async (req, res) => {
818
- try {
819
- const user = await prisma.user.create({ data: req.body });
820
- res.json(user);
821
- } catch (error) {
822
- console.error('User creation error:', error); // Log full error
823
-
824
- if (error.code === 'P2002') {
825
- return res.status(400).json({
826
- error: 'Email already exists'
827
- });
828
- }
829
-
830
- res.status(500).json({
831
- error: 'An error occurred while creating user'
832
- });
833
- }
834
- });
835
- \`\`\`
132
+ Each pitfall's symptom-to-fix code lives in the reference file for its axis.
133
+
134
+ - **JWT secret exposed in code** - hardcoded or committed secret; load from env and fail fast if missing. See `references/auth.md`.
135
+ - **Missing authorization checks** - authentication passes but resource ownership is never verified. See `references/auth.md`.
136
+ - **Weak password requirements** - no strength floor; enforce a schema or a strength library. See `references/input-validation.md`.
137
+ - **Verbose error messages** - raw DB errors returned to the client; log server-side, return generic messages. See `references/input-validation.md`.
836
138
 
837
139
  ## Security Checklist
838
140
 
@@ -907,4 +209,4 @@ app.post('/api/users', async (req, res) => {
907
209
 
908
210
  ---
909
211
 
910
- **Pro Tip:** Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities!
212
+ **Pro Tip:** Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities.