@ia-ccun/code-agent-cli 0.0.15 → 0.0.16

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 (75) hide show
  1. package/bin/cli.js +153 -84
  2. package/config/agent/extensions/working-msg.ts +33 -8
  3. package/config/agent/models.json +41 -11
  4. package/config/agent/prompts/code-simplifier.md +52 -0
  5. package/config/agent/skills/brainstorming/SKILL.md +165 -0
  6. package/config/agent/skills/brainstorming/scripts/frame-template.html +214 -0
  7. package/config/agent/skills/brainstorming/scripts/helper.js +88 -0
  8. package/config/agent/skills/brainstorming/scripts/server.cjs +338 -0
  9. package/config/agent/skills/brainstorming/scripts/start-server.sh +153 -0
  10. package/config/agent/skills/brainstorming/scripts/stop-server.sh +55 -0
  11. package/config/agent/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  12. package/config/agent/skills/brainstorming/visual-companion.md +286 -0
  13. package/config/agent/skills/dispatching-parallel-agents/SKILL.md +183 -0
  14. package/config/agent/skills/executing-plans/SKILL.md +71 -0
  15. package/config/agent/skills/finishing-a-development-branch/SKILL.md +201 -0
  16. package/config/agent/skills/owasp-security/SKILL.md +537 -0
  17. package/config/agent/skills/receiving-code-review/SKILL.md +214 -0
  18. package/config/agent/skills/requesting-code-review/SKILL.md +106 -0
  19. package/config/agent/skills/requesting-code-review/code-reviewer.md +146 -0
  20. package/config/agent/skills/skill-creator/SKILL.md +337 -213
  21. package/config/agent/skills/skill-creator/agents/analyzer.md +274 -0
  22. package/config/agent/skills/skill-creator/agents/comparator.md +202 -0
  23. package/config/agent/skills/skill-creator/agents/grader.md +223 -0
  24. package/config/agent/skills/skill-creator/assets/eval_review.html +146 -0
  25. package/config/agent/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  26. package/config/agent/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  27. package/config/agent/skills/skill-creator/references/schemas.md +430 -0
  28. package/config/agent/skills/skill-creator/scripts/__init__.py +0 -0
  29. package/config/agent/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  30. package/config/agent/skills/skill-creator/scripts/generate_report.py +326 -0
  31. package/config/agent/skills/skill-creator/scripts/improve_description.py +248 -0
  32. package/config/agent/skills/skill-creator/scripts/package_skill.py +33 -7
  33. package/config/agent/skills/skill-creator/scripts/quick_validate.py +11 -3
  34. package/config/agent/skills/skill-creator/scripts/run_eval.py +310 -0
  35. package/config/agent/skills/skill-creator/scripts/run_loop.py +332 -0
  36. package/config/agent/skills/skill-creator/scripts/utils.py +47 -0
  37. package/config/agent/skills/subagent-driven-development/SKILL.md +278 -0
  38. package/config/agent/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  39. package/config/agent/skills/subagent-driven-development/implementer-prompt.md +113 -0
  40. package/config/agent/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  41. package/config/agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
  42. package/config/agent/skills/systematic-debugging/SKILL.md +297 -0
  43. package/config/agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  44. package/config/agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
  45. package/config/agent/skills/systematic-debugging/defense-in-depth.md +122 -0
  46. package/config/agent/skills/systematic-debugging/find-polluter.sh +63 -0
  47. package/config/agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
  48. package/config/agent/skills/systematic-debugging/test-academic.md +14 -0
  49. package/config/agent/skills/systematic-debugging/test-pressure-1.md +58 -0
  50. package/config/agent/skills/systematic-debugging/test-pressure-2.md +68 -0
  51. package/config/agent/skills/systematic-debugging/test-pressure-3.md +69 -0
  52. package/config/agent/skills/test-driven-development/SKILL.md +372 -0
  53. package/config/agent/skills/test-driven-development/testing-anti-patterns.md +299 -0
  54. package/config/agent/skills/using-git-worktrees/SKILL.md +219 -0
  55. package/config/agent/skills/using-superpowers/SKILL.md +116 -0
  56. package/config/agent/skills/using-superpowers/references/codex-tools.md +25 -0
  57. package/config/agent/skills/using-superpowers/references/gemini-tools.md +33 -0
  58. package/config/agent/skills/verification-before-completion/SKILL.md +140 -0
  59. package/config/agent/skills/writing-plans/SKILL.md +146 -0
  60. package/config/agent/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  61. package/config/agent/skills/writing-skills/SKILL.md +667 -0
  62. package/config/agent/skills/writing-skills/anthropic-best-practices.md +1150 -0
  63. package/config/agent/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  64. package/config/agent/skills/writing-skills/graphviz-conventions.dot +172 -0
  65. package/config/agent/skills/writing-skills/persuasion-principles.md +187 -0
  66. package/config/agent/skills/writing-skills/render-graphs.js +168 -0
  67. package/config/agent/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  68. package/package.json +14 -7
  69. package/scripts/postinstall.js +4 -18
  70. package/config/agent/skills/github/SKILL.md +0 -47
  71. package/config/agent/skills/owasp/SKILL.md +0 -169
  72. package/config/agent/skills/pua/SKILL.md +0 -364
  73. package/config/agent/skills/skill-creator/references/output-patterns.md +0 -82
  74. package/config/agent/skills/skill-creator/references/workflows.md +0 -28
  75. package/config/agent/skills/skill-creator/scripts/init_skill.py +0 -303
