@mmerterden/multi-agent-pipeline 20.1.0 → 20.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/docs/facts.json +5 -5
  3. package/manifest.json +52 -31
  4. package/package.json +1 -1
  5. package/pipeline/skills/.skill-manifest.json +36 -20
  6. package/pipeline/skills/.skills-index.json +75 -9
  7. package/pipeline/skills/shared/README.md +13 -7
  8. package/pipeline/skills/shared/external/android-architecture/SKILL.md +71 -0
  9. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +142 -0
  10. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +314 -0
  11. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +432 -0
  12. package/pipeline/skills/shared/external/android-datastore/SKILL.md +236 -0
  13. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +297 -0
  14. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +249 -0
  15. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +270 -0
  16. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +62 -0
  17. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +255 -0
  18. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +257 -0
  19. package/pipeline/skills/shared/external/android-performance/SKILL.md +86 -602
  20. package/pipeline/skills/shared/external/android-performance/references/patterns.md +659 -0
  21. package/pipeline/skills/shared/external/android-security/SKILL.md +117 -430
  22. package/pipeline/skills/shared/external/android-security/references/patterns.md +690 -0
  23. package/pipeline/skills/shared/external/{android_ui_verification → android-ui-verification}/SKILL.md +1 -1
  24. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +35 -733
  25. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +299 -0
  26. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +255 -0
  27. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +167 -0
  28. package/pipeline/skills/shared/external/app-intents/SKILL.md +39 -174
  29. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +178 -0
  30. package/pipeline/skills/shared/external/compose-components/SKILL.md +48 -0
  31. package/pipeline/skills/shared/external/compose-components/references/patterns.md +200 -0
  32. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +66 -3
  33. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +191 -0
  34. package/pipeline/skills/shared/external/compose-testing/SKILL.md +107 -397
  35. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +631 -0
  36. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +121 -449
  37. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +715 -0
  38. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +143 -0
  39. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +27 -102
  40. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +42 -0
  41. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +94 -383
  42. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +640 -0
  43. package/pipeline/skills/shared/external/room-database/SKILL.md +101 -440
  44. package/pipeline/skills/shared/external/room-database/references/patterns.md +614 -0
  45. package/pipeline/skills/shared/external/storekit/SKILL.md +69 -343
  46. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +371 -0
  47. package/pipeline/skills/shared/external/widgetkit/SKILL.md +25 -101
  48. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +107 -0
  49. package/pipeline/skills/skills-index.md +8 -2
