omgkit 2.0.7 → 2.1.1
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.
- package/package.json +2 -2
- package/plugin/skills/backend/api-architecture/SKILL.md +857 -0
- package/plugin/skills/backend/caching-strategies/SKILL.md +755 -0
- package/plugin/skills/backend/event-driven-architecture/SKILL.md +753 -0
- package/plugin/skills/backend/real-time-systems/SKILL.md +635 -0
- package/plugin/skills/databases/database-optimization/SKILL.md +571 -0
- package/plugin/skills/databases/postgresql/SKILL.md +494 -18
- package/plugin/skills/devops/docker/SKILL.md +466 -18
- package/plugin/skills/devops/monorepo-management/SKILL.md +595 -0
- package/plugin/skills/devops/observability/SKILL.md +622 -0
- package/plugin/skills/devops/performance-profiling/SKILL.md +905 -0
- package/plugin/skills/frameworks/nextjs/SKILL.md +407 -44
- package/plugin/skills/frameworks/react/SKILL.md +1006 -32
- package/plugin/skills/frontend/advanced-ui-design/SKILL.md +426 -0
- package/plugin/skills/integrations/ai-integration/SKILL.md +730 -0
- package/plugin/skills/integrations/payment-integration/SKILL.md +735 -0
- package/plugin/skills/languages/python/SKILL.md +489 -25
- package/plugin/skills/languages/typescript/SKILL.md +379 -30
- package/plugin/skills/methodology/problem-solving/SKILL.md +355 -0
- package/plugin/skills/methodology/research-validation/SKILL.md +668 -0
- package/plugin/skills/methodology/sequential-thinking/SKILL.md +260 -0
- package/plugin/skills/mobile/mobile-development/SKILL.md +756 -0
- package/plugin/skills/security/security-hardening/SKILL.md +633 -0
- package/plugin/skills/tools/document-processing/SKILL.md +916 -0
- package/plugin/skills/tools/image-processing/SKILL.md +748 -0
- package/plugin/skills/tools/mcp-development/SKILL.md +883 -0
- package/plugin/skills/tools/media-processing/SKILL.md +831 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-hardening
|
|
3
|
+
description: Advanced security patterns including zero-trust architecture, secret management, CSP, and compliance frameworks
|
|
4
|
+
category: security
|
|
5
|
+
triggers:
|
|
6
|
+
- security hardening
|
|
7
|
+
- zero trust
|
|
8
|
+
- secret management
|
|
9
|
+
- csp
|
|
10
|
+
- security headers
|
|
11
|
+
- compliance
|
|
12
|
+
- penetration testing
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Security Hardening
|
|
16
|
+
|
|
17
|
+
Implement **advanced security patterns** beyond basic OWASP guidelines. This skill covers zero-trust architecture, secret management, security headers, and compliance frameworks.
|
|
18
|
+
|
|
19
|
+
## Purpose
|
|
20
|
+
|
|
21
|
+
Build defense-in-depth for production systems:
|
|
22
|
+
|
|
23
|
+
- Implement zero-trust security model
|
|
24
|
+
- Manage secrets securely with Vault
|
|
25
|
+
- Configure comprehensive security headers
|
|
26
|
+
- Design Content Security Policy (CSP)
|
|
27
|
+
- Prepare for security audits
|
|
28
|
+
- Meet compliance requirements (SOC2, GDPR)
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
### 1. Zero-Trust Architecture
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// Zero-trust middleware chain
|
|
36
|
+
import { Request, Response, NextFunction } from 'express';
|
|
37
|
+
|
|
38
|
+
// 1. Verify identity on every request
|
|
39
|
+
async function verifyIdentity(req: Request, res: Response, next: NextFunction) {
|
|
40
|
+
const token = extractToken(req);
|
|
41
|
+
|
|
42
|
+
if (!token) {
|
|
43
|
+
return res.status(401).json({ error: 'Authentication required' });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Verify token signature and expiration
|
|
48
|
+
const decoded = await verifyToken(token);
|
|
49
|
+
|
|
50
|
+
// Check if token is revoked
|
|
51
|
+
if (await isTokenRevoked(decoded.jti)) {
|
|
52
|
+
return res.status(401).json({ error: 'Token revoked' });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Verify user still exists and is active
|
|
56
|
+
const user = await getUserById(decoded.sub);
|
|
57
|
+
if (!user || user.status !== 'active') {
|
|
58
|
+
return res.status(401).json({ error: 'User not found or inactive' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
req.user = user;
|
|
62
|
+
req.tokenClaims = decoded;
|
|
63
|
+
next();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return res.status(401).json({ error: 'Invalid token' });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. Verify authorization for every action
|
|
70
|
+
async function verifyAuthorization(resource: string, action: string) {
|
|
71
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
72
|
+
const allowed = checkPermission(req.user, resource, action);
|
|
73
|
+
|
|
74
|
+
if (!allowed) {
|
|
75
|
+
auditLog({
|
|
76
|
+
event: 'authorization_denied',
|
|
77
|
+
userId: req.user.id,
|
|
78
|
+
resource,
|
|
79
|
+
action,
|
|
80
|
+
ip: req.ip,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
next();
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 3. Verify device/context
|
|
91
|
+
async function verifyContext(req: Request, res: Response, next: NextFunction) {
|
|
92
|
+
const deviceFingerprint = req.headers['x-device-fingerprint'];
|
|
93
|
+
const geoLocation = await getGeoLocation(req.ip);
|
|
94
|
+
|
|
95
|
+
// Check for unusual patterns
|
|
96
|
+
const risk = await assessRisk({
|
|
97
|
+
userId: req.user.id,
|
|
98
|
+
deviceFingerprint,
|
|
99
|
+
geoLocation,
|
|
100
|
+
userAgent: req.headers['user-agent'],
|
|
101
|
+
timestamp: Date.now(),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (risk.score > 0.8) {
|
|
105
|
+
// Require step-up authentication
|
|
106
|
+
return res.status(403).json({
|
|
107
|
+
error: 'Additional verification required',
|
|
108
|
+
challenge: risk.challengeType,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (risk.score > 0.5) {
|
|
113
|
+
// Log suspicious activity
|
|
114
|
+
auditLog({
|
|
115
|
+
event: 'suspicious_activity',
|
|
116
|
+
userId: req.user.id,
|
|
117
|
+
riskScore: risk.score,
|
|
118
|
+
factors: risk.factors,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
next();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Apply zero-trust chain
|
|
126
|
+
app.use('/api', verifyIdentity, verifyContext);
|
|
127
|
+
app.get('/api/users/:id', verifyAuthorization('users', 'read'), getUser);
|
|
128
|
+
app.put('/api/users/:id', verifyAuthorization('users', 'write'), updateUser);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 2. Secret Management with Vault
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import Vault from 'node-vault';
|
|
135
|
+
|
|
136
|
+
class SecretManager {
|
|
137
|
+
private vault: Vault.client;
|
|
138
|
+
private cache: Map<string, { value: any; expires: number }> = new Map();
|
|
139
|
+
private cacheTTL = 300000; // 5 minutes
|
|
140
|
+
|
|
141
|
+
constructor() {
|
|
142
|
+
this.vault = Vault({
|
|
143
|
+
apiVersion: 'v1',
|
|
144
|
+
endpoint: process.env.VAULT_ADDR,
|
|
145
|
+
token: process.env.VAULT_TOKEN,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getSecret(path: string): Promise<any> {
|
|
150
|
+
// Check cache
|
|
151
|
+
const cached = this.cache.get(path);
|
|
152
|
+
if (cached && cached.expires > Date.now()) {
|
|
153
|
+
return cached.value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Fetch from Vault
|
|
157
|
+
const response = await this.vault.read(`secret/data/${path}`);
|
|
158
|
+
const secret = response.data.data;
|
|
159
|
+
|
|
160
|
+
// Cache the secret
|
|
161
|
+
this.cache.set(path, {
|
|
162
|
+
value: secret,
|
|
163
|
+
expires: Date.now() + this.cacheTTL,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return secret;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async getDatabaseCredentials(): Promise<{ username: string; password: string }> {
|
|
170
|
+
// Dynamic database credentials
|
|
171
|
+
const response = await this.vault.read('database/creds/my-role');
|
|
172
|
+
return {
|
|
173
|
+
username: response.data.username,
|
|
174
|
+
password: response.data.password,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async rotateSecret(path: string, newValue: any): Promise<void> {
|
|
179
|
+
await this.vault.write(`secret/data/${path}`, {
|
|
180
|
+
data: newValue,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Invalidate cache
|
|
184
|
+
this.cache.delete(path);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// AWS IAM credentials via Vault
|
|
188
|
+
async getAWSCredentials(): Promise<AWS.Credentials> {
|
|
189
|
+
const response = await this.vault.read('aws/creds/my-role');
|
|
190
|
+
return {
|
|
191
|
+
accessKeyId: response.data.access_key,
|
|
192
|
+
secretAccessKey: response.data.secret_key,
|
|
193
|
+
sessionToken: response.data.security_token,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Environment variable encryption
|
|
199
|
+
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';
|
|
200
|
+
|
|
201
|
+
async function encryptEnvFile(envContent: string, password: string): Promise<string> {
|
|
202
|
+
const salt = randomBytes(16);
|
|
203
|
+
const key = await new Promise<Buffer>((resolve, reject) => {
|
|
204
|
+
scrypt(password, salt, 32, (err, key) => {
|
|
205
|
+
if (err) reject(err);
|
|
206
|
+
else resolve(key);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const iv = randomBytes(16);
|
|
211
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
212
|
+
|
|
213
|
+
let encrypted = cipher.update(envContent, 'utf8', 'hex');
|
|
214
|
+
encrypted += cipher.final('hex');
|
|
215
|
+
|
|
216
|
+
const authTag = cipher.getAuthTag();
|
|
217
|
+
|
|
218
|
+
return JSON.stringify({
|
|
219
|
+
salt: salt.toString('hex'),
|
|
220
|
+
iv: iv.toString('hex'),
|
|
221
|
+
authTag: authTag.toString('hex'),
|
|
222
|
+
encrypted,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 3. Security Headers
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import helmet from 'helmet';
|
|
231
|
+
|
|
232
|
+
// Comprehensive security headers
|
|
233
|
+
app.use(helmet({
|
|
234
|
+
contentSecurityPolicy: {
|
|
235
|
+
directives: {
|
|
236
|
+
defaultSrc: ["'self'"],
|
|
237
|
+
scriptSrc: [
|
|
238
|
+
"'self'",
|
|
239
|
+
"'strict-dynamic'",
|
|
240
|
+
(req, res) => `'nonce-${res.locals.nonce}'`,
|
|
241
|
+
],
|
|
242
|
+
styleSrc: ["'self'", "'unsafe-inline'"], // Consider using nonces for styles too
|
|
243
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
244
|
+
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
|
245
|
+
connectSrc: ["'self'", "https://api.example.com"],
|
|
246
|
+
frameSrc: ["'none'"],
|
|
247
|
+
objectSrc: ["'none'"],
|
|
248
|
+
baseUri: ["'self'"],
|
|
249
|
+
formAction: ["'self'"],
|
|
250
|
+
upgradeInsecureRequests: [],
|
|
251
|
+
blockAllMixedContent: [],
|
|
252
|
+
},
|
|
253
|
+
reportOnly: false,
|
|
254
|
+
},
|
|
255
|
+
crossOriginEmbedderPolicy: true,
|
|
256
|
+
crossOriginOpenerPolicy: { policy: 'same-origin' },
|
|
257
|
+
crossOriginResourcePolicy: { policy: 'same-origin' },
|
|
258
|
+
dnsPrefetchControl: { allow: false },
|
|
259
|
+
frameguard: { action: 'deny' },
|
|
260
|
+
hsts: {
|
|
261
|
+
maxAge: 31536000,
|
|
262
|
+
includeSubDomains: true,
|
|
263
|
+
preload: true,
|
|
264
|
+
},
|
|
265
|
+
ieNoOpen: true,
|
|
266
|
+
noSniff: true,
|
|
267
|
+
originAgentCluster: true,
|
|
268
|
+
permittedCrossDomainPolicies: { permittedPolicies: 'none' },
|
|
269
|
+
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
|
|
270
|
+
xssFilter: true,
|
|
271
|
+
}));
|
|
272
|
+
|
|
273
|
+
// CSP nonce generation
|
|
274
|
+
app.use((req, res, next) => {
|
|
275
|
+
res.locals.nonce = crypto.randomBytes(16).toString('base64');
|
|
276
|
+
next();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Custom security headers
|
|
280
|
+
app.use((req, res, next) => {
|
|
281
|
+
// Prevent caching of sensitive responses
|
|
282
|
+
if (req.path.startsWith('/api/')) {
|
|
283
|
+
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
|
284
|
+
res.set('Pragma', 'no-cache');
|
|
285
|
+
res.set('Expires', '0');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Feature policy / Permissions policy
|
|
289
|
+
res.set('Permissions-Policy',
|
|
290
|
+
'accelerometer=(), camera=(), geolocation=(), gyroscope=(), ' +
|
|
291
|
+
'magnetometer=(), microphone=(), payment=(), usb=()'
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Clear site data on logout
|
|
295
|
+
if (req.path === '/logout') {
|
|
296
|
+
res.set('Clear-Site-Data', '"cache", "cookies", "storage"');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
next();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// CSP violation reporting
|
|
303
|
+
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
|
|
304
|
+
const report = req.body['csp-report'];
|
|
305
|
+
|
|
306
|
+
logger.warn({
|
|
307
|
+
type: 'csp_violation',
|
|
308
|
+
violatedDirective: report['violated-directive'],
|
|
309
|
+
blockedUri: report['blocked-uri'],
|
|
310
|
+
documentUri: report['document-uri'],
|
|
311
|
+
sourceFile: report['source-file'],
|
|
312
|
+
lineNumber: report['line-number'],
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
res.status(204).end();
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 4. Input Validation & Sanitization
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
import { z } from 'zod';
|
|
323
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
324
|
+
import sqlstring from 'sqlstring';
|
|
325
|
+
|
|
326
|
+
// Strict input validation schemas
|
|
327
|
+
const UserInputSchema = z.object({
|
|
328
|
+
email: z.string().email().max(255).toLowerCase(),
|
|
329
|
+
name: z.string()
|
|
330
|
+
.min(2)
|
|
331
|
+
.max(100)
|
|
332
|
+
.regex(/^[a-zA-Z\s'-]+$/, 'Invalid characters in name'),
|
|
333
|
+
phone: z.string()
|
|
334
|
+
.regex(/^\+?[1-9]\d{1,14}$/, 'Invalid phone number')
|
|
335
|
+
.optional(),
|
|
336
|
+
bio: z.string().max(1000).optional(),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// HTML sanitization for rich text
|
|
340
|
+
function sanitizeHTML(html: string): string {
|
|
341
|
+
return DOMPurify.sanitize(html, {
|
|
342
|
+
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'ul', 'ol', 'li'],
|
|
343
|
+
ALLOWED_ATTR: ['href', 'target', 'rel'],
|
|
344
|
+
ALLOW_DATA_ATTR: false,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// SQL injection prevention (for raw queries)
|
|
349
|
+
function sanitizeSQL(input: string): string {
|
|
350
|
+
return sqlstring.escape(input);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Path traversal prevention
|
|
354
|
+
function sanitizePath(userPath: string, basePath: string): string {
|
|
355
|
+
const resolvedPath = path.resolve(basePath, userPath);
|
|
356
|
+
|
|
357
|
+
if (!resolvedPath.startsWith(path.resolve(basePath))) {
|
|
358
|
+
throw new Error('Path traversal detected');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return resolvedPath;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// File upload validation
|
|
365
|
+
const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
|
|
366
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
367
|
+
|
|
368
|
+
async function validateFileUpload(file: Express.Multer.File): Promise<void> {
|
|
369
|
+
// Check file size
|
|
370
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
371
|
+
throw new Error('File too large');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Verify MIME type using magic bytes
|
|
375
|
+
const fileType = await import('file-type');
|
|
376
|
+
const type = await fileType.fromBuffer(file.buffer);
|
|
377
|
+
|
|
378
|
+
if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) {
|
|
379
|
+
throw new Error('Invalid file type');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Check for malicious content in images
|
|
383
|
+
if (type.mime.startsWith('image/')) {
|
|
384
|
+
await scanImageForMalware(file.buffer);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 5. Audit Logging
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
interface AuditEvent {
|
|
393
|
+
timestamp: Date;
|
|
394
|
+
eventType: string;
|
|
395
|
+
userId?: string;
|
|
396
|
+
resourceType?: string;
|
|
397
|
+
resourceId?: string;
|
|
398
|
+
action: string;
|
|
399
|
+
outcome: 'success' | 'failure';
|
|
400
|
+
ipAddress: string;
|
|
401
|
+
userAgent?: string;
|
|
402
|
+
details?: Record<string, any>;
|
|
403
|
+
requestId?: string;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
class AuditLogger {
|
|
407
|
+
async log(event: AuditEvent): Promise<void> {
|
|
408
|
+
// Store in append-only audit log
|
|
409
|
+
await db.auditLog.create({
|
|
410
|
+
data: {
|
|
411
|
+
...event,
|
|
412
|
+
timestamp: event.timestamp || new Date(),
|
|
413
|
+
hash: this.calculateHash(event), // Tamper detection
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Send to SIEM for real-time analysis
|
|
418
|
+
await this.sendToSIEM(event);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private calculateHash(event: AuditEvent): string {
|
|
422
|
+
const content = JSON.stringify({
|
|
423
|
+
...event,
|
|
424
|
+
timestamp: event.timestamp.toISOString(),
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
return crypto
|
|
428
|
+
.createHmac('sha256', process.env.AUDIT_SECRET!)
|
|
429
|
+
.update(content)
|
|
430
|
+
.digest('hex');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private async sendToSIEM(event: AuditEvent): Promise<void> {
|
|
434
|
+
// Send to security information and event management system
|
|
435
|
+
await fetch(process.env.SIEM_ENDPOINT!, {
|
|
436
|
+
method: 'POST',
|
|
437
|
+
headers: { 'Content-Type': 'application/json' },
|
|
438
|
+
body: JSON.stringify(event),
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Audit middleware
|
|
444
|
+
function auditMiddleware(action: string, resourceType: string) {
|
|
445
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
446
|
+
const startTime = Date.now();
|
|
447
|
+
|
|
448
|
+
res.on('finish', async () => {
|
|
449
|
+
await auditLogger.log({
|
|
450
|
+
timestamp: new Date(),
|
|
451
|
+
eventType: 'api_call',
|
|
452
|
+
userId: req.user?.id,
|
|
453
|
+
resourceType,
|
|
454
|
+
resourceId: req.params.id,
|
|
455
|
+
action,
|
|
456
|
+
outcome: res.statusCode < 400 ? 'success' : 'failure',
|
|
457
|
+
ipAddress: req.ip,
|
|
458
|
+
userAgent: req.headers['user-agent'],
|
|
459
|
+
requestId: req.headers['x-request-id'] as string,
|
|
460
|
+
details: {
|
|
461
|
+
method: req.method,
|
|
462
|
+
path: req.path,
|
|
463
|
+
statusCode: res.statusCode,
|
|
464
|
+
duration: Date.now() - startTime,
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
next();
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Security-sensitive actions require audit
|
|
474
|
+
app.delete('/api/users/:id',
|
|
475
|
+
auditMiddleware('delete', 'user'),
|
|
476
|
+
deleteUser
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
app.post('/api/admin/roles',
|
|
480
|
+
auditMiddleware('create', 'role'),
|
|
481
|
+
createRole
|
|
482
|
+
);
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### 6. Compliance Frameworks
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
// GDPR compliance helpers
|
|
489
|
+
class GDPRCompliance {
|
|
490
|
+
// Right to access - export user data
|
|
491
|
+
async exportUserData(userId: string): Promise<UserDataExport> {
|
|
492
|
+
const user = await db.user.findUnique({
|
|
493
|
+
where: { id: userId },
|
|
494
|
+
include: {
|
|
495
|
+
orders: true,
|
|
496
|
+
addresses: true,
|
|
497
|
+
preferences: true,
|
|
498
|
+
activityLog: true,
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
personalData: {
|
|
504
|
+
name: user.name,
|
|
505
|
+
email: user.email,
|
|
506
|
+
phone: user.phone,
|
|
507
|
+
createdAt: user.createdAt,
|
|
508
|
+
},
|
|
509
|
+
orders: user.orders,
|
|
510
|
+
addresses: user.addresses,
|
|
511
|
+
preferences: user.preferences,
|
|
512
|
+
activityLog: user.activityLog,
|
|
513
|
+
exportedAt: new Date(),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Right to erasure - delete user data
|
|
518
|
+
async eraseUserData(userId: string): Promise<void> {
|
|
519
|
+
await db.$transaction([
|
|
520
|
+
db.activityLog.deleteMany({ where: { userId } }),
|
|
521
|
+
db.address.deleteMany({ where: { userId } }),
|
|
522
|
+
db.preference.deleteMany({ where: { userId } }),
|
|
523
|
+
// Anonymize orders (keep for accounting)
|
|
524
|
+
db.order.updateMany({
|
|
525
|
+
where: { userId },
|
|
526
|
+
data: { userId: null, customerName: 'DELETED' },
|
|
527
|
+
}),
|
|
528
|
+
db.user.delete({ where: { id: userId } }),
|
|
529
|
+
]);
|
|
530
|
+
|
|
531
|
+
await auditLogger.log({
|
|
532
|
+
timestamp: new Date(),
|
|
533
|
+
eventType: 'gdpr_erasure',
|
|
534
|
+
userId,
|
|
535
|
+
action: 'data_erasure',
|
|
536
|
+
outcome: 'success',
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Data retention enforcement
|
|
541
|
+
async enforceRetention(): Promise<void> {
|
|
542
|
+
const retentionPolicies = [
|
|
543
|
+
{ table: 'activityLog', retentionDays: 90 },
|
|
544
|
+
{ table: 'sessions', retentionDays: 30 },
|
|
545
|
+
{ table: 'auditLog', retentionDays: 365 * 7 }, // 7 years for compliance
|
|
546
|
+
];
|
|
547
|
+
|
|
548
|
+
for (const policy of retentionPolicies) {
|
|
549
|
+
const cutoff = new Date();
|
|
550
|
+
cutoff.setDate(cutoff.getDate() - policy.retentionDays);
|
|
551
|
+
|
|
552
|
+
await db[policy.table].deleteMany({
|
|
553
|
+
where: { createdAt: { lt: cutoff } },
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// PCI-DSS compliance
|
|
560
|
+
const pciCompliance = {
|
|
561
|
+
// Never store CVV
|
|
562
|
+
sanitizePaymentData(data: PaymentData): SanitizedPaymentData {
|
|
563
|
+
return {
|
|
564
|
+
cardLastFour: data.cardNumber.slice(-4),
|
|
565
|
+
cardBrand: detectCardBrand(data.cardNumber),
|
|
566
|
+
expiryMonth: data.expiryMonth,
|
|
567
|
+
expiryYear: data.expiryYear,
|
|
568
|
+
// CVV is NEVER stored
|
|
569
|
+
};
|
|
570
|
+
},
|
|
571
|
+
|
|
572
|
+
// Mask PAN in logs
|
|
573
|
+
maskPAN(pan: string): string {
|
|
574
|
+
return pan.slice(0, 6) + '******' + pan.slice(-4);
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Use Cases
|
|
580
|
+
|
|
581
|
+
### 1. API Security Hardening
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// Complete API security setup
|
|
585
|
+
app.use(helmet());
|
|
586
|
+
app.use(rateLimiter);
|
|
587
|
+
app.use(verifyIdentity);
|
|
588
|
+
app.use(auditMiddleware);
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### 2. Secure File Handling
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
// Secure file upload endpoint
|
|
595
|
+
app.post('/upload', authenticate, async (req, res) => {
|
|
596
|
+
await validateFileUpload(req.file);
|
|
597
|
+
const safePath = sanitizePath(req.body.path, UPLOAD_DIR);
|
|
598
|
+
// ... process file
|
|
599
|
+
});
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
## Best Practices
|
|
603
|
+
|
|
604
|
+
### Do's
|
|
605
|
+
|
|
606
|
+
- **Defense in depth** - Multiple layers of security
|
|
607
|
+
- **Principle of least privilege** - Minimal permissions
|
|
608
|
+
- **Regular security audits** - Penetration testing
|
|
609
|
+
- **Automated vulnerability scanning** - CI/CD integration
|
|
610
|
+
- **Incident response planning** - Document procedures
|
|
611
|
+
- **Security training** - Educate team members
|
|
612
|
+
|
|
613
|
+
### Don'ts
|
|
614
|
+
|
|
615
|
+
- Don't store secrets in code
|
|
616
|
+
- Don't trust client input
|
|
617
|
+
- Don't expose stack traces
|
|
618
|
+
- Don't use deprecated crypto
|
|
619
|
+
- Don't skip security headers
|
|
620
|
+
- Don't ignore security alerts
|
|
621
|
+
|
|
622
|
+
## Related Skills
|
|
623
|
+
|
|
624
|
+
- **owasp** - Security fundamentals
|
|
625
|
+
- **oauth** - Authentication patterns
|
|
626
|
+
- **defense-in-depth** - Layered security
|
|
627
|
+
|
|
628
|
+
## Reference Resources
|
|
629
|
+
|
|
630
|
+
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
|
|
631
|
+
- [HashiCorp Vault](https://www.vaultproject.io/docs)
|
|
632
|
+
- [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
|
|
633
|
+
- [SOC 2 Compliance](https://www.aicpa.org/soc2)
|