omgkit 2.2.0 → 2.3.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 (55) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  3. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  4. package/plugin/skills/databases/redis/SKILL.md +53 -860
  5. package/plugin/skills/devops/aws/SKILL.md +68 -672
  6. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  7. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  10. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  12. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  14. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  15. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  16. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  17. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  18. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  19. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  20. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  21. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  22. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  23. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  24. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  25. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  26. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  27. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  28. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  29. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  30. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  31. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  32. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  33. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  34. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  35. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  36. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  37. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  38. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  39. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  40. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  41. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  42. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  43. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  44. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  45. package/plugin/skills/security/oauth/SKILL.md +80 -934
  46. package/plugin/skills/security/owasp/SKILL.md +78 -862
  47. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  48. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  49. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  50. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  51. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  52. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  53. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  54. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  55. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,913 +1,130 @@
1
1
  ---
2
- name: owasp
3
- description: OWASP security best practices for web applications with vulnerability prevention, secure coding, and security testing
4
- category: security
5
- triggers:
6
- - owasp
7
- - web security
8
- - security best practices
9
- - vulnerability prevention
10
- - secure coding
11
- - penetration testing
2
+ name: Applying OWASP Security
3
+ description: Claude applies OWASP security best practices to web applications. Use when preventing vulnerabilities, implementing input validation, securing authentication, configuring security headers, or conducting security reviews.
12
4
  ---
13
5
 
14
- # OWASP
6
+ # Applying OWASP Security
15
7
 
16
- Enterprise-grade **web application security** following OWASP best practices. This skill covers the OWASP Top 10 vulnerabilities, secure coding patterns, input validation, authentication security, and security testing patterns used by top engineering teams.
17
-
18
- ## Purpose
19
-
20
- Build secure web applications:
21
-
22
- - Prevent OWASP Top 10 vulnerabilities
23
- - Implement secure coding practices
24
- - Validate and sanitize user input
25
- - Protect against injection attacks
26
- - Secure authentication and sessions
27
- - Configure security headers
28
- - Implement security testing
29
-
30
- ## Features
31
-
32
- ### 1. Injection Prevention
8
+ ## Quick Start
33
9
 
