ccjk 14.1.11 → 14.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 (84) hide show
  1. package/dist/chunks/config.mjs +17 -2
  2. package/dist/chunks/doctor.mjs +171 -2
  3. package/dist/chunks/index10.mjs +18 -4
  4. package/dist/chunks/mcp-cli.mjs +1 -1
  5. package/dist/chunks/package.mjs +1 -1
  6. package/dist/cli.mjs +0 -0
  7. package/dist/templates/agents/README.md +78 -0
  8. package/dist/templates/common/error-prevention.md +267 -0
  9. package/dist/templates/common/karpathy-baseline.md +83 -0
  10. package/dist/templates/common/output-styles/zh-CN/carmack-mode.md +381 -0
  11. package/dist/templates/common/output-styles/zh-CN/dhh-mode.md +265 -0
  12. package/dist/templates/common/output-styles/zh-CN/evan-you-mode.md +539 -0
  13. package/dist/templates/common/output-styles/zh-CN/jobs-mode.md +369 -0
  14. package/dist/templates/common/output-styles/zh-CN/linus-mode.md +135 -0
  15. package/dist/templates/common/output-styles/zh-CN/uncle-bob-mode.md +221 -0
  16. package/dist/templates/common/workflow/continuousDelivery/en/continuous-delivery.md +628 -0
  17. package/dist/templates/common/workflow/continuousDelivery/zh-CN/continuous-delivery.md +628 -0
  18. package/dist/templates/common/workflow/essential/en/agents/ccjk-config-agent.md +187 -0
  19. package/dist/templates/common/workflow/essential/en/agents/ccjk-mcp-agent.md +191 -0
  20. package/dist/templates/common/workflow/essential/en/agents/ccjk-skill-agent.md +249 -0
  21. package/dist/templates/common/workflow/essential/en/agents/ccjk-workflow-agent.md +277 -0
  22. package/dist/templates/common/workflow/essential/en/agents/get-current-datetime.md +29 -0
  23. package/dist/templates/common/workflow/essential/en/agents/init-architect.md +115 -0
  24. package/dist/templates/common/workflow/essential/en/agents/ui-ux-designer.md +91 -0
  25. package/dist/templates/common/workflow/essential/en/feat.md +92 -0
  26. package/dist/templates/common/workflow/essential/en/goal.md +147 -0
  27. package/dist/templates/common/workflow/essential/en/init-project.md +53 -0
  28. package/dist/templates/common/workflow/essential/zh-CN/agents/get-current-datetime.md +29 -0
  29. package/dist/templates/common/workflow/essential/zh-CN/agents/init-architect.md +115 -0
  30. package/dist/templates/common/workflow/essential/zh-CN/agents/ui-ux-designer.md +91 -0
  31. package/dist/templates/common/workflow/essential/zh-CN/feat.md +315 -0
  32. package/dist/templates/common/workflow/essential/zh-CN/goal.md +146 -0
  33. package/dist/templates/common/workflow/essential/zh-CN/init-project.md +53 -0
  34. package/dist/templates/common/workflow/git/en/git-cleanBranches.md +102 -0
  35. package/dist/templates/common/workflow/git/en/git-commit.md +205 -0
  36. package/dist/templates/common/workflow/git/en/git-rollback.md +90 -0
  37. package/dist/templates/common/workflow/git/en/git-worktree.md +276 -0
  38. package/dist/templates/common/workflow/git/zh-CN/git-cleanBranches.md +102 -0
  39. package/dist/templates/common/workflow/git/zh-CN/git-commit.md +205 -0
  40. package/dist/templates/common/workflow/git/zh-CN/git-rollback.md +90 -0
  41. package/dist/templates/common/workflow/git/zh-CN/git-worktree.md +276 -0
  42. package/dist/templates/common/workflow/interview/en/interview.md +67 -0
  43. package/dist/templates/common/workflow/interview/zh-CN/interview.md +67 -0
  44. package/dist/templates/common/workflow/linearMethod/en/linear-method.md +651 -0
  45. package/dist/templates/common/workflow/linearMethod/zh-CN/linear-method.md +752 -0
  46. package/dist/templates/common/workflow/refactoringMaster/en/refactoring-master.md +516 -0
  47. package/dist/templates/common/workflow/refactoringMaster/zh-CN/refactoring-master.md +812 -0
  48. package/dist/templates/common/workflow/sixStep/en/workflow.md +83 -0
  49. package/dist/templates/common/workflow/sixStep/zh-CN/workflow.md +359 -0
  50. package/dist/templates/common/workflow/specFirstTDD/en/spec-first-tdd.md +364 -0
  51. package/dist/templates/common/workflow/specFirstTDD/zh-CN/spec-first-tdd.md +366 -0
  52. package/dist/templates/hooks/README.md +212 -0
  53. package/dist/templates/hooks/git-workflow-hooks.md +551 -0
  54. package/dist/templates/hooks/post-test-coverage.md +434 -0
  55. package/dist/templates/hooks/pre-commit-black.md +274 -0
  56. package/dist/templates/hooks/pre-commit-eslint.md +153 -0
  57. package/dist/templates/hooks/pre-commit-gofmt.md +284 -0
  58. package/dist/templates/hooks/pre-commit-prettier.md +212 -0
  59. package/dist/templates/hooks/pre-commit-type-check.md +377 -0
  60. package/dist/templates/skills/ccjk-init.md +154 -0
  61. package/dist/templates/skills/ccjk-mcp-setup.md +205 -0
  62. package/dist/templates/skills/ccjk-troubleshoot.md +228 -0
  63. package/dist/templates/skills/django-patterns.md +1016 -0
  64. package/dist/templates/skills/git-workflow.md +748 -0
  65. package/dist/templates/skills/go-idioms.md +963 -0
  66. package/dist/templates/skills/nextjs-optimization.md +694 -0
  67. package/dist/templates/skills/python-pep8.md +852 -0
  68. package/dist/templates/skills/react-patterns.md +686 -0
  69. package/dist/templates/skills/rust-patterns.md +1057 -0
  70. package/dist/templates/skills/security-best-practices.md +1413 -0
  71. package/dist/templates/skills/testing-best-practices.md +1315 -0
  72. package/dist/templates/skills/ts-best-practices.md +354 -0
  73. package/package.json +40 -43
  74. package/templates/common/karpathy-baseline.md +83 -0
  75. package/templates/common/output-styles/zh-CN/carmack-mode.md +14 -0
  76. package/templates/common/output-styles/zh-CN/dhh-mode.md +14 -0
  77. package/templates/common/output-styles/zh-CN/evan-you-mode.md +14 -0
  78. package/templates/common/output-styles/zh-CN/jobs-mode.md +14 -0
  79. package/templates/common/output-styles/zh-CN/linus-mode.md +14 -0
  80. package/templates/common/output-styles/zh-CN/uncle-bob-mode.md +14 -0
  81. package/templates/common/workflow/linearMethod/zh-CN/linear-method.md +2 -0
  82. package/templates/common/workflow/refactoringMaster/zh-CN/refactoring-master.md +2 -0
  83. package/templates/common/workflow/sixStep/zh-CN/workflow.md +2 -0
  84. package/templates/common/workflow/specFirstTDD/zh-CN/spec-first-tdd.md +2 -0