@@ -0,0 +1,537 @@
1
+ ---
2
+ name: owasp-security
3
+ author: xujianjiang
4
+ description: Implement secure coding practices following OWASP Top 10. Use when preventing security vulnerabilities, implementing authentication, securing APIs, or conducting security reviews. Triggers on OWASP, security, XSS, SQL injection, CSRF, authentication security, secure coding, vulnerability.
5
+ ---
6
+
7
+ # OWASP Top 10 Security
8
+
9
+ Prevent common security vulnerabilities in web applications.
10
+
11
+ ## OWASP Top 10 (2021)
12
+
13
+ | # | Vulnerability | Prevention |
14
+ |---|---------------|------------|
15
+ | A01 | Broken Access Control | Proper authorization checks |
16
+ | A02 | Cryptographic Failures | Strong encryption, secure storage |
17
+ | A03 | Injection | Input validation, parameterized queries |
18
+ | A04 | Insecure Design | Threat modeling, secure patterns |
19
+ | A05 | Security Misconfiguration | Hardened configs, no defaults |
20
+ | A06 | Vulnerable Components | Dependency scanning, updates |
21
+ | A07 | Auth Failures | MFA, secure session management |
22
+ | A08 | Data Integrity Failures | Input validation, signed updates |
23
+ | A09 | Logging Failures | Comprehensive audit logs |
24
+ | A10 | SSRF | URL validation, allowlists |
25
+
26
+ ## A01: Broken Access Control
27
+
28
+ ### Prevention Patterns
29
+ ```typescript
30
+ // ❌ BAD: No authorization check
31
+ app.get('/api/users/:id', async (req, res) => {
32
+ const user = await db.users.findById(req.params.id);
33
+ res.json(user);
34
+ });
35
+
36
+ // ✅ GOOD: Verify ownership
37
+ app.get('/api/users/:id', authenticate, async (req, res) => {
38
+ const userId = req.params.id;
39
+
40
+ // Users can only access their own data
41
+ if (req.user.id !== userId && req.user.role !== 'admin') {
42
+ return res.status(403).json({ error: 'Forbidden' });
43
+ }
44
+
45
+ const user = await db.users.findById(userId);
46
+ res.json(user);
47
+ });
48
+
49
+ // ✅ GOOD: Role-based access control (RBAC)
50
+ const requireRole = (...roles: string[]) => {
51
+ return (req: Request, res: Response, next: NextFunction) => {
52
+ if (!roles.includes(req.user?.role)) {
53
+ return res.status(403).json({ error: 'Insufficient permissions' });
54
+ }
55
+ next();
56
+ };
57
+ };
58
+
59
+ app.delete('/api/posts/:id', authenticate, requireRole('admin', 'moderator'), deletePost);
60
+ ```
61
+
62
+ ### Insecure Direct Object Reference (IDOR)
63
+ ```typescript
64
+ // ❌ BAD: Predictable IDs exposed
65
+ GET /api/invoices/1001
66
+ GET /api/invoices/1002 // Can enumerate others' invoices
67
+
68
+ // ✅ GOOD: Use UUIDs + ownership check
69
+ app.get('/api/invoices/:id', authenticate, async (req, res) => {
70
+ const invoice = await db.invoices.findOne({
71
+ id: req.params.id,
72
+ userId: req.user.id, // Enforce ownership
73
+ });
74
+
75
+ if (!invoice) {
76
+ return res.status(404).json({ error: 'Not found' });
77
+ }
78
+
79
+ res.json(invoice);
80
+ });
81
+ ```
82
+
83
+ ## A02: Cryptographic Failures
84
+
85
+ ### Password Hashing
86
+ ```typescript
87
+ import bcrypt from 'bcrypt';
88
+ import crypto from 'crypto';
89
+
90
+ // ✅ Hash passwords with bcrypt
91
+ const SALT_ROUNDS = 12;
92
+
93
+ async function hashPassword(password: string): Promise<string> {
94
+ return bcrypt.hash(password, SALT_ROUNDS);
95
+ }
96
+
97
+ async function verifyPassword(password: string, hash: string): Promise<boolean> {
98
+ return bcrypt.compare(password, hash);
99
+ }
100
+
101
+ // ✅ Secure token generation
102
+ function generateSecureToken(length = 32): string {
103
+ return crypto.randomBytes(length).toString('hex');
104
+ }
105
+
106
+ // ✅ Encrypt sensitive data
107
+ const ALGORITHM = 'aes-256-gcm';
108
+ const KEY = crypto.scryptSync(process.env.ENCRYPTION_KEY!, 'salt', 32);
109
+
110
+ function encrypt(text: string): { encrypted: string; iv: string; tag: string } {
111
+ const iv = crypto.randomBytes(16);
112
+ const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
113
+
114
+ let encrypted = cipher.update(text, 'utf8', 'hex');
115
+ encrypted += cipher.final('hex');
116
+
117
+ return {
118
+ encrypted,
119
+ iv: iv.toString('hex'),
120
+ tag: cipher.getAuthTag().toString('hex'),
121
+ };
122
+ }
123
+
124
+ function decrypt(encrypted: string, iv: string, tag: string): string {
125
+ const decipher = crypto.createDecipheriv(ALGORITHM, KEY, Buffer.from(iv, 'hex'));
126
+ decipher.setAuthTag(Buffer.from(tag, 'hex'));
127
+
128
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
129
+ decrypted += decipher.final('utf8');
130
+
131
+ return decrypted;
132
+ }
133
+ ```
134
+
135
+ ### Secure Headers
136
+ ```typescript
137
+ import helmet from 'helmet';
138
+
139
+ app.use(helmet());
140
+ app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }));
141
+ app.use(helmet.contentSecurityPolicy({
142
+ directives: {
143
+ defaultSrc: ["'self'"],
144
+ scriptSrc: ["'self'", "'strict-dynamic'"],
145
+ styleSrc: ["'self'", "'unsafe-inline'"],
146
+ imgSrc: ["'self'", 'data:', 'https:'],
147
+ connectSrc: ["'self'"],
148
+ fontSrc: ["'self'"],
149
+ objectSrc: ["'none'"],
150
+ frameAncestors: ["'none'"],
151
+ },
152
+ }));
153
+ ```
154
+
155
+ ## A03: Injection
156
+
157
+ ### SQL Injection Prevention
158
+ ```typescript
159
+ // ❌ BAD: String concatenation
160
+ const query = `SELECT * FROM users WHERE email = '${email}'`;
161
+
162
+ // ✅ GOOD: Parameterized queries
163
+ // With Prisma
164
+ const user = await prisma.user.findUnique({ where: { email } });
165
+
166
+ // With raw SQL (parameterized)
167
+ const user = await db.query('SELECT * FROM users WHERE email = $1', [email]);
168
+
169
+ // With Knex
170
+ const user = await knex('users').where({ email }).first();
171
+ ```
172
+
173
+ ### NoSQL Injection Prevention
174
+ ```typescript
175
+ // ❌ BAD: Direct user input in query
176
+ const user = await User.findOne({ username: req.body.username });
177
+ // Attack: { "username": { "$gt": "" } } returns first user
178
+
179
+ // ✅ GOOD: Validate input type
180
+ import { z } from 'zod';
181
+
182
+ const loginSchema = z.object({
183
+ username: z.string().min(3).max(50),
184
+ password: z.string().min(8),
185
+ });
186
+
187
+ app.post('/login', async (req, res) => {
188
+ const { username, password } = loginSchema.parse(req.body);
189
+ const user = await User.findOne({ username: String(username) });
190
+ // ...
191
+ });
192
+ ```
193
+
194
+ ### Command Injection Prevention
195
+ ```typescript
196
+ import { execFile } from 'child_process';
197
+
198
+ // ❌ BAD: Shell injection
199
+ exec(`convert ${userInput} output.png`); // userInput: "; rm -rf /"
200
+
201
+ // ✅ GOOD: Use execFile with array args
202
+ execFile('convert', [userInput, 'output.png'], (error, stdout) => {
203
+ // Safe - arguments are not shell-interpreted
204
+ });
205
+
206
+ // ✅ GOOD: Validate and sanitize
207
+ const allowedFormats = ['png', 'jpg', 'gif'];
208
+ if (!allowedFormats.includes(format)) {
209
+ throw new Error('Invalid format');
210
+ }
211
+ ```
212
+
213
+ ## A04: Insecure Design
214
+
215
+ ### Rate Limiting
216
+ ```typescript
217
+ import rateLimit from 'express-rate-limit';
218
+
219
+ // General rate limit
220
+ const limiter = rateLimit({
221
+ windowMs: 15 * 60 * 1000, // 15 minutes
222
+ max: 100, // 100 requests per window
223
+ standardHeaders: true,
224
+ legacyHeaders: false,
225
+ });
226
+
227
+ // Strict limit for auth endpoints
228
+ const authLimiter = rateLimit({
229
+ windowMs: 60 * 60 * 1000, // 1 hour
230
+ max: 5, // 5 failed attempts
231
+ skipSuccessfulRequests: true,
232
+ });
233
+
234
+ app.use('/api/', limiter);
235
+ app.use('/api/auth/', authLimiter);
236
+ ```
237
+
238
+ ### Input Validation
239
+ ```typescript
240
+ import { z } from 'zod';
241
+
242
+ const userSchema = z.object({
243
+ email: z.string().email(),
244
+ password: z.string()
245
+ .min(8)
246
+ .regex(/[A-Z]/, 'Must contain uppercase')
247
+ .regex(/[a-z]/, 'Must contain lowercase')
248
+ .regex(/[0-9]/, 'Must contain number')
249
+ .regex(/[^A-Za-z0-9]/, 'Must contain special character'),
250
+ age: z.number().int().min(13).max(120),
251
+ role: z.enum(['user', 'admin']).default('user'),
252
+ });
253
+
254
+ app.post('/api/users', async (req, res) => {
255
+ try {
256
+ const data = userSchema.parse(req.body);
257
+ // Validated data is safe to use
258
+ } catch (error) {
259
+ if (error instanceof z.ZodError) {
260
+ return res.status(400).json({ errors: error.errors });
261
+ }
262
+ throw error;
263
+ }
264
+ });
265
+ ```
266
+
267
+ ## A05: Security Misconfiguration
268
+
269
+ ### Environment Configuration
270
+ ```typescript
271
+ // ✅ Never expose stack traces in production
272
+ app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
273
+ console.error(err.stack); // Log for debugging
274
+
275
+ res.status(500).json({
276
+ error: process.env.NODE_ENV === 'production'
277
+ ? 'Internal server error'
278
+ : err.message,
279
+ });
280
+ });
281
+
282
+ // ✅ Disable sensitive headers
283
+ app.disable('x-powered-by');
284
+
285
+ // ✅ Secure cookie configuration
286
+ app.use(session({
287
+ secret: process.env.SESSION_SECRET!,
288
+ cookie: {
289
+ secure: process.env.NODE_ENV === 'production',
290
+ httpOnly: true,
291
+ sameSite: 'strict',
292
+ maxAge: 24 * 60 * 60 * 1000, // 24 hours
293
+ },
294
+ resave: false,
295
+ saveUninitialized: false,
296
+ }));
297
+ ```
298
+
299
+ ## A06: Vulnerable Components
300
+
301
+ ### Dependency Scanning
302
+ ```bash
303
+ # Check for vulnerabilities
304
+ npm audit
305
+ npm audit fix
306
+
307
+ # Use Snyk for deeper scanning
308
+ npx snyk test
309
+ npx snyk monitor
310
+
311
+ # Keep dependencies updated
312
+ npx npm-check-updates -u
313
+ ```
314
+
315
+ ```json
316
+ // package.json - Use exact versions or ranges
317
+ {
318
+ "dependencies": {
319
+ "express": "^4.18.0", // Minor updates OK
320
+ "lodash": "4.17.21" // Exact version
321
+ },
322
+ "overrides": {
323
+ "vulnerable-package": "^2.0.0" // Force safe version
324
+ }
325
+ }
326
+ ```
327
+
328
+ ## A07: Authentication Failures
329
+
330
+ ### Secure Session Management
331
+ ```typescript
332
+ import jwt from 'jsonwebtoken';
333
+
334
+ // ✅ JWT with short expiry + refresh tokens
335
+ function generateTokens(userId: string) {
336
+ const accessToken = jwt.sign(
337
+ { userId },
338
+ process.env.JWT_SECRET!,
339
+ { expiresIn: '15m' } // Short-lived
340
+ );
341
+
342
+ const refreshToken = jwt.sign(
343
+ { userId, type: 'refresh' },
344
+ process.env.JWT_REFRESH_SECRET!,
345
+ { expiresIn: '7d' }
346
+ );
347
+
348
+ return { accessToken, refreshToken };
349
+ }
350
+
351
+ // ✅ Secure password reset
352
+ async function initiatePasswordReset(email: string) {
353
+ const user = await db.users.findByEmail(email);
354
+ if (!user) return; // Don't reveal if email exists
355
+
356
+ const token = crypto.randomBytes(32).toString('hex');
357
+ const hashedToken = crypto.createHash('sha256').update(token).digest('hex');
358
+
359
+ await db.passwordResets.create({
360
+ userId: user.id,
361
+ token: hashedToken,
362
+ expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour
363
+ });
364
+
365
+ await sendEmail(email, `Reset link: /reset?token=${token}`);
366
+ }
367
+ ```
368
+
369
+ ### Multi-Factor Authentication
370
+ ```typescript
371
+ import { authenticator } from 'otplib';
372
+ import QRCode from 'qrcode';
373
+
374
+ // Setup TOTP
375
+ async function setupMFA(userId: string) {
376
+ const secret = authenticator.generateSecret();
377
+ const otpauth = authenticator.keyuri(userId, 'MyApp', secret);
378
+ const qrCode = await QRCode.toDataURL(otpauth);
379
+
380
+ await db.users.update(userId, { mfaSecret: encrypt(secret) });
381
+
382
+ return { qrCode, secret };
383
+ }
384
+
385
+ // Verify TOTP
386
+ function verifyMFA(token: string, secret: string): boolean {
387
+ return authenticator.verify({ token, secret });
388
+ }
389
+ ```
390
+
391
+ ## A08: XSS Prevention
392
+
393
+ ```typescript
394
+ // ✅ React auto-escapes by default
395
+ const UserProfile = ({ user }) => (
396
+ <div>{user.name}</div> // Safe - auto-escaped
397
+ );
398
+
399
+ // ⚠️ Dangerous - avoid if possible
400
+ <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />
401
+
402
+ // ✅ Sanitize HTML if needed
403
+ import DOMPurify from 'dompurify';
404
+
405
+ const sanitizedHtml = DOMPurify.sanitize(userHtml, {
406
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
407
+ ALLOWED_ATTR: ['href'],
408
+ });
409
+
410
+ // ✅ Content Security Policy
411
+ app.use(helmet.contentSecurityPolicy({
412
+ directives: {
413
+ scriptSrc: ["'self'"], // No inline scripts
414
+ styleSrc: ["'self'", "'unsafe-inline'"],
415
+ },
416
+ }));
417
+ ```
418
+
419
+ ## A09: Logging & Monitoring
420
+
421
+ ```typescript
422
+ import winston from 'winston';
423
+
424
+ const logger = winston.createLogger({
425
+ level: 'info',
426
+ format: winston.format.combine(
427
+ winston.format.timestamp(),
428
+ winston.format.json()
429
+ ),
430
+ transports: [
431
+ new winston.transports.File({ filename: 'error.log', level: 'error' }),
432
+ new winston.transports.File({ filename: 'combined.log' }),
433
+ ],
434
+ });
435
+
436
+ // ✅ Log security events
437
+ function logSecurityEvent(event: string, details: object) {
438
+ logger.warn({
439
+ type: 'security',
440
+ event,
441
+ ...details,
442
+ timestamp: new Date().toISOString(),
443
+ });
444
+ }
445
+
446
+ // Usage
447
+ logSecurityEvent('failed_login', { email, ip: req.ip, userAgent: req.headers['user-agent'] });
448
+ logSecurityEvent('access_denied', { userId, resource, action });
449
+ logSecurityEvent('suspicious_activity', { userId, pattern: 'rapid_requests' });
450
+ ```
451
+
452
+ ## A10: SSRF Prevention
453
+
454
+ ```typescript
455
+ import { URL } from 'url';
456
+
457
+ // ✅ Validate URLs against allowlist
458
+ const ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com'];
459
+
460
+ function isAllowedUrl(urlString: string): boolean {
461
+ try {
462
+ const url = new URL(urlString);
463
+
464
+ // Block private IPs
465
+ const privatePatterns = [
466
+ /^localhost$/i,
467
+ /^127\./,
468
+ /^10\./,
469
+ /^172\.(1[6-9]|2[0-9]|3[01])\./,
470
+ /^192\.168\./,
471
+ /^0\./,
472
+ /^169\.254\./, // Link-local
473
+ ];
474
+
475
+ if (privatePatterns.some(p => p.test(url.hostname))) {
476
+ return false;
477
+ }
478
+
479
+ // Check allowlist
480
+ return ALLOWED_HOSTS.includes(url.hostname);
481
+ } catch {
482
+ return false;
483
+ }
484
+ }
485
+
486
+ app.post('/api/fetch-url', async (req, res) => {
487
+ const { url } = req.body;
488
+
489
+ if (!isAllowedUrl(url)) {
490
+ return res.status(400).json({ error: 'URL not allowed' });
491
+ }
492
+
493
+ const response = await fetch(url);
494
+ // ...
495
+ });
496
+ ```
497
+
498
+ ## Security Checklist
499
+
500
+ ```markdown
501
+ ## Pre-Deployment Checklist
502
+
503
+ ### Authentication
504
+ - [ ] Passwords hashed with bcrypt (cost ≥ 12)
505
+ - [ ] JWT tokens have short expiry
506
+ - [ ] Session cookies are httpOnly, secure, sameSite
507
+ - [ ] Rate limiting on auth endpoints
508
+
509
+ ### Authorization
510
+ - [ ] All endpoints have auth checks
511
+ - [ ] RBAC implemented correctly
512
+ - [ ] No IDOR vulnerabilities
513
+
514
+ ### Input/Output
515
+ - [ ] All input validated with Zod/Joi
516
+ - [ ] SQL queries parameterized
517
+ - [ ] XSS prevented (CSP, escaping)
518
+ - [ ] File uploads validated and sandboxed
519
+
520
+ ### Infrastructure
521
+ - [ ] HTTPS enforced
522
+ - [ ] Security headers configured
523
+ - [ ] Dependencies audited
524
+ - [ ] Secrets in environment variables
525
+
526
+ ### Monitoring
527
+ - [ ] Security events logged
528
+ - [ ] Error monitoring enabled
529
+ - [ ] Alerts configured
530
+ ```
531
+
532
+ ## Resources
533
+
534
+ - **OWASP Top 10**: https://owasp.org/Top10/
535
+ - **OWASP Cheat Sheets**: https://cheatsheetseries.owasp.org/
536
+ - **Node.js Security**: https://nodejs.org/en/docs/guides/security/
537
+ - **Snyk**: https://snyk.io/