@@ -0,0 +1,299 @@
1
+ # Authentication & Authorization Patterns
2
+
3
+ Full implementations for JWT-based authentication, token refresh, route protection, and the authorization pitfalls that most often ship broken.
4
+
5
+ ## JWT Authentication Flow
6
+
7
+ 1. User logs in with credentials
8
+ 2. Server validates credentials
9
+ 3. Server generates JWT token
10
+ 4. Client stores token securely
11
+ 5. Client sends token with each request
12
+ 6. Server validates token
13
+
14
+ ## 1. Generate Secure JWT Tokens
15
+
16
+ ```javascript
17
+ // auth.js
18
+ const jwt = require('jsonwebtoken');
19
+ const bcrypt = require('bcrypt');
20
+
21
+ // Login endpoint
22
+ app.post('/api/auth/login', async (req, res) => {
23
+ try {
24
+ const { email, password } = req.body;
25
+
26
+ // Validate input
27
+ if (!email || !password) {
28
+ return res.status(400).json({
29
+ error: 'Email and password are required'
30
+ });
31
+ }
32
+
33
+ // Find user
34
+ const user = await db.user.findUnique({
35
+ where: { email }
36
+ });
37
+
38
+ if (!user) {
39
+ // Don't reveal if user exists
40
+ return res.status(401).json({
41
+ error: 'Invalid credentials'
42
+ });
43
+ }
44
+
45
+ // Verify password
46
+ const validPassword = await bcrypt.compare(
47
+ password,
48
+ user.passwordHash
49
+ );
50
+
51
+ if (!validPassword) {
52
+ return res.status(401).json({
53
+ error: 'Invalid credentials'
54
+ });
55
+ }
56
+
57
+ // Generate JWT token
58
+ const token = jwt.sign(
59
+ {
60
+ userId: user.id,
61
+ email: user.email,
62
+ role: user.role
63
+ },
64
+ process.env.JWT_SECRET,
65
+ {
66
+ expiresIn: '1h',
67
+ issuer: 'your-app',
68
+ audience: 'your-app-users'
69
+ }
70
+ );
71
+
72
+ // Generate refresh token
73
+ const refreshToken = jwt.sign(
74
+ { userId: user.id },
75
+ process.env.JWT_REFRESH_SECRET,
76
+ { expiresIn: '7d' }
77
+ );
78
+
79
+ // Store refresh token in database
80
+ await db.refreshToken.create({
81
+ data: {
82
+ token: refreshToken,
83
+ userId: user.id,
84
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
85
+ }
86
+ });
87
+
88
+ res.json({
89
+ token,
90
+ refreshToken,
91
+ expiresIn: 3600
92
+ });
93
+
94
+ } catch (error) {
95
+ console.error('Login error:', error);
96
+ res.status(500).json({
97
+ error: 'An error occurred during login'
98
+ });
99
+ }
100
+ });
101
+ ```
102
+
103
+ ## 2. Verify JWT Tokens (Middleware)
104
+
105
+ ```javascript
106
+ // middleware/auth.js
107
+ const jwt = require('jsonwebtoken');
108
+
109
+ function authenticateToken(req, res, next) {
110
+ // Get token from header
111
+ const authHeader = req.headers['authorization'];
112
+ const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
113
+
114
+ if (!token) {
115
+ return res.status(401).json({
116
+ error: 'Access token required'
117
+ });
118
+ }
119
+
120
+ // Verify token
121
+ jwt.verify(
122
+ token,
123
+ process.env.JWT_SECRET,
124
+ {
125
+ issuer: 'your-app',
126
+ audience: 'your-app-users'
127
+ },
128
+ (err, user) => {
129
+ if (err) {
130
+ if (err.name === 'TokenExpiredError') {
131
+ return res.status(401).json({
132
+ error: 'Token expired'
133
+ });
134
+ }
135
+ return res.status(403).json({
136
+ error: 'Invalid token'
137
+ });
138
+ }
139
+
140
+ // Attach user to request
141
+ req.user = user;
142
+ next();
143
+ }
144
+ );
145
+ }
146
+
147
+ module.exports = { authenticateToken };
148
+ ```
149
+
150
+ ## 3. Protect Routes
151
+
152
+ ```javascript
153
+ const { authenticateToken } = require('./middleware/auth');
154
+
155
+ // Protected route
156
+ app.get('/api/user/profile', authenticateToken, async (req, res) => {
157
+ try {
158
+ const user = await db.user.findUnique({
159
+ where: { id: req.user.userId },
160
+ select: {
161
+ id: true,
162
+ email: true,
163
+ name: true,
164
+ // Don't return passwordHash
165
+ }
166
+ });
167
+
168
+ res.json(user);
169
+ } catch (error) {
170
+ res.status(500).json({ error: 'Server error' });
171
+ }
172
+ });
173
+ ```
174
+
175
+ ## 4. Implement Token Refresh
176
+
177
+ ```javascript
178
+ app.post('/api/auth/refresh', async (req, res) => {
179
+ const { refreshToken } = req.body;
180
+
181
+ if (!refreshToken) {
182
+ return res.status(401).json({
183
+ error: 'Refresh token required'
184
+ });
185
+ }
186
+
187
+ try {
188
+ // Verify refresh token
189
+ const decoded = jwt.verify(
190
+ refreshToken,
191
+ process.env.JWT_REFRESH_SECRET
192
+ );
193
+
194
+ // Check if refresh token exists in database
195
+ const storedToken = await db.refreshToken.findFirst({
196
+ where: {
197
+ token: refreshToken,
198
+ userId: decoded.userId,
199
+ expiresAt: { gt: new Date() }
200
+ }
201
+ });
202
+
203
+ if (!storedToken) {
204
+ return res.status(403).json({
205
+ error: 'Invalid refresh token'
206
+ });
207
+ }
208
+
209
+ // Generate new access token
210
+ const user = await db.user.findUnique({
211
+ where: { id: decoded.userId }
212
+ });
213
+
214
+ const newToken = jwt.sign(
215
+ {
216
+ userId: user.id,
217
+ email: user.email,
218
+ role: user.role
219
+ },
220
+ process.env.JWT_SECRET,
221
+ { expiresIn: '1h' }
222
+ );
223
+
224
+ res.json({
225
+ token: newToken,
226
+ expiresIn: 3600
227
+ });
228
+
229
+ } catch (error) {
230
+ res.status(403).json({
231
+ error: 'Invalid refresh token'
232
+ });
233
+ }
234
+ });
235
+ ```
236
+
237
+ ## JWT Security Best Practices
238
+
239
+ - Use strong JWT secrets (256-bit minimum)
240
+ - Set short expiration times (1 hour for access tokens)
241
+ - Implement refresh tokens for long-lived sessions
242
+ - Store refresh tokens in database (can be revoked)
243
+ - Use HTTPS only
244
+ - Don't store sensitive data in JWT payload
245
+ - Validate token issuer and audience
246
+ - Implement token blacklisting for logout
247
+
248
+ ## Common Pitfalls
249
+
250
+ ### JWT Secret Exposed in Code
251
+
252
+ Symptoms: JWT secret hardcoded or committed to Git.
253
+
254
+ ```javascript
255
+ // Bad
256
+ const JWT_SECRET = 'my-secret-key';
257
+
258
+ // Good
259
+ const JWT_SECRET = process.env.JWT_SECRET;
260
+ if (!JWT_SECRET) {
261
+ throw new Error('JWT_SECRET environment variable is required');
262
+ }
263
+
264
+ // Generate strong secret
265
+ // node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
266
+ ```
267
+
268
+ ### Missing Authorization Checks
269
+
270
+ Symptoms: Users can access resources they shouldn't. Authentication proves who the caller is; authorization proves they may act on this resource.
271
+
272
+ ```javascript
273
+ // Bad: Only checks authentication
274
+ app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
275
+ await prisma.post.delete({ where: { id: req.params.id } });
276
+ res.json({ success: true });
277
+ });
278
+
279
+ // Good: Checks both authentication and authorization
280
+ app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
281
+ const post = await prisma.post.findUnique({
282
+ where: { id: req.params.id }
283
+ });
284
+
285
+ if (!post) {
286
+ return res.status(404).json({ error: 'Post not found' });
287
+ }
288
+
289
+ // Check if user owns the post or is admin
290
+ if (post.userId !== req.user.userId && req.user.role !== 'admin') {
291
+ return res.status(403).json({
292
+ error: 'Not authorized to delete this post'
293
+ });
294
+ }
295
+
296
+ await prisma.post.delete({ where: { id: req.params.id } });
297
+ res.json({ success: true });
298
+ });
299
+ ```
@@ -0,0 +1,255 @@
1
+ # Input Validation & Injection Prevention
2
+
3
+ Full implementations for SQL injection prevention, schema validation, output sanitization, plus the password-strength and error-message pitfalls.
4
+
5
+ ## The Problem: SQL Injection
6
+
7
+ ```javascript
8
+ // NEVER DO THIS - SQL Injection vulnerability
9
+ app.get('/api/users/:id', async (req, res) => {
10
+ const userId = req.params.id;
11
+
12
+ // Dangerous: User input directly in query
13
+ const query = `SELECT * FROM users WHERE id = '${userId}'`;
14
+ const user = await db.query(query);
15
+
16
+ res.json(user);
17
+ });
18
+
19
+ // Attack example:
20
+ // GET /api/users/1' OR '1'='1
21
+ // Returns all users!
22
+ ```
23
+
24
+ ## 1. Use Parameterized Queries
25
+
26
+ ```javascript
27
+ // Safe: Parameterized query
28
+ app.get('/api/users/:id', async (req, res) => {
29
+ const userId = req.params.id;
30
+
31
+ // Validate input first
32
+ if (!userId || !/^\d+$/.test(userId)) {
33
+ return res.status(400).json({
34
+ error: 'Invalid user ID'
35
+ });
36
+ }
37
+
38
+ // Use parameterized query
39
+ const user = await db.query(
40
+ 'SELECT id, email, name FROM users WHERE id = $1',
41
+ [userId]
42
+ );
43
+
44
+ if (!user) {
45
+ return res.status(404).json({
46
+ error: 'User not found'
47
+ });
48
+ }
49
+
50
+ res.json(user);
51
+ });
52
+ ```
53
+
54
+ ## 2. Use ORM with Proper Escaping
55
+
56
+ ```javascript
57
+ // Safe: Using Prisma ORM
58
+ app.get('/api/users/:id', async (req, res) => {
59
+ const userId = parseInt(req.params.id);
60
+
61
+ if (isNaN(userId)) {
62
+ return res.status(400).json({
63
+ error: 'Invalid user ID'
64
+ });
65
+ }
66
+
67
+ const user = await prisma.user.findUnique({
68
+ where: { id: userId },
69
+ select: {
70
+ id: true,
71
+ email: true,
72
+ name: true,
73
+ // Don't select sensitive fields
74
+ }
75
+ });
76
+
77
+ if (!user) {
78
+ return res.status(404).json({
79
+ error: 'User not found'
80
+ });
81
+ }
82
+
83
+ res.json(user);
84
+ });
85
+ ```
86
+
87
+ ## 3. Implement Request Validation with Zod
88
+
89
+ ```javascript
90
+ const { z } = require('zod');
91
+
92
+ // Define validation schema
93
+ const createUserSchema = z.object({
94
+ email: z.string().email('Invalid email format'),
95
+ password: z.string()
96
+ .min(8, 'Password must be at least 8 characters')
97
+ .regex(/[A-Z]/, 'Password must contain uppercase letter')
98
+ .regex(/[a-z]/, 'Password must contain lowercase letter')
99
+ .regex(/[0-9]/, 'Password must contain number'),
100
+ name: z.string()
101
+ .min(2, 'Name must be at least 2 characters')
102
+ .max(100, 'Name too long'),
103
+ age: z.number()
104
+ .int('Age must be an integer')
105
+ .min(18, 'Must be 18 or older')
106
+ .max(120, 'Invalid age')
107
+ .optional()
108
+ });
109
+
110
+ // Validation middleware
111
+ function validateRequest(schema) {
112
+ return (req, res, next) => {
113
+ try {
114
+ schema.parse(req.body);
115
+ next();
116
+ } catch (error) {
117
+ res.status(400).json({
118
+ error: 'Validation failed',
119
+ details: error.errors
120
+ });
121
+ }
122
+ };
123
+ }
124
+
125
+ // Use validation
126
+ app.post('/api/users',
127
+ validateRequest(createUserSchema),
128
+ async (req, res) => {
129
+ // Input is validated at this point
130
+ const { email, password, name, age } = req.body;
131
+
132
+ // Hash password
133
+ const passwordHash = await bcrypt.hash(password, 10);
134
+
135
+ // Create user
136
+ const user = await prisma.user.create({
137
+ data: {
138
+ email,
139
+ passwordHash,
140
+ name,
141
+ age
142
+ }
143
+ });
144
+
145
+ // Don't return password hash
146
+ const { passwordHash: _, ...userWithoutPassword } = user;
147
+ res.status(201).json(userWithoutPassword);
148
+ }
149
+ );
150
+ ```
151
+
152
+ ## 4. Sanitize Output to Prevent XSS
153
+
154
+ ```javascript
155
+ const DOMPurify = require('isomorphic-dompurify');
156
+
157
+ app.post('/api/comments', authenticateToken, async (req, res) => {
158
+ const { content } = req.body;
159
+
160
+ // Validate
161
+ if (!content || content.length > 1000) {
162
+ return res.status(400).json({
163
+ error: 'Invalid comment content'
164
+ });
165
+ }
166
+
167
+ // Sanitize HTML to prevent XSS
168
+ const sanitizedContent = DOMPurify.sanitize(content, {
169
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
170
+ ALLOWED_ATTR: ['href']
171
+ });
172
+
173
+ const comment = await prisma.comment.create({
174
+ data: {
175
+ content: sanitizedContent,
176
+ userId: req.user.userId
177
+ }
178
+ });
179
+
180
+ res.status(201).json(comment);
181
+ });
182
+ ```
183
+
184
+ ## Validation Checklist
185
+
186
+ - [ ] Validate all user inputs
187
+ - [ ] Use parameterized queries or ORM
188
+ - [ ] Validate data types (string, number, email, etc.)
189
+ - [ ] Validate data ranges (min/max length, value ranges)
190
+ - [ ] Sanitize HTML content
191
+ - [ ] Escape special characters
192
+ - [ ] Validate file uploads (type, size, content)
193
+ - [ ] Use allowlists, not blocklists
194
+
195
+ ## Common Pitfalls
196
+
197
+ ### Weak Password Requirements
198
+
199
+ Symptoms: Users can set weak passwords like "password123".
200
+
201
+ ```javascript
202
+ const passwordSchema = z.string()
203
+ .min(12, 'Password must be at least 12 characters')
204
+ .regex(/[A-Z]/, 'Must contain uppercase letter')
205
+ .regex(/[a-z]/, 'Must contain lowercase letter')
206
+ .regex(/[0-9]/, 'Must contain number')
207
+ .regex(/[^A-Za-z0-9]/, 'Must contain special character');
208
+
209
+ // Or use a password strength library
210
+ const zxcvbn = require('zxcvbn');
211
+ const result = zxcvbn(password);
212
+ if (result.score < 3) {
213
+ return res.status(400).json({
214
+ error: 'Password too weak',
215
+ suggestions: result.feedback.suggestions
216
+ });
217
+ }
218
+ ```
219
+
220
+ ### Verbose Error Messages
221
+
222
+ Symptoms: Error messages reveal system details. Log the full error server-side, return a generic message to the client.
223
+
224
+ ```javascript
225
+ // Bad: Exposes database details
226
+ app.post('/api/users', async (req, res) => {
227
+ try {
228
+ const user = await prisma.user.create({ data: req.body });
229
+ res.json(user);
230
+ } catch (error) {
231
+ res.status(500).json({ error: error.message });
232
+ // Error: "Unique constraint failed on the fields: (`email`)"
233
+ }
234
+ });
235
+
236
+ // Good: Generic error message
237
+ app.post('/api/users', async (req, res) => {
238
+ try {
239
+ const user = await prisma.user.create({ data: req.body });
240
+ res.json(user);
241
+ } catch (error) {
242
+ console.error('User creation error:', error); // Log full error
243
+
244
+ if (error.code === 'P2002') {
245
+ return res.status(400).json({
246
+ error: 'Email already exists'
247
+ });
248
+ }
249
+
250
+ res.status(500).json({
251
+ error: 'An error occurred while creating user'
252
+ });
253
+ }
254
+ });
255
+ ```
@@ -0,0 +1,167 @@
1
+ # Rate Limiting & DDoS Protection
2
+
3
+ Full implementations for fixed-window and tiered rate limiting, security headers, and rate-limit response headers.
4
+
5
+ ## Why Rate Limiting?
6
+
7
+ - Prevent brute force attacks
8
+ - Protect against DDoS
9
+ - Prevent API abuse
10
+ - Ensure fair usage
11
+ - Reduce server costs
12
+
13
+ ## Implementation with Express Rate Limit
14
+
15
+ ```javascript
16
+ const rateLimit = require('express-rate-limit');
17
+ const RedisStore = require('rate-limit-redis');
18
+ const Redis = require('ioredis');
19
+
20
+ // Create Redis client
21
+ const redis = new Redis({
22
+ host: process.env.REDIS_HOST,
23
+ port: process.env.REDIS_PORT
24
+ });
25
+
26
+ // General API rate limit
27
+ const apiLimiter = rateLimit({
28
+ store: new RedisStore({
29
+ client: redis,
30
+ prefix: 'rl:api:'
31
+ }),
32
+ windowMs: 15 * 60 * 1000, // 15 minutes
33
+ max: 100, // 100 requests per window
34
+ message: {
35
+ error: 'Too many requests, please try again later',
36
+ retryAfter: 900 // seconds
37
+ },
38
+ standardHeaders: true, // Return rate limit info in headers
39
+ legacyHeaders: false,
40
+ // Custom key generator (by user ID or IP)
41
+ keyGenerator: (req) => {
42
+ return req.user?.userId || req.ip;
43
+ }
44
+ });
45
+
46
+ // Strict rate limit for authentication endpoints
47
+ const authLimiter = rateLimit({
48
+ store: new RedisStore({
49
+ client: redis,
50
+ prefix: 'rl:auth:'
51
+ }),
52
+ windowMs: 15 * 60 * 1000, // 15 minutes
53
+ max: 5, // Only 5 login attempts per 15 minutes
54
+ skipSuccessfulRequests: true, // Don't count successful logins
55
+ message: {
56
+ error: 'Too many login attempts, please try again later',
57
+ retryAfter: 900
58
+ }
59
+ });
60
+
61
+ // Apply rate limiters
62
+ app.use('/api/', apiLimiter);
63
+ app.use('/api/auth/login', authLimiter);
64
+ app.use('/api/auth/register', authLimiter);
65
+
66
+ // Custom rate limiter for expensive operations
67
+ const expensiveLimiter = rateLimit({
68
+ windowMs: 60 * 60 * 1000, // 1 hour
69
+ max: 10, // 10 requests per hour
70
+ message: {
71
+ error: 'Rate limit exceeded for this operation'
72
+ }
73
+ });
74
+
75
+ app.post('/api/reports/generate',
76
+ authenticateToken,
77
+ expensiveLimiter,
78
+ async (req, res) => {
79
+ // Expensive operation
80
+ }
81
+ );
82
+ ```
83
+
84
+ ## Advanced: Per-User Rate Limiting
85
+
86
+ ```javascript
87
+ // Different limits based on user tier
88
+ function createTieredRateLimiter() {
89
+ const limits = {
90
+ free: { windowMs: 60 * 60 * 1000, max: 100 },
91
+ pro: { windowMs: 60 * 60 * 1000, max: 1000 },
92
+ enterprise: { windowMs: 60 * 60 * 1000, max: 10000 }
93
+ };
94
+
95
+ return async (req, res, next) => {
96
+ const user = req.user;
97
+ const tier = user?.tier || 'free';
98
+ const limit = limits[tier];
99
+
100
+ const key = `rl:user:${user.userId}`;
101
+ const current = await redis.incr(key);
102
+
103
+ if (current === 1) {
104
+ await redis.expire(key, limit.windowMs / 1000);
105
+ }
106
+
107
+ if (current > limit.max) {
108
+ return res.status(429).json({
109
+ error: 'Rate limit exceeded',
110
+ limit: limit.max,
111
+ remaining: 0,
112
+ reset: await redis.ttl(key)
113
+ });
114
+ }
115
+
116
+ // Set rate limit headers
117
+ res.set({
118
+ 'X-RateLimit-Limit': limit.max,
119
+ 'X-RateLimit-Remaining': limit.max - current,
120
+ 'X-RateLimit-Reset': await redis.ttl(key)
121
+ });
122
+
123
+ next();
124
+ };
125
+ }
126
+
127
+ app.use('/api/', authenticateToken, createTieredRateLimiter());
128
+ ```
129
+
130
+ ## DDoS Protection with Helmet
131
+
132
+ ```javascript
133
+ const helmet = require('helmet');
134
+
135
+ app.use(helmet({
136
+ // Content Security Policy
137
+ contentSecurityPolicy: {
138
+ directives: {
139
+ defaultSrc: ["'self'"],
140
+ styleSrc: ["'self'", "'unsafe-inline'"],
141
+ scriptSrc: ["'self'"],
142
+ imgSrc: ["'self'", 'data:', 'https:']
143
+ }
144
+ },
145
+ // Prevent clickjacking
146
+ frameguard: { action: 'deny' },
147
+ // Hide X-Powered-By header
148
+ hidePoweredBy: true,
149
+ // Prevent MIME type sniffing
150
+ noSniff: true,
151
+ // Enable HSTS
152
+ hsts: {
153
+ maxAge: 31536000,
154
+ includeSubDomains: true,
155
+ preload: true
156
+ }
157
+ }));
158
+ ```
159
+
160
+ ## Rate Limit Response Headers
161
+
162
+ ```
163
+ X-RateLimit-Limit: 100
164
+ X-RateLimit-Remaining: 87
165
+ X-RateLimit-Reset: 1640000000
166
+ Retry-After: 900
167
+ ```