@@ -0,0 +1,1413 @@
1
+ ---
2
+ name: security-best-practices
3
+ description: Common vulnerabilities, input validation, authentication patterns, and security scanning
4
+ description_zh: 常见漏洞、输入验证、身份验证模式和安全扫描
5
+ version: 1.0.0
6
+ category: security
7
+ triggers: ['/security-best-practices', '/security', '/vulnerabilities', '/auth-security']
8
+ use_when:
9
+ - Implementing secure coding practices
10
+ - Conducting security reviews and audits
11
+ - Setting up authentication and authorization
12
+ - Preventing common security vulnerabilities
13
+ use_when_zh:
14
+ - 实现安全编码实践
15
+ - 进行安全审查和审计
16
+ - 设置身份验证和授权
17
+ - 防止常见安全漏洞
18
+ auto_activate: true
19
+ priority: 9
20
+ agents: [security-expert, backend-architect]
21
+ tags: [security, vulnerabilities, authentication, validation, encryption]
22
+ ---
23
+
24
+ # Security Best Practices | 安全最佳实践
25
+
26
+ ## Context | 上下文
27
+
28
+ Use this skill when implementing secure applications, conducting security reviews, and protecting against common vulnerabilities. Essential for building trustworthy and resilient software systems.
29
+
30
+ 在实现安全应用程序、进行安全审查和防范常见漏洞时使用此技能。对于构建可信赖和有弹性的软件系统至关重要。
31
+
32
+ ## OWASP Top 10 Vulnerabilities | OWASP 十大漏洞
33
+
34
+ ### 1. Injection Attacks | 注入攻击
35
+
36
+ ```javascript
37
+ // ✅ Good: SQL Injection Prevention
38
+ const mysql = require('mysql2/promise');
39
+
40
+ // Use parameterized queries
41
+ async function getUserById(userId) {
42
+ const connection = await mysql.createConnection(dbConfig);
43
+
44
+ // Safe: Parameters are properly escaped
45
+ const [rows] = await connection.execute(
46
+ 'SELECT * FROM users WHERE id = ?',
47
+ [userId]
48
+ );
49
+
50
+ await connection.end();
51
+ return rows[0];
52
+ }
53
+
54
+ async function searchUsers(searchTerm) {
55
+ const connection = await mysql.createConnection(dbConfig);
56
+
57
+ // Safe: Using LIKE with parameterized query
58
+ const [rows] = await connection.execute(
59
+ 'SELECT id, name, email FROM users WHERE name LIKE ? OR email LIKE ?',
60
+ [`%${searchTerm}%`, `%${searchTerm}%`]
61
+ );
62
+
63
+ await connection.end();
64
+ return rows;
65
+ }
66
+
67
+ // ✅ Good: NoSQL Injection Prevention (MongoDB)
68
+ const { MongoClient, ObjectId } = require('mongodb');
69
+
70
+ async function getUserByIdMongo(userId) {
71
+ const client = new MongoClient(mongoUrl);
72
+ await client.connect();
73
+
74
+ const db = client.db('myapp');
75
+ const users = db.collection('users');
76
+
77
+ // Safe: Validate and sanitize input
78
+ if (!ObjectId.isValid(userId)) {
79
+ throw new Error('Invalid user ID format');
80
+ }
81
+
82
+ const user = await users.findOne({ _id: new ObjectId(userId) });
83
+
84
+ await client.close();
85
+ return user;
86
+ }
87
+
88
+ async function searchUsersMongo(searchCriteria) {
89
+ const client = new MongoClient(mongoUrl);
90
+ await client.connect();
91
+
92
+ const db = client.db('myapp');
93
+ const users = db.collection('users');
94
+
95
+ // Safe: Validate search criteria structure
96
+ const allowedFields = ['name', 'email', 'department'];
97
+ const sanitizedCriteria = {};
98
+
99
+ for (const [key, value] of Object.entries(searchCriteria)) {
100
+ if (allowedFields.includes(key) && typeof value === 'string') {
101
+ sanitizedCriteria[key] = { $regex: value, $options: 'i' };
102
+ }
103
+ }
104
+
105
+ const users_result = await users.find(sanitizedCriteria).toArray();
106
+
107
+ await client.close();
108
+ return users_result;
109
+ }
110
+
111
+ // ❌ Bad: Vulnerable to SQL injection
112
+ async function vulnerableGetUser(userId) {
113
+ const connection = await mysql.createConnection(dbConfig);
114
+
115
+ // Dangerous: Direct string concatenation
116
+ const query = `SELECT * FROM users WHERE id = ${userId}`;
117
+ const [rows] = await connection.execute(query);
118
+
119
+ return rows[0];
120
+ }
121
+
122
+ // ❌ Bad: Vulnerable to NoSQL injection
123
+ async function vulnerableSearchMongo(userInput) {
124
+ const client = new MongoClient(mongoUrl);
125
+ await client.connect();
126
+
127
+ const db = client.db('myapp');
128
+ const users = db.collection('users');
129
+
130
+ // Dangerous: Direct use of user input
131
+ const result = await users.find(userInput).toArray();
132
+
133
+ return result;
134
+ }
135
+ ```
136
+
137
+ ### 2. Cross-Site Scripting (XSS) | 跨站脚本攻击
138
+
139
+ ```javascript
140
+ // ✅ Good: XSS Prevention
141
+ const DOMPurify = require('dompurify');
142
+ const { JSDOM } = require('jsdom');
143
+
144
+ const window = new JSDOM('').window;
145
+ const purify = DOMPurify(window);
146
+
147
+ // Input sanitization
148
+ function sanitizeHtml(dirty) {
149
+ return purify.sanitize(dirty, {
150
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
151
+ ALLOWED_ATTR: []
152
+ });
153
+ }
154
+
155
+ // Output encoding for different contexts
156
+ function escapeHtml(unsafe) {
157
+ return unsafe
158
+ .replace(/&/g, "&")
159
+ .replace(/</g, "&lt;")
160
+ .replace(/>/g, "&gt;")
161
+ .replace(/"/g, "&quot;")
162
+ .replace(/'/g, "&#039;");
163
+ }
164
+
165
+ function escapeJavaScript(unsafe) {
166
+ return unsafe
167
+ .replace(/\\/g, "\\\\")
168
+ .replace(/'/g, "\\'")
169
+ .replace(/"/g, '\\"')
170
+ .replace(/\n/g, "\\n")
171
+ .replace(/\r/g, "\\r")
172
+ .replace(/\t/g, "\\t");
173
+ }
174
+
175
+ // React component with proper escaping
176
+ import React from 'react';
177
+
178
+ function UserProfile({ user }) {
179
+ return (
180
+ <div>
181
+ {/* Safe: React automatically escapes */}
182
+ <h1>{user.name}</h1>
183
+ <p>{user.bio}</p>
184
+
185
+ {/* Safe: Sanitized HTML */}
186
+ <div dangerouslySetInnerHTML={{
187
+ __html: sanitizeHtml(user.description)
188
+ }} />
189
+
190
+ {/* Safe: URL validation */}
191
+ {isValidUrl(user.website) && (
192
+ <a href={user.website} target="_blank" rel="noopener noreferrer">
193
+ {user.website}
194
+ </a>
195
+ )}
196
+ </div>
197
+ );
198
+ }
199
+
200
+ function isValidUrl(string) {
201
+ try {
202
+ const url = new URL(string);
203
+ return url.protocol === 'http:' || url.protocol === 'https:';
204
+ } catch (_) {
205
+ return false;
206
+ }
207
+ }
208
+
209
+ // ✅ Good: Content Security Policy
210
+ app.use((req, res, next) => {
211
+ res.setHeader(
212
+ 'Content-Security-Policy',
213
+ "default-src 'self'; " +
214
+ "script-src 'self' 'unsafe-inline' https://trusted-cdn.com; " +
215
+ "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
216
+ "img-src 'self' data: https:; " +
217
+ "font-src 'self' https://fonts.gstatic.com; " +
218
+ "connect-src 'self' https://api.example.com; " +
219
+ "frame-ancestors 'none';"
220
+ );
221
+ next();
222
+ });
223
+
224
+ // ❌ Bad: Vulnerable to XSS
225
+ function vulnerableRender(userInput) {
226
+ // Dangerous: Direct HTML insertion
227
+ document.getElementById('content').innerHTML = userInput;
228
+
229
+ // Dangerous: Unescaped output in template
230
+ return `<div>${userInput}</div>`;
231
+ }
232
+
233
+ // ❌ Bad: Dangerous use of eval
234
+ function vulnerableExecute(userCode) {
235
+ // Extremely dangerous: Never use eval with user input
236
+ eval(userCode);
237
+ }
238
+ ```
239
+
240
+ ### 3. Authentication and Session Management | 身份验证和会话管理
241
+
242
+ ```javascript
243
+ // ✅ Good: Secure Authentication Implementation
244
+ const bcrypt = require('bcrypt');
245
+ const jwt = require('jsonwebtoken');
246
+ const crypto = require('crypto');
247
+ const rateLimit = require('express-rate-limit');
248
+
249
+ // Password hashing
250
+ async function hashPassword(password) {
251
+ const saltRounds = 12; // Adjust based on security requirements
252
+ return await bcrypt.hash(password, saltRounds);
253
+ }
254
+
255
+ async function verifyPassword(password, hashedPassword) {
256
+ return await bcrypt.compare(password, hashedPassword);
257
+ }
258
+
259
+ // Secure password requirements
260
+ function validatePassword(password) {
261
+ const minLength = 8;
262
+ const hasUpperCase = /[A-Z]/.test(password);
263
+ const hasLowerCase = /[a-z]/.test(password);
264
+ const hasNumbers = /\d/.test(password);
265
+ const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
266
+
267
+ const errors = [];
268
+
269
+ if (password.length < minLength) {
270
+ errors.push(`Password must be at least ${minLength} characters long`);
271
+ }
272
+
273
+ if (!hasUpperCase) {
274
+ errors.push('Password must contain at least one uppercase letter');
275
+ }
276
+
277
+ if (!hasLowerCase) {
278
+ errors.push('Password must contain at least one lowercase letter');
279
+ }
280
+
281
+ if (!hasNumbers) {
282
+ errors.push('Password must contain at least one number');
283
+ }
284
+
285
+ if (!hasSpecialChar) {
286
+ errors.push('Password must contain at least one special character');
287
+ }
288
+
289
+ return {
290
+ isValid: errors.length === 0,
291
+ errors
292
+ };
293
+ }
294
+
295
+ // JWT token management
296
+ const JWT_SECRET = process.env.JWT_SECRET || crypto.randomBytes(64).toString('hex');
297
+ const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET || crypto.randomBytes(64).toString('hex');
298
+
299
+ function generateTokens(userId) {
300
+ const accessToken = jwt.sign(
301
+ { userId, type: 'access' },
302
+ JWT_SECRET,
303
+ { expiresIn: '15m' }
304
+ );
305
+
306
+ const refreshToken = jwt.sign(
307
+ { userId, type: 'refresh' },
308
+ JWT_REFRESH_SECRET,
309
+ { expiresIn: '7d' }
310
+ );
311
+
312
+ return { accessToken, refreshToken };
313
+ }
314
+
315
+ function verifyAccessToken(token) {
316
+ try {
317
+ const decoded = jwt.verify(token, JWT_SECRET);
318
+ if (decoded.type !== 'access') {
319
+ throw new Error('Invalid token type');
320
+ }
321
+ return decoded;
322
+ } catch (error) {
323
+ throw new Error('Invalid or expired token');
324
+ }
325
+ }
326
+
327
+ // Rate limiting for authentication
328
+ const loginLimiter = rateLimit({
329
+ windowMs: 15 * 60 * 1000, // 15 minutes
330
+ max: 5, // Limit each IP to 5 requests per windowMs
331
+ message: 'Too many login attempts, please try again later',
332
+ standardHeaders: true,
333
+ legacyHeaders: false,
334
+ });
335
+
336
+ // Secure login endpoint
337
+ app.post('/login', loginLimiter, async (req, res) => {
338
+ try {
339
+ const { email, password } = req.body;
340
+
341
+ // Input validation
342
+ if (!email || !password) {
343
+ return res.status(400).json({ error: 'Email and password are required' });
344
+ }
345
+
346
+ // Find user
347
+ const user = await User.findOne({ email: email.toLowerCase() });
348
+ if (!user) {
349
+ // Don't reveal whether user exists
350
+ return res.status(401).json({ error: 'Invalid credentials' });
351
+ }
352
+
353
+ // Check if account is locked
354
+ if (user.lockUntil && user.lockUntil > Date.now()) {
355
+ return res.status(423).json({ error: 'Account temporarily locked' });
356
+ }
357
+
358
+ // Verify password
359
+ const isValidPassword = await verifyPassword(password, user.passwordHash);
360
+
361
+ if (!isValidPassword) {
362
+ // Increment failed attempts
363
+ await user.incFailedAttempts();
364
+ return res.status(401).json({ error: 'Invalid credentials' });
365
+ }
366
+
367
+ // Reset failed attempts on successful login
368
+ await user.resetFailedAttempts();
369
+
370
+ // Generate tokens
371
+ const { accessToken, refreshToken } = generateTokens(user._id);
372
+
373
+ // Store refresh token securely
374
+ await user.updateRefreshToken(refreshToken);
375
+
376
+ // Set secure cookie
377
+ res.cookie('refreshToken', refreshToken, {
378
+ httpOnly: true,
379
+ secure: process.env.NODE_ENV === 'production',
380
+ sameSite: 'strict',
381
+ maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
382
+ });
383
+
384
+ res.json({
385
+ accessToken,
386
+ user: {
387
+ id: user._id,
388
+ email: user.email,
389
+ name: user.name
390
+ }
391
+ });
392
+
393
+ } catch (error) {
394
+ console.error('Login error:', error);
395
+ res.status(500).json({ error: 'Internal server error' });
396
+ }
397
+ });
398
+
399
+ // Multi-factor authentication
400
+ const speakeasy = require('speakeasy');
401
+ const QRCode = require('qrcode');
402
+
403
+ async function setupTwoFactor(userId) {
404
+ const secret = speakeasy.generateSecret({
405
+ name: `MyApp (${userId})`,
406
+ issuer: 'MyApp'
407
+ });
408
+
409
+ // Store secret temporarily (user must verify before enabling)
410
+ await User.findByIdAndUpdate(userId, {
411
+ tempTwoFactorSecret: secret.base32
412
+ });
413
+
414
+ // Generate QR code
415
+ const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);
416
+
417
+ return {
418
+ secret: secret.base32,
419
+ qrCode: qrCodeUrl
420
+ };
421
+ }
422
+
423
+ function verifyTwoFactor(token, secret) {
424
+ return speakeasy.totp.verify({
425
+ secret,
426
+ encoding: 'base32',
427
+ token,
428
+ window: 2 // Allow some time drift
429
+ });
430
+ }
431
+
432
+ // ❌ Bad: Insecure authentication
433
+ async function insecureLogin(email, password) {
434
+ // Bad: Plain text password storage
435
+ const user = await User.findOne({ email, password });
436
+
437
+ // Bad: Weak JWT secret
438
+ const token = jwt.sign({ userId: user.id }, 'secret123');
439
+
440
+ // Bad: No rate limiting, no input validation
441
+ return { token };
442
+ }
443
+ ```
444
+
445
+ ### 4. Access Control and Authorization | 访问控制和授权
446
+
447
+ ```javascript
448
+ // ✅ Good: Role-Based Access Control (RBAC)
449
+ const permissions = {
450
+ 'user:read': ['admin', 'manager', 'user'],
451
+ 'user:write': ['admin', 'manager'],
452
+ 'user:delete': ['admin'],
453
+ 'report:read': ['admin', 'manager'],
454
+ 'report:write': ['admin', 'manager'],
455
+ 'system:admin': ['admin']
456
+ };
457
+
458
+ function hasPermission(userRole, permission) {
459
+ return permissions[permission]?.includes(userRole) || false;
460
+ }
461
+
462
+ // Middleware for permission checking
463
+ function requirePermission(permission) {
464
+ return (req, res, next) => {
465
+ const user = req.user; // Set by authentication middleware
466
+
467
+ if (!user) {
468
+ return res.status(401).json({ error: 'Authentication required' });
469
+ }
470
+
471
+ if (!hasPermission(user.role, permission)) {
472
+ return res.status(403).json({ error: 'Insufficient permissions' });
473
+ }
474
+
475
+ next();
476
+ };
477
+ }
478
+
479
+ // Resource-based authorization
480
+ async function canAccessResource(userId, resourceId, action) {
481
+ const resource = await Resource.findById(resourceId);
482
+
483
+ if (!resource) {
484
+ return false;
485
+ }
486
+
487
+ // Owner can do anything
488
+ if (resource.ownerId.toString() === userId.toString()) {
489
+ return true;
490
+ }
491
+
492
+ // Check shared permissions
493
+ const permission = await Permission.findOne({
494
+ resourceId,
495
+ userId,
496
+ action: { $in: [action, 'all'] }
497
+ });
498
+
499
+ return !!permission;
500
+ }
501
+
502
+ // API endpoint with proper authorization
503
+ app.get('/api/users/:id',
504
+ authenticateToken,
505
+ requirePermission('user:read'),
506
+ async (req, res) => {
507
+ try {
508
+ const requestedUserId = req.params.id;
509
+ const currentUserId = req.user.id;
510
+
511
+ // Users can only access their own data unless they have admin role
512
+ if (requestedUserId !== currentUserId && req.user.role !== 'admin') {
513
+ return res.status(403).json({ error: 'Access denied' });
514
+ }
515
+
516
+ const user = await User.findById(requestedUserId);
517
+ if (!user) {
518
+ return res.status(404).json({ error: 'User not found' });
519
+ }
520
+
521
+ // Filter sensitive data based on permissions
522
+ const userData = filterUserData(user, req.user.role);
523
+
524
+ res.json(userData);
525
+ } catch (error) {
526
+ console.error('Error fetching user:', error);
527
+ res.status(500).json({ error: 'Internal server error' });
528
+ }
529
+ }
530
+ );
531
+
532
+ function filterUserData(user, viewerRole) {
533
+ const baseData = {
534
+ id: user._id,
535
+ name: user.name,
536
+ email: user.email
537
+ };
538
+
539
+ // Admin can see everything
540
+ if (viewerRole === 'admin') {
541
+ return {
542
+ ...baseData,
543
+ role: user.role,
544
+ createdAt: user.createdAt,
545
+ lastLogin: user.lastLogin,
546
+ isActive: user.isActive
547
+ };
548
+ }
549
+
550
+ // Regular users see limited data
551
+ return baseData;
552
+ }
553
+
554
+ // ✅ Good: Attribute-Based Access Control (ABAC)
555
+ class AccessControlEngine {
556
+ constructor() {
557
+ this.policies = [];
558
+ }
559
+
560
+ addPolicy(policy) {
561
+ this.policies.push(policy);
562
+ }
563
+
564
+ async evaluate(subject, action, resource, context = {}) {
565
+ for (const policy of this.policies) {
566
+ const result = await policy.evaluate(subject, action, resource, context);
567
+ if (result === 'deny') {
568
+ return false;
569
+ }
570
+ if (result === 'allow') {
571
+ return true;
572
+ }
573
+ }
574
+
575
+ // Default deny
576
+ return false;
577
+ }
578
+ }
579
+
580
+ class Policy {
581
+ constructor(name, condition, effect) {
582
+ this.name = name;
583
+ this.condition = condition;
584
+ this.effect = effect; // 'allow' or 'deny'
585
+ }
586
+
587
+ async evaluate(subject, action, resource, context) {
588
+ if (await this.condition(subject, action, resource, context)) {
589
+ return this.effect;
590
+ }
591
+ return 'not_applicable';
592
+ }
593
+ }
594
+
595
+ // Example policies
596
+ const ownerPolicy = new Policy(
597
+ 'owner-access',
598
+ async (subject, action, resource) => {
599
+ return resource.ownerId === subject.id;
600
+ },
601
+ 'allow'
602
+ );
603
+
604
+ const businessHoursPolicy = new Policy(
605
+ 'business-hours-only',
606
+ async (subject, action, resource, context) => {
607
+ const hour = new Date().getHours();
608
+ return hour >= 9 && hour <= 17; // 9 AM to 5 PM
609
+ },
610
+ 'allow'
611
+ );
612
+
613
+ const acEngine = new AccessControlEngine();
614
+ acEngine.addPolicy(ownerPolicy);
615
+ acEngine.addPolicy(businessHoursPolicy);
616
+ ```
617
+
618
+ ## Input Validation and Sanitization | 输入验证和清理
619
+
620
+ ### 1. Comprehensive Input Validation | 全面输入验证
621
+
622
+ ```javascript
623
+ // ✅ Good: Input validation with Joi
624
+ const Joi = require('joi');
625
+
626
+ // User registration schema
627
+ const userRegistrationSchema = Joi.object({
628
+ name: Joi.string()
629
+ .min(2)
630
+ .max(50)
631
+ .pattern(/^[a-zA-Z\s]+$/)
632
+ .required()
633
+ .messages({
634
+ 'string.pattern.base': 'Name can only contain letters and spaces'
635
+ }),
636
+
637
+ email: Joi.string()
638
+ .email({ minDomainSegments: 2 })
639
+ .required()
640
+ .lowercase(),
641
+
642
+ password: Joi.string()
643
+ .min(8)
644
+ .max(128)
645
+ .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/)
646
+ .required()
647
+ .messages({
648
+ 'string.pattern.base': 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character'
649
+ }),
650
+
651
+ age: Joi.number()
652
+ .integer()
653
+ .min(13)
654
+ .max(120)
655
+ .required(),
656
+
657
+ phone: Joi.string()
658
+ .pattern(/^\+?[\d\s\-\(\)]+$/)
659
+ .optional(),
660
+
661
+ website: Joi.string()
662
+ .uri({ scheme: ['http', 'https'] })
663
+ .optional()
664
+ });
665
+
666
+ // Validation middleware
667
+ function validateInput(schema) {
668
+ return (req, res, next) => {
669
+ const { error, value } = schema.validate(req.body, {
670
+ abortEarly: false,
671
+ stripUnknown: true
672
+ });
673
+
674
+ if (error) {
675
+ const errors = error.details.map(detail => ({
676
+ field: detail.path.join('.'),
677
+ message: detail.message
678
+ }));
679
+
680
+ return res.status(400).json({
681
+ error: 'Validation failed',
682
+ details: errors
683
+ });
684
+ }
685
+
686
+ req.validatedData = value;
687
+ next();
688
+ };
689
+ }
690
+
691
+ // File upload validation
692
+ const multer = require('multer');
693
+ const path = require('path');
694
+
695
+ const fileFilter = (req, file, cb) => {
696
+ // Allowed file types
697
+ const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
698
+
699
+ if (allowedTypes.includes(file.mimetype)) {
700
+ cb(null, true);
701
+ } else {
702
+ cb(new Error('Invalid file type. Only JPEG, PNG, GIF, and PDF files are allowed.'), false);
703
+ }
704
+ };
705
+
706
+ const upload = multer({
707
+ dest: 'uploads/',
708
+ limits: {
709
+ fileSize: 5 * 1024 * 1024, // 5MB limit
710
+ files: 5 // Maximum 5 files
711
+ },
712
+ fileFilter
713
+ });
714
+
715
+ // API endpoint with validation
716
+ app.post('/api/users',
717
+ validateInput(userRegistrationSchema),
718
+ async (req, res) => {
719
+ try {
720
+ const userData = req.validatedData;
721
+
722
+ // Additional business logic validation
723
+ const existingUser = await User.findOne({ email: userData.email });
724
+ if (existingUser) {
725
+ return res.status(409).json({ error: 'Email already registered' });
726
+ }
727
+
728
+ // Hash password
729
+ userData.passwordHash = await hashPassword(userData.password);
730
+ delete userData.password;
731
+
732
+ const user = new User(userData);
733
+ await user.save();
734
+
735
+ res.status(201).json({
736
+ message: 'User created successfully',
737
+ user: {
738
+ id: user._id,
739
+ name: user.name,
740
+ email: user.email
741
+ }
742
+ });
743
+
744
+ } catch (error) {
745
+ console.error('User creation error:', error);
746
+ res.status(500).json({ error: 'Internal server error' });
747
+ }
748
+ }
749
+ );
750
+
751
+ // ✅ Good: SQL injection prevention with parameterized queries
752
+ async function searchProducts(filters) {
753
+ const { category, minPrice, maxPrice, searchTerm } = filters;
754
+
755
+ let query = 'SELECT * FROM products WHERE 1=1';
756
+ const params = [];
757
+
758
+ if (category) {
759
+ query += ' AND category = ?';
760
+ params.push(category);
761
+ }
762
+
763
+ if (minPrice !== undefined) {
764
+ query += ' AND price >= ?';
765
+ params.push(minPrice);
766
+ }
767
+
768
+ if (maxPrice !== undefined) {
769
+ query += ' AND price <= ?';
770
+ params.push(maxPrice);
771
+ }
772
+
773
+ if (searchTerm) {
774
+ query += ' AND (name LIKE ? OR description LIKE ?)';
775
+ params.push(`%${searchTerm}%`, `%${searchTerm}%`);
776
+ }
777
+
778
+ const [rows] = await connection.execute(query, params);
779
+ return rows;
780
+ }
781
+
782
+ // ❌ Bad: No input validation
783
+ app.post('/api/users', async (req, res) => {
784
+ // Dangerous: No validation of input data
785
+ const user = new User(req.body);
786
+ await user.save();
787
+ res.json(user);
788
+ });
789
+ ```
790
+
791
+ ## Cryptography and Data Protection | 密码学和数据保护
792
+
793
+ ### 1. Encryption and Hashing | 加密和哈希
794
+
795
+ ```javascript
796
+ // ✅ Good: Proper encryption implementation
797
+ const crypto = require('crypto');
798
+
799
+ class EncryptionService {
800
+ constructor() {
801
+ this.algorithm = 'aes-256-gcm';
802
+ this.keyLength = 32; // 256 bits
803
+ this.ivLength = 16; // 128 bits
804
+ this.tagLength = 16; // 128 bits
805
+ }
806
+
807
+ // Generate a random encryption key
808
+ generateKey() {
809
+ return crypto.randomBytes(this.keyLength);
810
+ }
811
+
812
+ // Encrypt data
813
+ encrypt(plaintext, key) {
814
+ const iv = crypto.randomBytes(this.ivLength);
815
+ const cipher = crypto.createCipher(this.algorithm, key, iv);
816
+
817
+ let encrypted = cipher.update(plaintext, 'utf8', 'hex');
818
+ encrypted += cipher.final('hex');
819
+
820
+ const tag = cipher.getAuthTag();
821
+
822
+ // Return IV + tag + encrypted data
823
+ return {
824
+ iv: iv.toString('hex'),
825
+ tag: tag.toString('hex'),
826
+ encrypted: encrypted
827
+ };
828
+ }
829
+
830
+ // Decrypt data
831
+ decrypt(encryptedData, key) {
832
+ const { iv, tag, encrypted } = encryptedData;
833
+
834
+ const decipher = crypto.createDecipher(
835
+ this.algorithm,
836
+ key,
837
+ Buffer.from(iv, 'hex')
838
+ );
839
+
840
+ decipher.setAuthTag(Buffer.from(tag, 'hex'));
841
+
842
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
843
+ decrypted += decipher.final('utf8');
844
+
845
+ return decrypted;
846
+ }
847
+
848
+ // Hash data with salt
849
+ hashWithSalt(data, salt = null) {
850
+ if (!salt) {
851
+ salt = crypto.randomBytes(32);
852
+ }
853
+
854
+ const hash = crypto.pbkdf2Sync(data, salt, 100000, 64, 'sha512');
855
+
856
+ return {
857
+ hash: hash.toString('hex'),
858
+ salt: salt.toString('hex')
859
+ };
860
+ }
861
+
862
+ // Verify hash
863
+ verifyHash(data, hash, salt) {
864
+ const { hash: computedHash } = this.hashWithSalt(
865
+ data,
866
+ Buffer.from(salt, 'hex')
867
+ );
868
+
869
+ return crypto.timingSafeEqual(
870
+ Buffer.from(hash, 'hex'),
871
+ Buffer.from(computedHash, 'hex')
872
+ );
873
+ }
874
+ }
875
+
876
+ // ✅ Good: Secure random token generation
877
+ function generateSecureToken(length = 32) {
878
+ return crypto.randomBytes(length).toString('hex');
879
+ }
880
+
881
+ function generateApiKey() {
882
+ const prefix = 'ak_';
883
+ const randomPart = crypto.randomBytes(24).toString('base64url');
884
+ return prefix + randomPart;
885
+ }
886
+
887
+ // ✅ Good: Digital signatures
888
+ class SignatureService {
889
+ constructor() {
890
+ this.algorithm = 'sha256';
891
+ }
892
+
893
+ // Generate key pair
894
+ generateKeyPair() {
895
+ return crypto.generateKeyPairSync('rsa', {
896
+ modulusLength: 2048,
897
+ publicKeyEncoding: {
898
+ type: 'spki',
899
+ format: 'pem'
900
+ },
901
+ privateKeyEncoding: {
902
+ type: 'pkcs8',
903
+ format: 'pem'
904
+ }
905
+ });
906
+ }
907
+
908
+ // Sign data
909
+ sign(data, privateKey) {
910
+ const sign = crypto.createSign(this.algorithm);
911
+ sign.update(data);
912
+ return sign.sign(privateKey, 'hex');
913
+ }
914
+
915
+ // Verify signature
916
+ verify(data, signature, publicKey) {
917
+ const verify = crypto.createVerify(this.algorithm);
918
+ verify.update(data);
919
+ return verify.verify(publicKey, signature, 'hex');
920
+ }
921
+ }
922
+
923
+ // ✅ Good: Secure session management
924
+ const session = require('express-session');
925
+ const MongoStore = require('connect-mongo');
926
+
927
+ app.use(session({
928
+ secret: process.env.SESSION_SECRET || crypto.randomBytes(64).toString('hex'),
929
+ name: 'sessionId', // Don't use default name
930
+ resave: false,
931
+ saveUninitialized: false,
932
+ store: MongoStore.create({
933
+ mongoUrl: process.env.MONGODB_URI,
934
+ touchAfter: 24 * 3600 // Lazy session update
935
+ }),
936
+ cookie: {
937
+ secure: process.env.NODE_ENV === 'production', // HTTPS only in production
938
+ httpOnly: true, // Prevent XSS
939
+ maxAge: 1000 * 60 * 60 * 24, // 24 hours
940
+ sameSite: 'strict' // CSRF protection
941
+ }
942
+ }));
943
+
944
+ // ❌ Bad: Weak encryption
945
+ function weakEncryption(data) {
946
+ // Bad: Using deprecated algorithm
947
+ const cipher = crypto.createCipher('des', 'weak-key');
948
+ return cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
949
+ }
950
+
951
+ // ❌ Bad: Predictable tokens
952
+ function weakToken() {
953
+ // Bad: Predictable timestamp-based token
954
+ return Date.now().toString() + Math.random().toString();
955
+ }
956
+ ```
957
+
958
+ ### 2. Secure Communication | 安全通信
959
+
960
+ ```javascript
961
+ // ✅ Good: HTTPS configuration
962
+ const https = require('https');
963
+ const fs = require('fs');
964
+
965
+ // SSL/TLS configuration
966
+ const httpsOptions = {
967
+ key: fs.readFileSync('path/to/private-key.pem'),
968
+ cert: fs.readFileSync('path/to/certificate.pem'),
969
+ // Additional security options
970
+ secureProtocol: 'TLSv1_2_method',
971
+ ciphers: [
972
+ 'ECDHE-RSA-AES128-GCM-SHA256',
973
+ 'ECDHE-RSA-AES256-GCM-SHA384',
974
+ 'ECDHE-RSA-AES128-SHA256',
975
+ 'ECDHE-RSA-AES256-SHA384'
976
+ ].join(':'),
977
+ honorCipherOrder: true
978
+ };
979
+
980
+ const server = https.createServer(httpsOptions, app);
981
+
982
+ // ✅ Good: Security headers middleware
983
+ const helmet = require('helmet');
984
+
985
+ app.use(helmet({
986
+ contentSecurityPolicy: {
987
+ directives: {
988
+ defaultSrc: ["'self'"],
989
+ styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
990
+ fontSrc: ["'self'", "https://fonts.gstatic.com"],
991
+ imgSrc: ["'self'", "data:", "https:"],
992
+ scriptSrc: ["'self'"],
993
+ connectSrc: ["'self'", "https://api.example.com"]
994
+ }
995
+ },
996
+ hsts: {
997
+ maxAge: 31536000,
998
+ includeSubDomains: true,
999
+ preload: true
1000
+ }
1001
+ }));
1002
+
1003
+ // ✅ Good: API rate limiting
1004
+ const rateLimit = require('express-rate-limit');
1005
+
1006
+ const apiLimiter = rateLimit({
1007
+ windowMs: 15 * 60 * 1000, // 15 minutes
1008
+ max: 100, // Limit each IP to 100 requests per windowMs
1009
+ message: 'Too many requests from this IP, please try again later.',
1010
+ standardHeaders: true,
1011
+ legacyHeaders: false,
1012
+ });
1013
+
1014
+ const strictLimiter = rateLimit({
1015
+ windowMs: 15 * 60 * 1000,
1016
+ max: 5, // Stricter limit for sensitive endpoints
1017
+ skipSuccessfulRequests: true
1018
+ });
1019
+
1020
+ app.use('/api/', apiLimiter);
1021
+ app.use('/api/auth/', strictLimiter);
1022
+
1023
+ // ✅ Good: Request validation and sanitization
1024
+ const validator = require('validator');
1025
+
1026
+ function sanitizeInput(req, res, next) {
1027
+ // Sanitize string inputs
1028
+ for (const key in req.body) {
1029
+ if (typeof req.body[key] === 'string') {
1030
+ req.body[key] = validator.escape(req.body[key]);
1031
+ }
1032
+ }
1033
+
1034
+ // Validate and sanitize specific fields
1035
+ if (req.body.email) {
1036
+ req.body.email = validator.normalizeEmail(req.body.email);
1037
+ if (!validator.isEmail(req.body.email)) {
1038
+ return res.status(400).json({ error: 'Invalid email format' });
1039
+ }
1040
+ }
1041
+
1042
+ if (req.body.url) {
1043
+ if (!validator.isURL(req.body.url, { protocols: ['http', 'https'] })) {
1044
+ return res.status(400).json({ error: 'Invalid URL format' });
1045
+ }
1046
+ }
1047
+
1048
+ next();
1049
+ }
1050
+
1051
+ app.use(sanitizeInput);
1052
+ ```
1053
+
1054
+ ## Security Testing and Monitoring | 安全测试和监控
1055
+
1056
+ ### 1. Security Testing | 安全测试
1057
+
1058
+ ```javascript
1059
+ // ✅ Good: Security-focused unit tests
1060
+ describe('Authentication Security', () => {
1061
+ describe('Password validation', () => {
1062
+ it('should reject weak passwords', () => {
1063
+ const weakPasswords = [
1064
+ 'password',
1065
+ '123456',
1066
+ 'qwerty',
1067
+ 'abc123',
1068
+ 'password123'
1069
+ ];
1070
+
1071
+ weakPasswords.forEach(password => {
1072
+ const result = validatePassword(password);
1073
+ expect(result.isValid).toBe(false);
1074
+ });
1075
+ });
1076
+
1077
+ it('should accept strong passwords', () => {
1078
+ const strongPasswords = [
1079
+ 'MyStr0ng!Pass',
1080
+ 'C0mplex@Password123',
1081
+ 'Secure#Pass2023!'
1082
+ ];
1083
+
1084
+ strongPasswords.forEach(password => {
1085
+ const result = validatePassword(password);
1086
+ expect(result.isValid).toBe(true);
1087
+ });
1088
+ });
1089
+ });
1090
+
1091
+ describe('SQL injection prevention', () => {
1092
+ it('should handle malicious input safely', async () => {
1093
+ const maliciousInputs = [
1094
+ "'; DROP TABLE users; --",
1095
+ "1' OR '1'='1",
1096
+ "admin'/*",
1097
+ "1; DELETE FROM users WHERE 1=1; --"
1098
+ ];
1099
+
1100
+ for (const input of maliciousInputs) {
1101
+ const result = await getUserById(input);
1102
+ expect(result).toBeNull(); // Should not return any data
1103
+ }
1104
+ });
1105
+ });
1106
+
1107
+ describe('XSS prevention', () => {
1108
+ it('should sanitize HTML input', () => {
1109
+ const maliciousInputs = [
1110
+ '<script>alert("XSS")</script>',
1111
+ '<img src="x" onerror="alert(1)">',
1112
+ 'javascript:alert("XSS")',
1113
+ '<svg onload="alert(1)">'
1114
+ ];
1115
+
1116
+ maliciousInputs.forEach(input => {
1117
+ const sanitized = sanitizeHtml(input);
1118
+ expect(sanitized).not.toContain('<script>');
1119
+ expect(sanitized).not.toContain('javascript:');
1120
+ expect(sanitized).not.toContain('onerror');
1121
+ expect(sanitized).not.toContain('onload');
1122
+ });
1123
+ });
1124
+ });
1125
+ });
1126
+
1127
+ // ✅ Good: Penetration testing helpers
1128
+ class SecurityTester {
1129
+ constructor(baseUrl) {
1130
+ this.baseUrl = baseUrl;
1131
+ }
1132
+
1133
+ async testSqlInjection(endpoint, params) {
1134
+ const sqlPayloads = [
1135
+ "' OR '1'='1",
1136
+ "'; DROP TABLE users; --",
1137
+ "1' UNION SELECT * FROM users --",
1138
+ "admin'/*"
1139
+ ];
1140
+
1141
+ const results = [];
1142
+
1143
+ for (const payload of sqlPayloads) {
1144
+ try {
1145
+ const testParams = { ...params };
1146
+ // Inject payload into each parameter
1147
+ for (const key in testParams) {
1148
+ testParams[key] = payload;
1149
+
1150
+ const response = await fetch(`${this.baseUrl}${endpoint}`, {
1151
+ method: 'POST',
1152
+ headers: { 'Content-Type': 'application/json' },
1153
+ body: JSON.stringify(testParams)
1154
+ });
1155
+
1156
+ results.push({
1157
+ payload,
1158
+ parameter: key,
1159
+ status: response.status,
1160
+ vulnerable: response.status === 200 && response.headers.get('content-length') > 1000
1161
+ });
1162
+ }
1163
+ } catch (error) {
1164
+ results.push({
1165
+ payload,
1166
+ error: error.message
1167
+ });
1168
+ }
1169
+ }
1170
+
1171
+ return results;
1172
+ }
1173
+
1174
+ async testXss(endpoint, params) {
1175
+ const xssPayloads = [
1176
+ '<script>alert("XSS")</script>',
1177
+ '<img src="x" onerror="alert(1)">',
1178
+ 'javascript:alert("XSS")',
1179
+ '<svg onload="alert(1)">'
1180
+ ];
1181
+
1182
+ const results = [];
1183
+
1184
+ for (const payload of xssPayloads) {
1185
+ const testParams = { ...params };
1186
+
1187
+ for (const key in testParams) {
1188
+ testParams[key] = payload;
1189
+
1190
+ const response = await fetch(`${this.baseUrl}${endpoint}`, {
1191
+ method: 'POST',
1192
+ headers: { 'Content-Type': 'application/json' },
1193
+ body: JSON.stringify(testParams)
1194
+ });
1195
+
1196
+ const responseText = await response.text();
1197
+
1198
+ results.push({
1199
+ payload,
1200
+ parameter: key,
1201
+ vulnerable: responseText.includes(payload) && !responseText.includes('&lt;script&gt;')
1202
+ });
1203
+ }
1204
+ }
1205
+
1206
+ return results;
1207
+ }
1208
+ }
1209
+ ```
1210
+
1211
+ ### 2. Security Monitoring | 安全监控
1212
+
1213
+ ```javascript
1214
+ // ✅ Good: Security event logging
1215
+ const winston = require('winston');
1216
+
1217
+ const securityLogger = winston.createLogger({
1218
+ level: 'info',
1219
+ format: winston.format.combine(
1220
+ winston.format.timestamp(),
1221
+ winston.format.errors({ stack: true }),
1222
+ winston.format.json()
1223
+ ),
1224
+ transports: [
1225
+ new winston.transports.File({ filename: 'logs/security.log' }),
1226
+ new winston.transports.Console()
1227
+ ]
1228
+ });
1229
+
1230
+ // Security event types
1231
+ const SecurityEvents = {
1232
+ LOGIN_SUCCESS: 'login_success',
1233
+ LOGIN_FAILURE: 'login_failure',
1234
+ LOGIN_BLOCKED: 'login_blocked',
1235
+ PASSWORD_CHANGE: 'password_change',
1236
+ PERMISSION_DENIED: 'permission_denied',
1237
+ SUSPICIOUS_ACTIVITY: 'suspicious_activity',
1238
+ DATA_ACCESS: 'data_access',
1239
+ ADMIN_ACTION: 'admin_action'
1240
+ };
1241
+
1242
+ function logSecurityEvent(eventType, details) {
1243
+ securityLogger.info({
1244
+ event: eventType,
1245
+ timestamp: new Date().toISOString(),
1246
+ ...details
1247
+ });
1248
+ }
1249
+
1250
+ // Middleware for security logging
1251
+ function securityLoggingMiddleware(req, res, next) {
1252
+ const originalSend = res.send;
1253
+
1254
+ res.send = function(data) {
1255
+ // Log failed authentication attempts
1256
+ if (res.statusCode === 401) {
1257
+ logSecurityEvent(SecurityEvents.LOGIN_FAILURE, {
1258
+ ip: req.ip,
1259
+ userAgent: req.get('User-Agent'),
1260
+ endpoint: req.path,
1261
+ method: req.method
1262
+ });
1263
+ }
1264
+
1265
+ // Log permission denied
1266
+ if (res.statusCode === 403) {
1267
+ logSecurityEvent(SecurityEvents.PERMISSION_DENIED, {
1268
+ ip: req.ip,
1269
+ userId: req.user?.id,
1270
+ endpoint: req.path,
1271
+ method: req.method
1272
+ });
1273
+ }
1274
+
1275
+ originalSend.call(this, data);
1276
+ };
1277
+
1278
+ next();
1279
+ }
1280
+
1281
+ // ✅ Good: Intrusion detection
1282
+ class IntrusionDetector {
1283
+ constructor() {
1284
+ this.suspiciousPatterns = [
1285
+ /(\bor\b|\band\b).*=.*=/i, // SQL injection patterns
1286
+ /<script[^>]*>.*?<\/script>/i, // XSS patterns
1287
+ /javascript:/i,
1288
+ /vbscript:/i,
1289
+ /onload|onerror|onclick/i,
1290
+ /\.\.\//g, // Path traversal
1291
+ /\/etc\/passwd/i,
1292
+ /cmd\.exe|powershell/i
1293
+ ];
1294
+
1295
+ this.rateLimits = new Map();
1296
+ }
1297
+
1298
+ analyzeRequest(req) {
1299
+ const threats = [];
1300
+
1301
+ // Check for suspicious patterns in all input
1302
+ const inputs = [
1303
+ ...Object.values(req.query || {}),
1304
+ ...Object.values(req.body || {}),
1305
+ req.get('User-Agent') || '',
1306
+ req.get('Referer') || ''
1307
+ ];
1308
+
1309
+ for (const input of inputs) {
1310
+ if (typeof input === 'string') {
1311
+ for (const pattern of this.suspiciousPatterns) {
1312
+ if (pattern.test(input)) {
1313
+ threats.push({
1314
+ type: 'suspicious_pattern',
1315
+ pattern: pattern.toString(),
1316
+ input: input.substring(0, 100) // Truncate for logging
1317
+ });
1318
+ }
1319
+ }
1320
+ }
1321
+ }
1322
+
1323
+ // Rate limiting check
1324
+ const clientId = req.ip;
1325
+ const now = Date.now();
1326
+ const windowMs = 60000; // 1 minute
1327
+ const maxRequests = 100;
1328
+
1329
+ if (!this.rateLimits.has(clientId)) {
1330
+ this.rateLimits.set(clientId, []);
1331
+ }
1332
+
1333
+ const requests = this.rateLimits.get(clientId);
1334
+ const recentRequests = requests.filter(time => now - time < windowMs);
1335
+
1336
+ if (recentRequests.length >= maxRequests) {
1337
+ threats.push({
1338
+ type: 'rate_limit_exceeded',
1339
+ requestCount: recentRequests.length,
1340
+ windowMs
1341
+ });
1342
+ }
1343
+
1344
+ recentRequests.push(now);
1345
+ this.rateLimits.set(clientId, recentRequests);
1346
+
1347
+ return threats;
1348
+ }
1349
+ }
1350
+
1351
+ const intrusionDetector = new IntrusionDetector();
1352
+
1353
+ app.use((req, res, next) => {
1354
+ const threats = intrusionDetector.analyzeRequest(req);
1355
+
1356
+ if (threats.length > 0) {
1357
+ logSecurityEvent(SecurityEvents.SUSPICIOUS_ACTIVITY, {
1358
+ ip: req.ip,
1359
+ userAgent: req.get('User-Agent'),
1360
+ endpoint: req.path,
1361
+ method: req.method,
1362
+ threats
1363
+ });
1364
+
1365
+ // Block request if high-risk threats detected
1366
+ const highRiskThreats = threats.filter(t =>
1367
+ t.type === 'suspicious_pattern' || t.type === 'rate_limit_exceeded'
1368
+ );
1369
+
1370
+ if (highRiskThreats.length > 0) {
1371
+ return res.status(429).json({ error: 'Request blocked due to suspicious activity' });
1372
+ }
1373
+ }
1374
+
1375
+ next();
1376
+ });
1377
+ ```
1378
+
1379
+ ## Security Checklist | 安全检查清单
1380
+
1381
+ - [ ] Input validation is implemented for all user inputs
1382
+ - [ ] SQL injection prevention with parameterized queries
1383
+ - [ ] XSS prevention with proper output encoding
1384
+ - [ ] Authentication uses strong password requirements
1385
+ - [ ] Multi-factor authentication is available for sensitive accounts
1386
+ - [ ] Authorization checks are implemented at all levels
1387
+ - [ ] Sensitive data is encrypted at rest and in transit
1388
+ - [ ] Security headers are properly configured
1389
+ - [ ] Rate limiting is implemented for API endpoints
1390
+ - [ ] Security logging and monitoring are in place
1391
+ - [ ] Regular security testing and code reviews
1392
+ - [ ] Dependencies are regularly updated and scanned for vulnerabilities
1393
+ - [ ] Error messages don't reveal sensitive information
1394
+ - [ ] Session management is secure
1395
+ - [ ] File uploads are properly validated and sandboxed
1396
+
1397
+ ## 安全检查清单
1398
+
1399
+ - [ ] 对所有用户输入实施输入验证
1400
+ - [ ] 使用参数化查询防止 SQL 注入
1401
+ - [ ] 通过适当的输出编码防止 XSS
1402
+ - [ ] 身份验证使用强密码要求
1403
+ - [ ] 敏感账户可使用多因素身份验证
1404
+ - [ ] 在所有级别实施授权检查
1405
+ - [ ] 敏感数据在静态和传输中加密
1406
+ - [ ] 正确配置安全标头
1407
+ - [ ] 为 API 端点实施速率限制
1408
+ - [ ] 建立安全日志记录和监控
1409
+ - [ ] 定期进行安全测试和代码审查
1410
+ - [ ] 定期更新依赖项并扫描漏洞
1411
+ - [ ] 错误消息不泄露敏感信息
1412
+ - [ ] 会话管理安全
1413
+ - [ ] 文件上传经过适当验证和沙箱化