34
10
  ```typescript
35
- // lib/security/sql-injection.ts
36
- import { PrismaClient } from "@prisma/client";
37
-
38
- const prisma = new PrismaClient();
39
-
40
- // BAD: SQL Injection vulnerable
41
- async function unsafeQuery(userId: string) {
42
- // NEVER DO THIS
43
- return prisma.$queryRawUnsafe(
44
- `SELECT * FROM users WHERE id = '${userId}'`
45
- );
46
- }
47
-
48
- // GOOD: Parameterized queries
49
- async function safeQuery(userId: string) {
50
- return prisma.user.findUnique({
51
- where: { id: userId },
52
- });
53
- }
54
-
55
- // GOOD: Raw query with parameters
56
- async function safeRawQuery(userId: string) {
57
- return prisma.$queryRaw`
58
- SELECT * FROM users WHERE id = ${userId}
59
- `;
60
- }
61
-
62
- // lib/security/nosql-injection.ts
63
- import { Filter } from "mongodb";
64
-
65
- interface User {
66
- _id: string;
67
- email: string;
68
- password: string;
69
- }
70
-
71
- // BAD: NoSQL Injection vulnerable
72
- async function unsafeMongoQuery(db: Db, email: unknown) {
73
- // If email is { $gt: "" }, this returns all users
74
- return db.collection("users").findOne({ email });
75
- }
76
-
77
- // GOOD: Type validation before query
78
- async function safeMongoQuery(db: Db, email: unknown) {
79
- if (typeof email !== "string") {
80
- throw new Error("Invalid email format");
81
- }
82
-
83
- const filter: Filter<User> = { email };
84
- return db.collection<User>("users").findOne(filter);
85
- }
86
-
87
- // lib/security/command-injection.ts
88
- import { execFile } from "child_process";
89
- import { promisify } from "util";
90
-
91
- const execFileAsync = promisify(execFile);
92
-
93
- // BAD: Command Injection vulnerable
94
- async function unsafeExec(filename: string) {
95
- const { exec } = await import("child_process");
96
- // NEVER DO THIS
97
- exec(`ls -la ${filename}`, (error, stdout) => {
98
- console.log(stdout);
99
- });
100
- }
101
-
102
- // GOOD: Use execFile with arguments array
103
- async function safeExec(filename: string) {
104
- // Validate filename
105
- if (!/^[\w\-. ]+$/.test(filename)) {
106
- throw new Error("Invalid filename");
107
- }
108
-
109
- const { stdout } = await execFileAsync("ls", ["-la", filename]);
110
- return stdout;
111
- }
112
-
113
- // GOOD: Avoid shell commands entirely
114
- import fs from "fs/promises";
115
-
116
- async function listFiles(directory: string) {
117
- // Validate and resolve path
118
- const resolvedPath = path.resolve(directory);
119
- const basePath = path.resolve("/allowed/base/path");
120
-
121
- if (!resolvedPath.startsWith(basePath)) {
122
- throw new Error("Path traversal attempt detected");
123
- }
124
-
125
- return fs.readdir(resolvedPath, { withFileTypes: true });
126
- }
127
- ```
128
-
129
- ### 2. XSS Prevention
130
-
131
- ```typescript
132
- // lib/security/xss.ts
11
+ // lib/security/validation.ts
12
+ import { z } from "zod";
133
13
  import DOMPurify from "isomorphic-dompurify";
134
- import { escape } from "html-escaper";
135
14
 
136
- // Sanitize HTML content
137
- export function sanitizeHtml(dirty: string): string {
138
- return DOMPurify.sanitize(dirty, {
139
- ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p", "br", "ul", "ol", "li"],
140
- ALLOWED_ATTR: ["href", "target", "rel"],
141
- ALLOW_DATA_ATTR: false,
142
- });
143
- }
144
-
145
- // Escape for text content
146
- export function escapeHtml(text: string): string {
147
- return escape(text);
148
- }
149
-
150
- // Safe URL validation
151
- export function isValidUrl(url: string): boolean {
152
- try {
153
- const parsed = new URL(url);
154
- return ["http:", "https:"].includes(parsed.protocol);
155
- } catch {
156
- return false;
157
- }
158
- }
159
-
160
- // React component with XSS prevention
161
- import React from "react";
162
-
163
- interface UserContentProps {
164
- content: string;
165
- allowHtml?: boolean;
166
- }
167
-
168
- export function UserContent({ content, allowHtml = false }: UserContentProps) {
169
- if (allowHtml) {
170
- // Sanitize before rendering
171
- const sanitized = sanitizeHtml(content);
172
- return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
173
- }
174
-
175
- // Default: escape all HTML
176
- return <div>{content}</div>;
177
- }
15
+ // Input validation
16
+ export const userSchema = z.object({
17
+ email: z.string().email().max(254),
18
+ password: z.string().min(12).max(128),
19
+ name: z.string().min(2).max(100).regex(/^[\p{L}\s'-]+$/u),
20
+ });
178
21
 
179
- // Express middleware for XSS protection
180
- import { Request, Response, NextFunction } from "express";
22
+ // HTML sanitization
23
+ export const sanitizeHtml = (dirty: string) =>
24
+ DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p"] });
25
+ ```
181
26
 
