developer-ai 1.0.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.
- package/README.md +241 -0
- package/bin/developer-ai.js +2 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +219 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +82 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +115 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +29 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/agent.d.ts +38 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +155 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/system-prompt.d.ts +6 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +44 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/types.d.ts +42 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +6 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +13 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +202 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/providers/ollama.d.ts +13 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +60 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +9 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +40 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/skills/loader.d.ts +25 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +93 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/tests/tools.test.d.ts +2 -0
- package/dist/tests/tools.test.d.ts.map +1 -0
- package/dist/tests/tools.test.js +170 -0
- package/dist/tests/tools.test.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +19 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-files.d.ts +3 -0
- package/dist/tools/list-files.d.ts.map +1 -0
- package/dist/tools/list-files.js +60 -0
- package/dist/tools/list-files.js.map +1 -0
- package/dist/tools/read-file.d.ts +3 -0
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +46 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/registry.d.ts +24 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +37 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/run-command.d.ts +3 -0
- package/dist/tools/run-command.d.ts.map +1 -0
- package/dist/tools/run-command.js +114 -0
- package/dist/tools/run-command.js.map +1 -0
- package/dist/tools/search-text.d.ts +3 -0
- package/dist/tools/search-text.d.ts.map +1 -0
- package/dist/tools/search-text.js +103 -0
- package/dist/tools/search-text.js.map +1 -0
- package/dist/tools/utils.d.ts +6 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +14 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/web-search.d.ts +3 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +80 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write-file.d.ts +3 -0
- package/dist/tools/write-file.d.ts.map +1 -0
- package/dist/tools/write-file.js +66 -0
- package/dist/tools/write-file.js.map +1 -0
- package/package.json +54 -0
- package/skills/accessibility/SKILL.md +496 -0
- package/skills/api-design/SKILL.md +419 -0
- package/skills/code-review/SKILL.md +267 -0
- package/skills/debugging/SKILL.md +332 -0
- package/skills/documentation/SKILL.md +496 -0
- package/skills/error-handling/SKILL.md +504 -0
- package/skills/git-workflow/SKILL.md +448 -0
- package/skills/human-like-coding/SKILL.md +400 -0
- package/skills/performance-optimization/SKILL.md +412 -0
- package/skills/prompt-engineering/SKILL.md +362 -0
- package/skills/refactoring/SKILL.md +457 -0
- package/skills/security-audit/SKILL.md +453 -0
- package/skills/testing-strategy/SKILL.md +501 -0
- package/skills/webapp-testing/SKILL.md +309 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-audit
|
|
3
|
+
description: Guide for identifying and fixing security vulnerabilities. Use when reviewing code for security issues, setting up authentication, handling sensitive data, or hardening applications. Covers OWASP Top 10, input validation, authentication, and secure coding practices.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Audit Skill
|
|
7
|
+
|
|
8
|
+
This skill provides guidance for identifying and fixing security vulnerabilities in web applications.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Security must be built into every layer of an application. This skill covers common vulnerabilities, secure coding practices, and security testing approaches.
|
|
13
|
+
|
|
14
|
+
## OWASP Top 10
|
|
15
|
+
|
|
16
|
+
### 1. Injection
|
|
17
|
+
|
|
18
|
+
**SQL Injection**
|
|
19
|
+
```javascript
|
|
20
|
+
// VULNERABLE
|
|
21
|
+
const query = `SELECT * FROM users WHERE id = ${userId}`;
|
|
22
|
+
|
|
23
|
+
// SECURE - Parameterized query
|
|
24
|
+
const query = 'SELECT * FROM users WHERE id = $1';
|
|
25
|
+
const result = await db.query(query, [userId]);
|
|
26
|
+
|
|
27
|
+
// SECURE - ORM
|
|
28
|
+
const user = await User.findUnique({ where: { id: userId } });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Command Injection**
|
|
32
|
+
```javascript
|
|
33
|
+
// VULNERABLE
|
|
34
|
+
exec(`convert ${userInput}.png output.jpg`);
|
|
35
|
+
|
|
36
|
+
// SECURE - Validate and sanitize
|
|
37
|
+
const safeFilename = path.basename(userInput).replace(/[^a-zA-Z0-9]/g, '');
|
|
38
|
+
exec(`convert ${safeFilename}.png output.jpg`);
|
|
39
|
+
|
|
40
|
+
// SECURE - Use library instead of shell
|
|
41
|
+
const sharp = require('sharp');
|
|
42
|
+
await sharp(`${safeFilename}.png`).toFile('output.jpg');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Broken Authentication
|
|
46
|
+
|
|
47
|
+
**Password Storage**
|
|
48
|
+
```javascript
|
|
49
|
+
// VULNERABLE - Plain text or weak hash
|
|
50
|
+
const hash = md5(password);
|
|
51
|
+
|
|
52
|
+
// SECURE - bcrypt with proper cost factor
|
|
53
|
+
const bcrypt = require('bcrypt');
|
|
54
|
+
const hash = await bcrypt.hash(password, 12);
|
|
55
|
+
const valid = await bcrypt.compare(password, hash);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Session Management**
|
|
59
|
+
```javascript
|
|
60
|
+
// SECURE session configuration
|
|
61
|
+
app.use(session({
|
|
62
|
+
secret: process.env.SESSION_SECRET,
|
|
63
|
+
name: '__session', // Don't use default name
|
|
64
|
+
cookie: {
|
|
65
|
+
httpOnly: true,
|
|
66
|
+
secure: true, // HTTPS only
|
|
67
|
+
sameSite: 'strict',
|
|
68
|
+
maxAge: 15 * 60 * 1000, // 15 minutes
|
|
69
|
+
},
|
|
70
|
+
resave: false,
|
|
71
|
+
saveUninitialized: false,
|
|
72
|
+
}));
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Sensitive Data Exposure
|
|
76
|
+
|
|
77
|
+
**Environment Variables**
|
|
78
|
+
```javascript
|
|
79
|
+
// NEVER commit secrets
|
|
80
|
+
// Use .env file (in .gitignore)
|
|
81
|
+
const apiKey = process.env.API_KEY;
|
|
82
|
+
|
|
83
|
+
// Validate required env vars at startup
|
|
84
|
+
const required = ['DATABASE_URL', 'JWT_SECRET', 'API_KEY'];
|
|
85
|
+
for (const key of required) {
|
|
86
|
+
if (!process.env[key]) {
|
|
87
|
+
throw new Error(`Missing required env var: ${key}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Data Encryption**
|
|
93
|
+
```javascript
|
|
94
|
+
const crypto = require('crypto');
|
|
95
|
+
|
|
96
|
+
// Encrypt sensitive data
|
|
97
|
+
function encrypt(text, key) {
|
|
98
|
+
const iv = crypto.randomBytes(16);
|
|
99
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
100
|
+
const encrypted = Buffer.concat([
|
|
101
|
+
cipher.update(text, 'utf8'),
|
|
102
|
+
cipher.final()
|
|
103
|
+
]);
|
|
104
|
+
const tag = cipher.getAuthTag();
|
|
105
|
+
return { iv, encrypted, tag };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Decrypt
|
|
109
|
+
function decrypt({ iv, encrypted, tag }, key) {
|
|
110
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
111
|
+
decipher.setAuthTag(tag);
|
|
112
|
+
return decipher.update(encrypted) + decipher.final('utf8');
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 4. XML External Entities (XXE)
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
// SECURE - Disable external entities
|
|
120
|
+
const parser = new DOMParser();
|
|
121
|
+
// Use JSON instead of XML when possible
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 5. Broken Access Control
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// VULNERABLE - No authorization check
|
|
128
|
+
app.get('/api/users/:id', async (req, res) => {
|
|
129
|
+
const user = await User.findById(req.params.id);
|
|
130
|
+
res.json(user);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// SECURE - Check authorization
|
|
134
|
+
app.get('/api/users/:id', authenticate, async (req, res) => {
|
|
135
|
+
// Users can only access their own data
|
|
136
|
+
if (req.user.id !== req.params.id && req.user.role !== 'admin') {
|
|
137
|
+
return res.status(403).json({ error: 'Forbidden' });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const user = await User.findById(req.params.id);
|
|
141
|
+
res.json(user);
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 6. Security Misconfiguration
|
|
146
|
+
|
|
147
|
+
**HTTP Headers**
|
|
148
|
+
```javascript
|
|
149
|
+
const helmet = require('helmet');
|
|
150
|
+
|
|
151
|
+
app.use(helmet({
|
|
152
|
+
contentSecurityPolicy: {
|
|
153
|
+
directives: {
|
|
154
|
+
defaultSrc: ["'self'"],
|
|
155
|
+
scriptSrc: ["'self'"],
|
|
156
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
157
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
hsts: {
|
|
161
|
+
maxAge: 31536000,
|
|
162
|
+
includeSubDomains: true,
|
|
163
|
+
preload: true,
|
|
164
|
+
},
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
// Additional headers
|
|
168
|
+
app.use((req, res, next) => {
|
|
169
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
170
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
171
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
172
|
+
next();
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 7. Cross-Site Scripting (XSS)
|
|
177
|
+
|
|
178
|
+
**Output Encoding**
|
|
179
|
+
```javascript
|
|
180
|
+
// VULNERABLE
|
|
181
|
+
element.innerHTML = userInput;
|
|
182
|
+
|
|
183
|
+
// SECURE - Use textContent for text
|
|
184
|
+
element.textContent = userInput;
|
|
185
|
+
|
|
186
|
+
// SECURE - Sanitize HTML if needed
|
|
187
|
+
const DOMPurify = require('dompurify');
|
|
188
|
+
element.innerHTML = DOMPurify.sanitize(userInput);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**React (Auto-escapes by default)**
|
|
192
|
+
```jsx
|
|
193
|
+
// SAFE - React escapes this
|
|
194
|
+
return <div>{userInput}</div>;
|
|
195
|
+
|
|
196
|
+
// DANGEROUS - Explicitly rendering HTML
|
|
197
|
+
return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
|
|
198
|
+
|
|
199
|
+
// If you must render HTML, sanitize it
|
|
200
|
+
const sanitized = DOMPurify.sanitize(userInput);
|
|
201
|
+
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 8. Insecure Deserialization
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
// VULNERABLE - Deserializing untrusted data
|
|
208
|
+
const data = JSON.parse(userInput);
|
|
209
|
+
Object.assign(user, data); // Prototype pollution possible
|
|
210
|
+
|
|
211
|
+
// SECURE - Validate and whitelist fields
|
|
212
|
+
const allowedFields = ['name', 'email'];
|
|
213
|
+
const data = JSON.parse(userInput);
|
|
214
|
+
const sanitized = {};
|
|
215
|
+
for (const field of allowedFields) {
|
|
216
|
+
if (data[field] !== undefined) {
|
|
217
|
+
sanitized[field] = data[field];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 9. Using Components with Known Vulnerabilities
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Check for vulnerabilities
|
|
226
|
+
npm audit
|
|
227
|
+
npm audit fix
|
|
228
|
+
|
|
229
|
+
# Use Snyk for deeper analysis
|
|
230
|
+
npx snyk test
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### 10. Insufficient Logging & Monitoring
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
const winston = require('winston');
|
|
237
|
+
|
|
238
|
+
const logger = winston.createLogger({
|
|
239
|
+
level: 'info',
|
|
240
|
+
format: winston.format.json(),
|
|
241
|
+
transports: [
|
|
242
|
+
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
|
243
|
+
new winston.transports.File({ filename: 'combined.log' }),
|
|
244
|
+
],
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Log security events
|
|
248
|
+
function logSecurityEvent(event, details) {
|
|
249
|
+
logger.warn({
|
|
250
|
+
type: 'SECURITY',
|
|
251
|
+
event,
|
|
252
|
+
...details,
|
|
253
|
+
timestamp: new Date().toISOString(),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Example: Failed login attempt
|
|
258
|
+
logSecurityEvent('FAILED_LOGIN', {
|
|
259
|
+
email: req.body.email,
|
|
260
|
+
ip: req.ip,
|
|
261
|
+
userAgent: req.get('User-Agent'),
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Input Validation
|
|
266
|
+
|
|
267
|
+
### Server-Side Validation
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
const Joi = require('joi');
|
|
271
|
+
|
|
272
|
+
const userSchema = Joi.object({
|
|
273
|
+
name: Joi.string().min(2).max(100).required(),
|
|
274
|
+
email: Joi.string().email().required(),
|
|
275
|
+
password: Joi.string()
|
|
276
|
+
.min(8)
|
|
277
|
+
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/)
|
|
278
|
+
.required(),
|
|
279
|
+
age: Joi.number().integer().min(13).max(120),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
app.post('/users', async (req, res) => {
|
|
283
|
+
const { error, value } = userSchema.validate(req.body);
|
|
284
|
+
|
|
285
|
+
if (error) {
|
|
286
|
+
return res.status(400).json({
|
|
287
|
+
error: 'Validation failed',
|
|
288
|
+
details: error.details,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Proceed with sanitized value
|
|
293
|
+
const user = await User.create(value);
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### File Upload Validation
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
const multer = require('multer');
|
|
301
|
+
const path = require('path');
|
|
302
|
+
|
|
303
|
+
const upload = multer({
|
|
304
|
+
limits: {
|
|
305
|
+
fileSize: 5 * 1024 * 1024, // 5MB
|
|
306
|
+
},
|
|
307
|
+
fileFilter: (req, file, cb) => {
|
|
308
|
+
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
|
|
309
|
+
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif'];
|
|
310
|
+
|
|
311
|
+
const ext = path.extname(file.originalname).toLowerCase();
|
|
312
|
+
|
|
313
|
+
if (!allowedTypes.includes(file.mimetype)) {
|
|
314
|
+
return cb(new Error('Invalid file type'));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!allowedExtensions.includes(ext)) {
|
|
318
|
+
return cb(new Error('Invalid file extension'));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
cb(null, true);
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Authentication Best Practices
|
|
327
|
+
|
|
328
|
+
### JWT Implementation
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
const jwt = require('jsonwebtoken');
|
|
332
|
+
|
|
333
|
+
// Generate tokens
|
|
334
|
+
function generateTokens(user) {
|
|
335
|
+
const accessToken = jwt.sign(
|
|
336
|
+
{ userId: user.id, role: user.role },
|
|
337
|
+
process.env.JWT_SECRET,
|
|
338
|
+
{ expiresIn: '15m' }
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const refreshToken = jwt.sign(
|
|
342
|
+
{ userId: user.id },
|
|
343
|
+
process.env.REFRESH_SECRET,
|
|
344
|
+
{ expiresIn: '7d' }
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
return { accessToken, refreshToken };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Verify middleware
|
|
351
|
+
function authenticate(req, res, next) {
|
|
352
|
+
const authHeader = req.headers.authorization;
|
|
353
|
+
|
|
354
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
355
|
+
return res.status(401).json({ error: 'Missing token' });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const token = authHeader.substring(7);
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
|
362
|
+
req.user = payload;
|
|
363
|
+
next();
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return res.status(401).json({ error: 'Invalid token' });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Rate Limiting
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
const rateLimit = require('express-rate-limit');
|
|
374
|
+
|
|
375
|
+
// General rate limit
|
|
376
|
+
const generalLimiter = rateLimit({
|
|
377
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
378
|
+
max: 100,
|
|
379
|
+
message: { error: 'Too many requests' },
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Strict limit for auth endpoints
|
|
383
|
+
const authLimiter = rateLimit({
|
|
384
|
+
windowMs: 15 * 60 * 1000,
|
|
385
|
+
max: 5,
|
|
386
|
+
message: { error: 'Too many login attempts' },
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
app.use('/api/', generalLimiter);
|
|
390
|
+
app.use('/api/auth/', authLimiter);
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## CSRF Protection
|
|
394
|
+
|
|
395
|
+
```javascript
|
|
396
|
+
const csrf = require('csurf');
|
|
397
|
+
|
|
398
|
+
// For traditional forms
|
|
399
|
+
app.use(csrf({ cookie: true }));
|
|
400
|
+
|
|
401
|
+
app.get('/form', (req, res) => {
|
|
402
|
+
res.render('form', { csrfToken: req.csrfToken() });
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// For APIs - use SameSite cookies and check Origin header
|
|
406
|
+
app.use((req, res, next) => {
|
|
407
|
+
const origin = req.get('Origin');
|
|
408
|
+
const allowedOrigins = ['https://example.com'];
|
|
409
|
+
|
|
410
|
+
if (origin && !allowedOrigins.includes(origin)) {
|
|
411
|
+
return res.status(403).json({ error: 'Invalid origin' });
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
next();
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Security Audit Checklist
|
|
419
|
+
|
|
420
|
+
### Authentication & Authorization
|
|
421
|
+
- [ ] Passwords hashed with bcrypt (cost 12+)
|
|
422
|
+
- [ ] JWT tokens expire quickly (15 min)
|
|
423
|
+
- [ ] Refresh token rotation implemented
|
|
424
|
+
- [ ] Rate limiting on auth endpoints
|
|
425
|
+
- [ ] Account lockout after failed attempts
|
|
426
|
+
- [ ] Authorization checks on all protected routes
|
|
427
|
+
|
|
428
|
+
### Data Protection
|
|
429
|
+
- [ ] HTTPS enforced everywhere
|
|
430
|
+
- [ ] Sensitive data encrypted at rest
|
|
431
|
+
- [ ] Secrets in environment variables
|
|
432
|
+
- [ ] No secrets in logs or error messages
|
|
433
|
+
- [ ] PII handling compliant with regulations
|
|
434
|
+
|
|
435
|
+
### Input/Output
|
|
436
|
+
- [ ] All input validated server-side
|
|
437
|
+
- [ ] SQL injection prevented (parameterized queries)
|
|
438
|
+
- [ ] XSS prevented (output encoding)
|
|
439
|
+
- [ ] File uploads validated and sanitized
|
|
440
|
+
- [ ] CSRF protection enabled
|
|
441
|
+
|
|
442
|
+
### Infrastructure
|
|
443
|
+
- [ ] Security headers configured
|
|
444
|
+
- [ ] Dependencies updated and scanned
|
|
445
|
+
- [ ] Error messages don't leak info
|
|
446
|
+
- [ ] Security events logged
|
|
447
|
+
- [ ] Backups encrypted
|
|
448
|
+
|
|
449
|
+
### Monitoring
|
|
450
|
+
- [ ] Failed login attempts logged
|
|
451
|
+
- [ ] Suspicious activity alerts
|
|
452
|
+
- [ ] Security audit trail maintained
|
|
453
|
+
- [ ] Incident response plan in place
|