182
- export function xssProtection(
183
- req: Request,
184
- res: Response,
185
- next: NextFunction
186
- ) {
187
- // Sanitize request body
188
- if (req.body && typeof req.body === "object") {
189
- req.body = sanitizeObject(req.body);
190
- }
27
+ ## Features
191
28
 
192
- // Sanitize query parameters
193
- if (req.query && typeof req.query === "object") {
194
- req.query = sanitizeObject(req.query as Record<string, unknown>);
195
- }
29
+ | Feature | Description | Reference |
30
+ |---------|-------------|-----------|
31
+ | Injection Prevention | SQL, NoSQL, command injection protection | [OWASP Injection](https://owasp.org/Top10/A03_2021-Injection/) |
32
+ | XSS Prevention | Output encoding and HTML sanitization | [OWASP XSS](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) |
33
+ | CSRF Protection | Token-based cross-site request forgery defense | [OWASP CSRF](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) |
34
+ | Authentication Security | Password hashing, rate limiting, session management | [OWASP Auth](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) |
35
+ | Security Headers | CSP, HSTS, X-Frame-Options configuration | [OWASP Headers](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html) |
36
+ | Input Validation | Schema validation and sanitization | [OWASP Validation](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html) |
196
37
 
197
- next();
198
- }
38
+ ## Common Patterns
199
39
 
200
- function sanitizeObject(obj: Record<string, unknown>): Record<string, unknown> {
201
- const sanitized: Record<string, unknown> = {};
40
+ ### Parameterized Queries (SQL Injection Prevention)
202
41
 
203
- for (const [key, value] of Object.entries(obj)) {
204
- if (typeof value === "string") {
205
- sanitized[key] = escapeHtml(value);
206
- } else if (typeof value === "object" && value !== null) {
207
- sanitized[key] = sanitizeObject(value as Record<string, unknown>);
208
- } else {
209
- sanitized[key] = value;
210
- }
211
- }
42
+ ```typescript
43
+ // BAD - SQL injection vulnerable
44
+ const result = await db.$queryRawUnsafe(`SELECT * FROM users WHERE id = '${userId}'`);
212
45
 
213
- return sanitized;
214
- }
46
+ // GOOD - Parameterized query
47
+ const result = await db.user.findUnique({ where: { id: userId } });
48
+ const result = await db.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
215
49
  ```
216
50
 
217
- ### 3. CSRF Protection
51
+ ### CSRF Protection Middleware
218
52
 
219
53
  ```typescript
220
- // lib/security/csrf.ts
221
54
  import crypto from "crypto";
222
- import { Request, Response, NextFunction } from "express";
223
-
224
- const CSRF_TOKEN_LENGTH = 32;
225
- const CSRF_HEADER = "x-csrf-token";
226
- const CSRF_COOKIE = "csrf_token";
227
-
228
- // Generate CSRF token
229
- export function generateCsrfToken(): string {
230
- return crypto.randomBytes(CSRF_TOKEN_LENGTH).toString("hex");
231
- }
232
-
233
- // CSRF middleware
234
- export function csrfProtection() {
235
- return (req: Request, res: Response, next: NextFunction) => {
236
- // Skip for safe methods
237
- if (["GET", "HEAD", "OPTIONS"].includes(req.method)) {
238
- return next();
239
- }
240
55
 
241
- const cookieToken = req.cookies[CSRF_COOKIE];
242
- const headerToken = req.headers[CSRF_HEADER];
56
+ export function csrfProtection(req: Request, res: Response, next: NextFunction) {
57
+ if (["GET", "HEAD", "OPTIONS"].includes(req.method)) return next();
243
58
 
244
- if (!cookieToken || !headerToken || cookieToken !== headerToken) {
245
- return res.status(403).json({
246
- error: "CSRF validation failed",
247
- message: "Invalid or missing CSRF token",
248
- });
249
- }
59
+ const cookieToken = req.cookies["csrf_token"];
60
+ const headerToken = req.headers["x-csrf-token"];
250
61
 
251
- next();
252
- };
253
- }
254
-
255
- // Set CSRF token on response
256
- export function setCsrfToken(req: Request, res: Response, next: NextFunction) {
257
- if (!req.cookies[CSRF_COOKIE]) {
258
- const token = generateCsrfToken();
259
- res.cookie(CSRF_COOKIE, token, {
260
- httpOnly: false, // Must be readable by JavaScript
261
- secure: process.env.NODE_ENV === "production",
262
- sameSite: "strict",
263
- maxAge: 24 * 60 * 60 * 1000, // 24 hours
264
- });
62
+ if (!cookieToken || !headerToken || cookieToken !== headerToken) {
63
+ return res.status(403).json({ error: "CSRF validation failed" });
265
64
  }
266
65
  next();
267
66
  }
67
+ ```
268
68
 
269
- // React hook for CSRF
270
- export function useCsrf() {
271
- const getToken = (): string | null => {
272
- const match = document.cookie.match(new RegExp(`${CSRF_COOKIE}=([^;]+)`));
273
- return match ? match[1] : null;
274
- };
275
-
276
- const fetchWithCsrf = async (url: string, options: RequestInit = {}) => {
277
- const token = getToken();
69
+ ### Security Headers Configuration
278
70
 
279
- return fetch(url, {
280
- ...options,
281
- headers: {
282
- ...options.headers,
283
- [CSRF_HEADER]: token || "",
284
- },
285
- });
286
- };
71
+ ```typescript
72
+ import helmet from "helmet";
287
73
 
288
- return { getToken, fetchWithCsrf };
289
- }
74
+ app.use(helmet({
75
+ contentSecurityPolicy: {
76
+ directives: {
77
+ defaultSrc: ["'self'"],
78
+ scriptSrc: ["'self'", "'strict-dynamic'"],
79
+ styleSrc: ["'self'", "'unsafe-inline'"],
80
+ imgSrc: ["'self'", "data:", "https:"],
81
+ frameSrc: ["'none'"],
82
+ objectSrc: ["'none'"],
83
+ },
84
+ },
85
+ strictTransportSecurity: { maxAge: 31536000, includeSubDomains: true, preload: true },
86
+ frameguard: { action: "deny" },
87
+ }));
290
88
  ```
291
89
 
292
- ### 4. Authentication Security
90
+ ### Password Security
293
91
 
294
92
  ```typescript
295
- // lib/security/password.ts
296
93
  import bcrypt from "bcrypt";
297
- import crypto from "crypto";
298
94
 
299
95
  const SALT_ROUNDS = 12;
300
- const MIN_PASSWORD_LENGTH = 12;
301
- const MAX_PASSWORD_LENGTH = 128;
302
-
303
- export interface PasswordValidationResult {
304
- valid: boolean;
305
- errors: string[];
306
- }
307
-
308
- // Password validation
309
- export function validatePassword(password: string): PasswordValidationResult {
310
- const errors: string[] = [];
311
96
 
312
- if (password.length < MIN_PASSWORD_LENGTH) {
313
- errors.push(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
314
- }
315
-
316
- if (password.length > MAX_PASSWORD_LENGTH) {
317
- errors.push(`Password must be at most ${MAX_PASSWORD_LENGTH} characters`);
318
- }
319
-
320
- if (!/[a-z]/.test(password)) {
321
- errors.push("Password must contain at least one lowercase letter");
322
- }
323
-
324
- if (!/[A-Z]/.test(password)) {
325
- errors.push("Password must contain at least one uppercase letter");
326
- }
327
-
328
- if (!/\d/.test(password)) {
329
- errors.push("Password must contain at least one digit");
330
- }
331
-
332
- if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
333
- errors.push("Password must contain at least one special character");
334
- }
335
-
336
- // Check for common passwords
337
- if (isCommonPassword(password)) {
338
- errors.push("Password is too common");
339
- }
340
-
341
- return { valid: errors.length === 0, errors };
342
- }
343
-
344
- // Hash password
345
97
  export async function hashPassword(password: string): Promise<string> {
346
98
  return bcrypt.hash(password, SALT_ROUNDS);
347
99
  }
348
100
 
349
- // Verify password
350
- export async function verifyPassword(
351
- password: string,
352
- hash: string
353
- ): Promise<boolean> {
101
+ export async function verifyPassword(password: string, hash: string): Promise<boolean> {
354
102
  return bcrypt.compare(password, hash);
355
103
  }
356
104
 
357
- // Generate secure token
358
- export function generateSecureToken(length = 32): string {
359
- return crypto.randomBytes(length).toString("hex");
360
- }
361
-
362
- // lib/security/rate-limit.ts
363
- import rateLimit from "express-rate-limit";
364
- import RedisStore from "rate-limit-redis";
365
- import Redis from "ioredis";
366
-
367
- const redis = new Redis(process.env.REDIS_URL);
368
-
369
- // Login rate limiting
370
- export const loginRateLimiter = rateLimit({
371
- store: new RedisStore({
372
- sendCommand: (...args: string[]) => redis.call(...args),
373
- }),
374
- windowMs: 15 * 60 * 1000, // 15 minutes
375
- max: 5, // 5 attempts per window
376
- message: {
377
- error: "Too many login attempts",
378
- message: "Please try again after 15 minutes",
379
- },
380
- standardHeaders: true,
381
- legacyHeaders: false,
382
- keyGenerator: (req) => {
383
- // Rate limit by IP and email combination
384
- return `${req.ip}:${req.body?.email || "unknown"}`;
385
- },
386
- });
387
-
388
- // API rate limiting
389
- export const apiRateLimiter = rateLimit({
390
- store: new RedisStore({
391
- sendCommand: (...args: string[]) => redis.call(...args),
392
- }),
393
- windowMs: 60 * 1000, // 1 minute
394
- max: 100, // 100 requests per minute
395
- message: {
396
- error: "Too many requests",
397
- message: "Please slow down",
398
- },
399
- standardHeaders: true,
400
- legacyHeaders: false,
401
- });
402
-
403
- // lib/security/session.ts
404
- import session from "express-session";
405
- import RedisStore from "connect-redis";
406
-
407
- export function configureSession(redis: Redis) {
408
- return session({
409
- store: new RedisStore({ client: redis }),
410
- name: "session_id",
411
- secret: process.env.SESSION_SECRET!,
412
- resave: false,
413
- saveUninitialized: false,
414
- cookie: {
415
- secure: process.env.NODE_ENV === "production",
416
- httpOnly: true,
417
- sameSite: "strict",
418
- maxAge: 24 * 60 * 60 * 1000, // 24 hours
419
- domain: process.env.COOKIE_DOMAIN,
420
- },
421
- rolling: true, // Reset expiry on activity
422
- });
423
- }
424
- ```
425
-
426
- ### 5. Security Headers
427
-
428
- ```typescript
429
- // lib/security/headers.ts
430
- import helmet from "helmet";
431
- import { Express } from "express";
432
-
433
- export function configureSecurityHeaders(app: Express) {
434
- // Use helmet with custom configuration
435
- app.use(
436
- helmet({
437
- // Content Security Policy
438
- contentSecurityPolicy: {
439
- directives: {
440
- defaultSrc: ["'self'"],
441
- scriptSrc: ["'self'", "'strict-dynamic'"],
442
- styleSrc: ["'self'", "'unsafe-inline'"], // Required for many CSS-in-JS
443
- imgSrc: ["'self'", "data:", "https:"],
444
- fontSrc: ["'self'", "https://fonts.gstatic.com"],
445
- connectSrc: ["'self'", process.env.API_URL],
446
- frameSrc: ["'none'"],
447
- objectSrc: ["'none'"],
448
- baseUri: ["'self'"],
449
- formAction: ["'self'"],
450
- frameAncestors: ["'none'"],
451
- upgradeInsecureRequests: [],
452
- },
453
- },
454
-
455
- // Strict Transport Security
456
- strictTransportSecurity: {
457
- maxAge: 31536000, // 1 year
458
- includeSubDomains: true,
459
- preload: true,
460
- },
461
-
462
- // Prevent clickjacking
463
- frameguard: {
464
- action: "deny",
465
- },
466
-
467
- // Prevent MIME sniffing
468
- noSniff: true,
469
-
470
- // XSS filter (legacy browsers)
471
- xssFilter: true,
472
-
473
- // Referrer policy
474
- referrerPolicy: {
475
- policy: "strict-origin-when-cross-origin",
476
- },
477
-
478
- // Permissions policy
479
- permittedCrossDomainPolicies: {
480
- permittedPolicies: "none",
481
- },
482
- })
483
- );
484
-
485
- // Additional security headers
486
- app.use((req, res, next) => {
487
- // Prevent caching of sensitive data
488
- res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
489
- res.setHeader("Pragma", "no-cache");
490
-
491
- // Remove server information
492
- res.removeHeader("X-Powered-By");
493
-
494
- next();
495
- });
496
- }
497
-
498
- // Next.js security headers
499
- // next.config.js
500
- const securityHeaders = [
501
- {
502
- key: "X-DNS-Prefetch-Control",
503
- value: "on",
504
- },
505
- {
506
- key: "Strict-Transport-Security",
507
- value: "max-age=31536000; includeSubDomains; preload",
508
- },
509
- {
510
- key: "X-Frame-Options",
511
- value: "DENY",
512
- },
513
- {
514
- key: "X-Content-Type-Options",
515
- value: "nosniff",
516
- },
517
- {
518
- key: "X-XSS-Protection",
519
- value: "1; mode=block",
520
- },
521
- {
522
- key: "Referrer-Policy",
523
- value: "strict-origin-when-cross-origin",
524
- },
525
- {
526
- key: "Permissions-Policy",
527
- value: "camera=(), microphone=(), geolocation=(), interest-cohort=()",
528
- },
529
- {
530
- key: "Content-Security-Policy",
531
- value: `
532
- default-src 'self';
533
- script-src 'self' 'unsafe-eval' 'unsafe-inline';
534
- style-src 'self' 'unsafe-inline';
535
- img-src 'self' data: https:;
536
- font-src 'self';
537
- connect-src 'self' ${process.env.NEXT_PUBLIC_API_URL};
538
- frame-ancestors 'none';
539
- base-uri 'self';
540
- form-action 'self';
541
- `.replace(/\s+/g, " ").trim(),
542
- },
543
- ];
544
-
545
- module.exports = {
546
- async headers() {
547
- return [
548
- {
549
- source: "/:path*",
550
- headers: securityHeaders,
551
- },
552
- ];
553
- },
554
- };
555
- ```
556
-
557
- ### 6. Input Validation
558
-
559
- ```typescript
560
- // lib/security/validation.ts
561
- import { z } from "zod";
562
-
563
- // Email validation schema
564
- export const emailSchema = z
565
- .string()
566
- .email("Invalid email format")
567
- .max(254, "Email too long")
568
- .transform((email) => email.toLowerCase().trim());
569
-
570
- // Password validation schema
571
- export const passwordSchema = z
572
- .string()
573
- .min(12, "Password must be at least 12 characters")
574
- .max(128, "Password must be at most 128 characters")
575
- .regex(/[a-z]/, "Password must contain a lowercase letter")
576
- .regex(/[A-Z]/, "Password must contain an uppercase letter")
577
- .regex(/\d/, "Password must contain a digit")
578
- .regex(/[!@#$%^&*]/, "Password must contain a special character");
579
-
580
- // User registration schema
581
- export const registerSchema = z.object({
582
- email: emailSchema,
583
- password: passwordSchema,
584
- name: z
585
- .string()
586
- .min(2, "Name must be at least 2 characters")
587
- .max(100, "Name must be at most 100 characters")
588
- .regex(/^[\p{L}\s'-]+$/u, "Name contains invalid characters"),
589
- });
590
-
591
- // URL validation
592
- export const urlSchema = z
593
- .string()
594
- .url("Invalid URL format")
595
- .refine(
596
- (url) => {
597
- try {
598
- const parsed = new URL(url);
599
- return ["http:", "https:"].includes(parsed.protocol);
600
- } catch {
601
- return false;
602
- }
603
- },
604
- { message: "URL must use http or https protocol" }
605
- );
606
-
607
- // File upload validation
608
- export const fileUploadSchema = z.object({
609
- filename: z
610
- .string()
611
- .max(255)
612
- .regex(
613
- /^[\w\-. ]+$/,
614
- "Filename contains invalid characters"
615
- ),
616
- mimetype: z.enum([
617
- "image/jpeg",
618
- "image/png",
619
- "image/gif",
620
- "application/pdf",
621
- ]),
622
- size: z.number().max(10 * 1024 * 1024, "File size must be under 10MB"),
623
- });
624
-
625
- // Express validation middleware
626
- import { Request, Response, NextFunction } from "express";
627
-
628
- export function validate<T>(schema: z.ZodSchema<T>) {
629
- return async (req: Request, res: Response, next: NextFunction) => {
630
- try {
631
- req.body = await schema.parseAsync(req.body);
632
- next();
633
- } catch (error) {
634
- if (error instanceof z.ZodError) {
635
- return res.status(400).json({
636
- error: "Validation failed",
637
- details: error.errors.map((e) => ({
638
- field: e.path.join("."),
639
- message: e.message,
640
- })),
641
- });
642
- }
643
- next(error);
644
- }
645
- };
646
- }
647
-
648
- // Usage
649
- app.post("/register", validate(registerSchema), async (req, res) => {
650
- // req.body is now typed and validated
651
- const { email, password, name } = req.body;
652
- // ...
653
- });
654
- ```
655
-
656
- ### 7. Security Testing
657
-
658
- ```typescript
659
- // tests/security/xss.test.ts
660
- import { sanitizeHtml, escapeHtml } from "@/lib/security/xss";
661
-
662
- describe("XSS Prevention", () => {
663
- describe("sanitizeHtml", () => {
664
- it("removes script tags", () => {
665
- const input = '<script>alert("xss")</script>';
666
- expect(sanitizeHtml(input)).toBe("");
667
- });
668
-
669
- it("removes event handlers", () => {
670
- const input = '<img src="x" onerror="alert(1)">';
671
- expect(sanitizeHtml(input)).toBe("");
672
- });
673
-
674
- it("removes javascript: URLs", () => {
675
- const input = '<a href="javascript:alert(1)">click</a>';
676
- const result = sanitizeHtml(input);
677
- expect(result).not.toContain("javascript:");
678
- });
679
-
680
- it("allows safe HTML tags", () => {
681
- const input = "<p><strong>Bold</strong> and <em>italic</em></p>";
682
- expect(sanitizeHtml(input)).toBe(input);
683
- });
684
-
685
- it("removes data attributes", () => {
686
- const input = '<div data-dangerous="value">content</div>';
687
- expect(sanitizeHtml(input)).not.toContain("data-dangerous");
688
- });
689
- });
690
-
691
- describe("escapeHtml", () => {
692
- it("escapes HTML entities", () => {
693
- const input = '<script>alert("xss")</script>';
694
- const result = escapeHtml(input);
695
- expect(result).toBe("&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;");
696
- });
697
-
698
- it("escapes ampersands", () => {
699
- expect(escapeHtml("&")).toBe("&amp;");
700
- });
701
- });
702
- });
703
-
704
- // tests/security/injection.test.ts
705
- import { safeQuery, safeRawQuery } from "@/lib/security/sql-injection";
706
-
707
- describe("SQL Injection Prevention", () => {
708
- it("handles malicious input safely", async () => {
709
- const maliciousInput = "'; DROP TABLE users; --";
710
-
711
- // Should not throw and should not execute injection
712
- await expect(safeQuery(maliciousInput)).resolves.toBeNull();
713
- });
714
-
715
- it("uses parameterized queries", async () => {
716
- const userId = "123";
717
- const result = await safeRawQuery(userId);
718
-
719
- // Query should be parameterized, not interpolated
720
- expect(result).toBeDefined();
721
- });
722
- });
723
-
724
- // tests/security/csrf.test.ts
725
- import request from "supertest";
726
- import app from "@/app";
727
-
728
- describe("CSRF Protection", () => {
729
- it("rejects requests without CSRF token", async () => {
730
- const response = await request(app)
731
- .post("/api/user/profile")
732
- .send({ name: "Test" });
733
-
734
- expect(response.status).toBe(403);
735
- expect(response.body.error).toBe("CSRF validation failed");
736
- });
737
-
738
- it("accepts requests with valid CSRF token", async () => {
739
- // Get CSRF token
740
- const getResponse = await request(app).get("/api/csrf-token");
741
- const csrfToken = getResponse.body.token;
742
-
743
- const response = await request(app)
744
- .post("/api/user/profile")
745
- .set("x-csrf-token", csrfToken)
746
- .set("Cookie", getResponse.headers["set-cookie"])
747
- .send({ name: "Test" });
748
-
749
- expect(response.status).not.toBe(403);
750
- });
751
- });
752
-
753
- // tests/security/auth.test.ts
754
- import { validatePassword, hashPassword, verifyPassword } from "@/lib/security/password";
755
-
756
- describe("Authentication Security", () => {
757
- describe("Password Validation", () => {
758
- it("rejects short passwords", () => {
759
- const result = validatePassword("Short1!");
760
- expect(result.valid).toBe(false);
761
- expect(result.errors).toContain("Password must be at least 12 characters");
762
- });
763
-
764
- it("requires complexity", () => {
765
- const result = validatePassword("simplelongpassword");
766
- expect(result.valid).toBe(false);
767
- expect(result.errors.length).toBeGreaterThan(0);
768
- });
769
-
770
- it("accepts strong passwords", () => {
771
- const result = validatePassword("SecureP@ssw0rd123!");
772
- expect(result.valid).toBe(true);
773
- expect(result.errors).toHaveLength(0);
774
- });
775
- });
776
-
777
- describe("Password Hashing", () => {
778
- it("hashes passwords securely", async () => {
779
- const password = "SecureP@ssw0rd123!";
780
- const hash = await hashPassword(password);
781
-
782
- expect(hash).not.toBe(password);
783
- expect(hash.startsWith("$2b$")).toBe(true);
784
- });
785
-
786
- it("verifies correct passwords", async () => {
787
- const password = "SecureP@ssw0rd123!";
788
- const hash = await hashPassword(password);
789
-
790
- expect(await verifyPassword(password, hash)).toBe(true);
791
- });
792
-
793
- it("rejects incorrect passwords", async () => {
794
- const hash = await hashPassword("SecureP@ssw0rd123!");
795
-
796
- expect(await verifyPassword("WrongPassword1!", hash)).toBe(false);
797
- });
798
- });
799
- });
800
- ```
801
-
802
- ## Use Cases
803
-
804
- ### Security Audit Checklist
805
-
806
- ```typescript
807
- // lib/security/audit.ts
808
- export interface SecurityAuditResult {
809
- category: string;
810
- check: string;
811
- status: "pass" | "fail" | "warning";
812
- message: string;
813
- }
814
-
815
- export async function runSecurityAudit(): Promise<SecurityAuditResult[]> {
816
- const results: SecurityAuditResult[] = [];
817
-
818
- // Check HTTPS
819
- results.push({
820
- category: "Transport",
821
- check: "HTTPS Enabled",
822
- status: process.env.NODE_ENV === "production" ? "pass" : "warning",
823
- message: "HTTPS should be enabled in production",
824
- });
825
-
826
- // Check security headers
827
- results.push({
828
- category: "Headers",
829
- check: "Security Headers",
830
- status: "pass",
831
- message: "Helmet middleware configured",
832
- });
833
-
834
- // Check rate limiting
835
- results.push({
836
- category: "Rate Limiting",
837
- check: "Login Rate Limiting",
838
- status: "pass",
839
- message: "Login endpoints rate limited",
840
- });
841
-
842
- // Check CSRF protection
843
- results.push({
844
- category: "CSRF",
845
- check: "CSRF Protection",
846
- status: "pass",
847
- message: "CSRF tokens required for state-changing requests",
848
- });
849
-
850
- return results;
105
+ export function validatePasswordStrength(password: string): string[] {
106
+ const errors: string[] = [];
107
+ if (password.length < 12) errors.push("Must be at least 12 characters");
108
+ if (!/[a-z]/.test(password)) errors.push("Must contain lowercase");
109
+ if (!/[A-Z]/.test(password)) errors.push("Must contain uppercase");
110
+ if (!/\d/.test(password)) errors.push("Must contain digit");
111
+ if (!/[!@#$%^&*]/.test(password)) errors.push("Must contain special character");
112
+ return errors;
851
113
  }
852
114
  ```
853
115
 
854
- ### Dependency Scanning
855
-
856
- ```yaml
857
- # .github/workflows/security.yml
858
- name: Security Scan
859
-
860
- on:
861
- push:
862
- branches: [main]
863
- schedule:
864
- - cron: "0 0 * * *" # Daily
865
-
866
- jobs:
867
- security:
868
- runs-on: ubuntu-latest
869
- steps:
870
- - uses: actions/checkout@v4
871
-
872
- - name: Run npm audit
873
- run: npm audit --audit-level=high
874
-
875
- - name: Run Snyk scan
876
- uses: snyk/actions/node@master
877
- env:
878
- SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
879
-
880
- - name: Run SAST scan
881
- uses: github/codeql-action/analyze@v3
882
- ```
883
-
884
116
  ## Best Practices
885
117
 
886
- ### Do's
887
-
888
- - Validate all user input on the server
889
- - Use parameterized queries for database access
890
- - Implement proper authentication and session management
891
- - Set security headers on all responses
892
- - Use HTTPS for all communications
893
- - Implement rate limiting on sensitive endpoints
894
- - Log security events for monitoring
895
- - Keep dependencies updated
896
- - Conduct regular security audits
897
- - Follow the principle of least privilege
898
-
899
- ### Don'ts
900
-
901
- - Don't trust client-side validation alone
902
- - Don't store sensitive data in plain text
903
- - Don't expose detailed error messages to users
904
- - Don't use deprecated cryptographic algorithms
905
- - Don't disable security features for convenience
906
- - Don't hardcode secrets in source code
907
- - Don't ignore security warnings
908
- - Don't use eval() or similar functions
909
- - Don't allow unlimited file uploads
910
- - Don't skip security testing
118
+ | Do | Avoid |
119
+ |----|-------|
120
+ | Validate all input on the server side | Trusting client-side validation alone |
121
+ | Use parameterized queries for all DB access | String concatenation in queries |
122
+ | Set security headers on all responses | Disabling security features for convenience |
123
+ | Implement rate limiting on sensitive endpoints | Allowing unlimited attempts |
124
+ | Hash passwords with bcrypt (12+ rounds) | Using weak/deprecated crypto algorithms |
125
+ | Log security events for monitoring | Exposing detailed error messages to users |
126
+ | Keep dependencies updated | Ignoring security warnings |
127
+ | Use HTTPS for all communications | Hardcoding secrets in source code |
911
128
 
912
129
  ## References
913
130
 
@@ -915,4 +132,3 @@ jobs:
915
132
  - [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
916
133
  - [OWASP Testing Guide](https://owasp.org/www-project-web-security-testing-guide/)
917
134
  - [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/)
918
- - [CWE Top 25](https://cwe.mitre.org/top25/archive/2023/2023_top25_list.html)