@mytechtoday/augment-extensions 0.1.0 → 0.1.2
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/AGENTS.md +83 -3
- package/README.md +6 -5
- package/augment-extensions/coding-standards/python/README.md +44 -0
- package/augment-extensions/coding-standards/python/module.json +26 -0
- package/augment-extensions/coding-standards/python/rules/best-practices.md +232 -0
- package/augment-extensions/coding-standards/python/rules/code-organization.md +220 -0
- package/augment-extensions/coding-standards/python/rules/error-handling.md +221 -0
- package/augment-extensions/coding-standards/python/rules/naming-conventions.md +172 -0
- package/augment-extensions/coding-standards/python/rules/type-hints.md +188 -0
- package/augment-extensions/coding-standards/react/README.md +45 -0
- package/augment-extensions/coding-standards/react/module.json +27 -0
- package/augment-extensions/coding-standards/react/rules/component-patterns.md +214 -0
- package/augment-extensions/coding-standards/react/rules/hooks-best-practices.md +235 -0
- package/augment-extensions/coding-standards/react/rules/performance.md +300 -0
- package/augment-extensions/coding-standards/react/rules/state-management.md +265 -0
- package/augment-extensions/coding-standards/react/rules/typescript-react.md +271 -0
- package/augment-extensions/domain-rules/api-design/README.md +41 -0
- package/augment-extensions/domain-rules/api-design/module.json +27 -0
- package/augment-extensions/domain-rules/api-design/rules/authentication.md +263 -0
- package/augment-extensions/domain-rules/api-design/rules/documentation.md +395 -0
- package/augment-extensions/domain-rules/api-design/rules/error-handling.md +290 -0
- package/augment-extensions/domain-rules/api-design/rules/graphql-api.md +313 -0
- package/augment-extensions/domain-rules/api-design/rules/rest-api.md +214 -0
- package/augment-extensions/domain-rules/api-design/rules/versioning.md +268 -0
- package/augment-extensions/domain-rules/security/README.md +41 -0
- package/augment-extensions/domain-rules/security/module.json +28 -0
- package/augment-extensions/domain-rules/security/rules/authentication-security.md +361 -0
- package/augment-extensions/domain-rules/security/rules/encryption.md +208 -0
- package/augment-extensions/domain-rules/security/rules/input-validation.md +294 -0
- package/augment-extensions/domain-rules/security/rules/owasp-top-10.md +339 -0
- package/augment-extensions/domain-rules/security/rules/secure-coding.md +293 -0
- package/augment-extensions/domain-rules/security/rules/web-security.md +268 -0
- package/augment-extensions/examples/design-patterns/README.md +37 -0
- package/augment-extensions/examples/design-patterns/examples/behavioral-patterns.md +370 -0
- package/augment-extensions/examples/design-patterns/examples/creational-patterns.md +250 -0
- package/augment-extensions/examples/design-patterns/examples/structural-patterns.md +264 -0
- package/augment-extensions/examples/design-patterns/module.json +27 -0
- package/{modules → augment-extensions}/workflows/beads/examples/complete-workflow-example.md +5 -5
- package/{modules → augment-extensions}/workflows/beads/rules/file-format.md +45 -1
- package/{modules → augment-extensions}/workflows/beads/rules/workflow.md +41 -0
- package/{modules → augment-extensions}/workflows/openspec/examples/complete-change-example.md +14 -0
- package/{modules → augment-extensions}/workflows/openspec/rules/spec-format.md +44 -1
- package/{modules → augment-extensions}/workflows/openspec/rules/workflow.md +25 -0
- package/cli/dist/cli.js +69 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/coord.d.ts +30 -0
- package/cli/dist/commands/coord.d.ts.map +1 -0
- package/cli/dist/commands/coord.js +150 -0
- package/cli/dist/commands/coord.js.map +1 -0
- package/cli/dist/commands/link.js +1 -1
- package/cli/dist/commands/link.js.map +1 -1
- package/cli/dist/commands/list.js +1 -1
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/search.d.ts.map +1 -1
- package/cli/dist/commands/search.js +107 -5
- package/cli/dist/commands/search.js.map +1 -1
- package/cli/dist/commands/show.js +1 -1
- package/cli/dist/commands/show.js.map +1 -1
- package/cli/dist/commands/sync.d.ts +26 -0
- package/cli/dist/commands/sync.d.ts.map +1 -0
- package/cli/dist/commands/sync.js +106 -0
- package/cli/dist/commands/sync.js.map +1 -0
- package/cli/dist/commands/update.d.ts.map +1 -1
- package/cli/dist/commands/update.js +132 -7
- package/cli/dist/commands/update.js.map +1 -1
- package/cli/dist/utils/auto-sync.d.ts +34 -0
- package/cli/dist/utils/auto-sync.d.ts.map +1 -0
- package/cli/dist/utils/auto-sync.js +172 -0
- package/cli/dist/utils/auto-sync.js.map +1 -0
- package/cli/dist/utils/beads-sync.d.ts +51 -0
- package/cli/dist/utils/beads-sync.d.ts.map +1 -0
- package/cli/dist/utils/beads-sync.js +171 -0
- package/cli/dist/utils/beads-sync.js.map +1 -0
- package/cli/dist/utils/coordination-queries.d.ts +79 -0
- package/cli/dist/utils/coordination-queries.d.ts.map +1 -0
- package/cli/dist/utils/coordination-queries.js +155 -0
- package/cli/dist/utils/coordination-queries.js.map +1 -0
- package/cli/dist/utils/file-tracking.d.ts +42 -0
- package/cli/dist/utils/file-tracking.d.ts.map +1 -0
- package/cli/dist/utils/file-tracking.js +155 -0
- package/cli/dist/utils/file-tracking.js.map +1 -0
- package/cli/dist/utils/migrate.d.ts +25 -0
- package/cli/dist/utils/migrate.d.ts.map +1 -0
- package/cli/dist/utils/migrate.js +204 -0
- package/cli/dist/utils/migrate.js.map +1 -0
- package/cli/dist/utils/openspec-sync.d.ts +48 -0
- package/cli/dist/utils/openspec-sync.d.ts.map +1 -0
- package/cli/dist/utils/openspec-sync.js +167 -0
- package/cli/dist/utils/openspec-sync.js.map +1 -0
- package/{MODULES.md → modules.md} +1 -1
- package/package.json +9 -7
- /package/{modules → augment-extensions}/coding-standards/typescript/README.md +0 -0
- /package/{modules → augment-extensions}/coding-standards/typescript/module.json +0 -0
- /package/{modules → augment-extensions}/coding-standards/typescript/rules/naming-conventions.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/README.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/module.json +0 -0
- /package/{modules → augment-extensions}/workflows/beads/rules/best-practices.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/rules/manual-setup.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/README.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/module.json +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/rules/best-practices.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/rules/manual-setup.md +0 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# OWASP Top 10 Vulnerabilities
|
|
2
|
+
|
|
3
|
+
Mitigations for the OWASP Top 10 web application security risks.
|
|
4
|
+
|
|
5
|
+
## A01:2021 - Broken Access Control
|
|
6
|
+
|
|
7
|
+
Unauthorized access to resources or functions.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Bad - No authorization check
|
|
11
|
+
app.get('/api/users/:id', async (req, res) => {
|
|
12
|
+
const user = await db.users.findOne(req.params.id);
|
|
13
|
+
res.json(user);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Good - Check authorization
|
|
17
|
+
app.get('/api/users/:id', authenticate, async (req, res) => {
|
|
18
|
+
const user = await db.users.findOne(req.params.id);
|
|
19
|
+
|
|
20
|
+
// Users can only access their own data (unless admin)
|
|
21
|
+
if (user.id !== req.user.id && req.user.role !== 'admin') {
|
|
22
|
+
return res.status(403).json({ error: 'Forbidden' });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
res.json(user);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Good - Use middleware for authorization
|
|
29
|
+
const authorize = (permission: string) => {
|
|
30
|
+
return (req, res, next) => {
|
|
31
|
+
if (!req.user.permissions.includes(permission)) {
|
|
32
|
+
return res.status(403).json({ error: 'Insufficient permissions' });
|
|
33
|
+
}
|
|
34
|
+
next();
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
app.delete('/api/users/:id', authenticate, authorize('delete:users'), async (req, res) => {
|
|
39
|
+
await db.users.delete(req.params.id);
|
|
40
|
+
res.status(204).send();
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## A02:2021 - Cryptographic Failures
|
|
45
|
+
|
|
46
|
+
Sensitive data exposure due to weak cryptography.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Bad - Storing passwords in plain text
|
|
50
|
+
await db.users.create({
|
|
51
|
+
email: 'user@example.com',
|
|
52
|
+
password: 'secret123' // ❌ Never store plain text passwords
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Good - Hash passwords with bcrypt
|
|
56
|
+
import bcrypt from 'bcrypt';
|
|
57
|
+
|
|
58
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
59
|
+
await db.users.create({
|
|
60
|
+
email: 'user@example.com',
|
|
61
|
+
password: hashedPassword
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Good - Verify password
|
|
65
|
+
const isValid = await bcrypt.compare(inputPassword, user.password);
|
|
66
|
+
|
|
67
|
+
// Bad - Weak encryption
|
|
68
|
+
const encrypted = Buffer.from(data).toString('base64'); // ❌ Not encryption
|
|
69
|
+
|
|
70
|
+
// Good - Use proper encryption
|
|
71
|
+
import crypto from 'crypto';
|
|
72
|
+
|
|
73
|
+
const algorithm = 'aes-256-gcm';
|
|
74
|
+
const key = crypto.randomBytes(32);
|
|
75
|
+
const iv = crypto.randomBytes(16);
|
|
76
|
+
|
|
77
|
+
const cipher = crypto.createCipheriv(algorithm, key, iv);
|
|
78
|
+
let encrypted = cipher.update(data, 'utf8', 'hex');
|
|
79
|
+
encrypted += cipher.final('hex');
|
|
80
|
+
const authTag = cipher.getAuthTag();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## A03:2021 - Injection
|
|
84
|
+
|
|
85
|
+
SQL, NoSQL, OS command injection attacks.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Bad - SQL injection vulnerability
|
|
89
|
+
app.get('/users', async (req, res) => {
|
|
90
|
+
const query = `SELECT * FROM users WHERE name = '${req.query.name}'`;
|
|
91
|
+
const users = await db.query(query); // ❌ Vulnerable to SQL injection
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Good - Use parameterized queries
|
|
95
|
+
app.get('/users', async (req, res) => {
|
|
96
|
+
const users = await db.query(
|
|
97
|
+
'SELECT * FROM users WHERE name = $1',
|
|
98
|
+
[req.query.name]
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Good - Use ORM
|
|
103
|
+
const users = await db.users.findMany({
|
|
104
|
+
where: { name: req.query.name }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Bad - Command injection
|
|
108
|
+
const { exec } = require('child_process');
|
|
109
|
+
exec(`ping ${req.query.host}`); // ❌ Vulnerable to command injection
|
|
110
|
+
|
|
111
|
+
// Good - Validate and sanitize input
|
|
112
|
+
const host = req.query.host;
|
|
113
|
+
if (!/^[a-zA-Z0-9.-]+$/.test(host)) {
|
|
114
|
+
return res.status(400).json({ error: 'Invalid host' });
|
|
115
|
+
}
|
|
116
|
+
exec(`ping ${host}`);
|
|
117
|
+
|
|
118
|
+
// Better - Use safe alternatives
|
|
119
|
+
import { ping } from 'ping';
|
|
120
|
+
const result = await ping.promise.probe(req.query.host);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## A04:2021 - Insecure Design
|
|
124
|
+
|
|
125
|
+
Flaws in design and architecture.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Bad - No rate limiting
|
|
129
|
+
app.post('/api/login', async (req, res) => {
|
|
130
|
+
const user = await authenticate(req.body.email, req.body.password);
|
|
131
|
+
// ❌ Allows brute force attacks
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Good - Implement rate limiting
|
|
135
|
+
import rateLimit from 'express-rate-limit';
|
|
136
|
+
|
|
137
|
+
const loginLimiter = rateLimit({
|
|
138
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
139
|
+
max: 5, // 5 attempts
|
|
140
|
+
message: 'Too many login attempts, please try again later'
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
app.post('/api/login', loginLimiter, async (req, res) => {
|
|
144
|
+
const user = await authenticate(req.body.email, req.body.password);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Good - Implement account lockout
|
|
148
|
+
const MAX_ATTEMPTS = 5;
|
|
149
|
+
const LOCKOUT_TIME = 15 * 60 * 1000; // 15 minutes
|
|
150
|
+
|
|
151
|
+
if (user.loginAttempts >= MAX_ATTEMPTS) {
|
|
152
|
+
const timeSinceLastAttempt = Date.now() - user.lastLoginAttempt;
|
|
153
|
+
if (timeSinceLastAttempt < LOCKOUT_TIME) {
|
|
154
|
+
return res.status(429).json({ error: 'Account locked' });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## A05:2021 - Security Misconfiguration
|
|
160
|
+
|
|
161
|
+
Insecure default configurations.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Bad - Exposing error details in production
|
|
165
|
+
app.use((err, req, res, next) => {
|
|
166
|
+
res.status(500).json({
|
|
167
|
+
error: err.message,
|
|
168
|
+
stack: err.stack // ❌ Exposes internal details
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Good - Hide details in production
|
|
173
|
+
app.use((err, req, res, next) => {
|
|
174
|
+
console.error(err); // Log server-side
|
|
175
|
+
|
|
176
|
+
if (process.env.NODE_ENV === 'production') {
|
|
177
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
178
|
+
} else {
|
|
179
|
+
res.status(500).json({ error: err.message, stack: err.stack });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Bad - Default credentials
|
|
184
|
+
const dbConfig = {
|
|
185
|
+
user: 'admin',
|
|
186
|
+
password: 'admin' // ❌ Default credentials
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Good - Use environment variables
|
|
190
|
+
const dbConfig = {
|
|
191
|
+
user: process.env.DB_USER,
|
|
192
|
+
password: process.env.DB_PASSWORD
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Good - Security headers
|
|
196
|
+
import helmet from 'helmet';
|
|
197
|
+
app.use(helmet());
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## A06:2021 - Vulnerable and Outdated Components
|
|
201
|
+
|
|
202
|
+
Using components with known vulnerabilities.
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Good - Regularly update dependencies
|
|
206
|
+
npm audit
|
|
207
|
+
npm audit fix
|
|
208
|
+
|
|
209
|
+
# Good - Use automated tools
|
|
210
|
+
npm install -g npm-check-updates
|
|
211
|
+
ncu -u
|
|
212
|
+
npm install
|
|
213
|
+
|
|
214
|
+
# Good - Monitor for vulnerabilities
|
|
215
|
+
# Use Dependabot, Snyk, or similar tools
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## A07:2021 - Identification and Authentication Failures
|
|
219
|
+
|
|
220
|
+
Weak authentication mechanisms.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// Bad - Weak password requirements
|
|
224
|
+
if (password.length < 6) { // ❌ Too weak
|
|
225
|
+
return res.status(400).json({ error: 'Password too short' });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Good - Strong password requirements
|
|
229
|
+
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{12,}$/;
|
|
230
|
+
if (!passwordRegex.test(password)) {
|
|
231
|
+
return res.status(400).json({
|
|
232
|
+
error: 'Password must be at least 12 characters with uppercase, lowercase, number, and special character'
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Good - Implement MFA
|
|
237
|
+
const mfaToken = speakeasy.totp({
|
|
238
|
+
secret: user.mfaSecret,
|
|
239
|
+
encoding: 'base32'
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (req.body.mfaCode !== mfaToken) {
|
|
243
|
+
return res.status(401).json({ error: 'Invalid MFA code' });
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## A08:2021 - Software and Data Integrity Failures
|
|
248
|
+
|
|
249
|
+
Insecure CI/CD, updates, or deserialization.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Bad - Unsafe deserialization
|
|
253
|
+
const userData = eval(req.body.data); // ❌ Never use eval
|
|
254
|
+
|
|
255
|
+
// Good - Safe JSON parsing
|
|
256
|
+
try {
|
|
257
|
+
const userData = JSON.parse(req.body.data);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
return res.status(400).json({ error: 'Invalid JSON' });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Good - Verify package integrity
|
|
263
|
+
# package-lock.json ensures integrity
|
|
264
|
+
npm ci # Use in CI/CD instead of npm install
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## A09:2021 - Security Logging and Monitoring Failures
|
|
268
|
+
|
|
269
|
+
Insufficient logging and monitoring.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// Good - Log security events
|
|
273
|
+
import winston from 'winston';
|
|
274
|
+
|
|
275
|
+
const logger = winston.createLogger({
|
|
276
|
+
level: 'info',
|
|
277
|
+
format: winston.format.json(),
|
|
278
|
+
transports: [
|
|
279
|
+
new winston.transports.File({ filename: 'security.log' })
|
|
280
|
+
]
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Log authentication attempts
|
|
284
|
+
logger.info('Login attempt', {
|
|
285
|
+
email: req.body.email,
|
|
286
|
+
ip: req.ip,
|
|
287
|
+
userAgent: req.headers['user-agent'],
|
|
288
|
+
success: true
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Log authorization failures
|
|
292
|
+
logger.warn('Authorization failed', {
|
|
293
|
+
userId: req.user.id,
|
|
294
|
+
resource: req.path,
|
|
295
|
+
action: req.method,
|
|
296
|
+
ip: req.ip
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## A10:2021 - Server-Side Request Forgery (SSRF)
|
|
301
|
+
|
|
302
|
+
Fetching remote resources without validation.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Bad - SSRF vulnerability
|
|
306
|
+
app.get('/fetch', async (req, res) => {
|
|
307
|
+
const response = await fetch(req.query.url); // ❌ Allows SSRF
|
|
308
|
+
res.send(await response.text());
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Good - Validate URL
|
|
312
|
+
const allowedDomains = ['api.example.com', 'cdn.example.com'];
|
|
313
|
+
|
|
314
|
+
const url = new URL(req.query.url);
|
|
315
|
+
if (!allowedDomains.includes(url.hostname)) {
|
|
316
|
+
return res.status(400).json({ error: 'Invalid domain' });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Good - Block private IPs
|
|
320
|
+
import isPrivateIp from 'private-ip';
|
|
321
|
+
|
|
322
|
+
if (isPrivateIp(url.hostname)) {
|
|
323
|
+
return res.status(400).json({ error: 'Private IPs not allowed' });
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Best Practices
|
|
328
|
+
|
|
329
|
+
1. **Implement proper access control** - Check authorization
|
|
330
|
+
2. **Use strong encryption** - Protect sensitive data
|
|
331
|
+
3. **Prevent injection** - Use parameterized queries
|
|
332
|
+
4. **Design securely** - Security by design
|
|
333
|
+
5. **Configure securely** - No default credentials
|
|
334
|
+
6. **Update dependencies** - Patch vulnerabilities
|
|
335
|
+
7. **Strong authentication** - MFA, strong passwords
|
|
336
|
+
8. **Verify integrity** - Check package integrity
|
|
337
|
+
9. **Log security events** - Monitor for attacks
|
|
338
|
+
10. **Prevent SSRF** - Validate URLs and IPs
|
|
339
|
+
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Secure Coding Practices
|
|
2
|
+
|
|
3
|
+
General secure coding practices for building secure applications.
|
|
4
|
+
|
|
5
|
+
## Principle of Least Privilege
|
|
6
|
+
|
|
7
|
+
Grant minimum necessary permissions.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Bad - Overly permissive
|
|
11
|
+
const user = {
|
|
12
|
+
id: '123',
|
|
13
|
+
role: 'admin', // ❌ Everyone is admin
|
|
14
|
+
permissions: ['*'] // ❌ All permissions
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Good - Specific permissions
|
|
18
|
+
const user = {
|
|
19
|
+
id: '123',
|
|
20
|
+
role: 'editor',
|
|
21
|
+
permissions: ['read:posts', 'write:posts', 'read:users']
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Good - Check specific permission
|
|
25
|
+
const hasPermission = (user: User, permission: string): boolean => {
|
|
26
|
+
return user.permissions.includes(permission);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (!hasPermission(req.user, 'delete:posts')) {
|
|
30
|
+
return res.status(403).json({ error: 'Forbidden' });
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Defense in Depth
|
|
35
|
+
|
|
36
|
+
Implement multiple layers of security.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Layer 1: Input validation
|
|
40
|
+
const schema = z.object({
|
|
41
|
+
email: z.string().email(),
|
|
42
|
+
content: z.string().max(1000)
|
|
43
|
+
});
|
|
44
|
+
const data = schema.parse(req.body);
|
|
45
|
+
|
|
46
|
+
// Layer 2: Authentication
|
|
47
|
+
if (!req.user) {
|
|
48
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Layer 3: Authorization
|
|
52
|
+
if (req.user.id !== post.authorId && req.user.role !== 'admin') {
|
|
53
|
+
return res.status(403).json({ error: 'Forbidden' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Layer 4: Rate limiting
|
|
57
|
+
// Layer 5: Sanitization
|
|
58
|
+
const sanitized = DOMPurify.sanitize(data.content);
|
|
59
|
+
|
|
60
|
+
// Layer 6: Logging
|
|
61
|
+
logger.info('Post updated', { userId: req.user.id, postId: post.id });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Secure Defaults
|
|
65
|
+
|
|
66
|
+
Use secure configurations by default.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Good - Secure defaults
|
|
70
|
+
const config = {
|
|
71
|
+
https: true,
|
|
72
|
+
httpOnly: true,
|
|
73
|
+
sameSite: 'strict',
|
|
74
|
+
secure: true,
|
|
75
|
+
maxAge: 3600000,
|
|
76
|
+
...userConfig // Allow override if needed
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Bad - Insecure defaults
|
|
80
|
+
const config = {
|
|
81
|
+
https: false, // ❌
|
|
82
|
+
httpOnly: false, // ❌
|
|
83
|
+
...userConfig
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Fail Securely
|
|
88
|
+
|
|
89
|
+
Handle errors without exposing sensitive information.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Bad - Exposing internal details
|
|
93
|
+
try {
|
|
94
|
+
const user = await db.users.findOne(userId);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
res.status(500).json({
|
|
97
|
+
error: error.message, // ❌ May expose DB details
|
|
98
|
+
stack: error.stack // ❌ Exposes code structure
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Good - Generic error message
|
|
103
|
+
try {
|
|
104
|
+
const user = await db.users.findOne(userId);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logger.error('Database error', { error, userId }); // Log internally
|
|
107
|
+
res.status(500).json({ error: 'Internal server error' }); // Generic message
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Don't Trust Client Data
|
|
112
|
+
|
|
113
|
+
Always validate on server side.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Bad - Trusting client
|
|
117
|
+
app.post('/purchase', async (req, res) => {
|
|
118
|
+
const { productId, price } = req.body; // ❌ Client sends price
|
|
119
|
+
await processPayment(price);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Good - Verify on server
|
|
123
|
+
app.post('/purchase', async (req, res) => {
|
|
124
|
+
const { productId } = req.body;
|
|
125
|
+
const product = await db.products.findOne(productId);
|
|
126
|
+
await processPayment(product.price); // ✅ Use server price
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Avoid Security by Obscurity
|
|
131
|
+
|
|
132
|
+
Don't rely on secrecy of implementation.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Bad - Security by obscurity
|
|
136
|
+
const isAdmin = (user) => {
|
|
137
|
+
return user.secretAdminFlag === 'xK9mP2qL'; // ❌ Weak
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Good - Proper authorization
|
|
141
|
+
const isAdmin = (user) => {
|
|
142
|
+
return user.role === 'admin' && user.verified === true;
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Secure Error Messages
|
|
147
|
+
|
|
148
|
+
Don't leak information through error messages.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Bad - Information leakage
|
|
152
|
+
app.post('/login', async (req, res) => {
|
|
153
|
+
const user = await db.users.findOne({ email: req.body.email });
|
|
154
|
+
|
|
155
|
+
if (!user) {
|
|
156
|
+
return res.status(401).json({ error: 'Email not found' }); // ❌ Reveals email exists
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!await bcrypt.compare(req.body.password, user.password)) {
|
|
160
|
+
return res.status(401).json({ error: 'Incorrect password' }); // ❌ Reveals email exists
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Good - Generic error message
|
|
165
|
+
app.post('/login', async (req, res) => {
|
|
166
|
+
const user = await db.users.findOne({ email: req.body.email });
|
|
167
|
+
|
|
168
|
+
if (!user || !await bcrypt.compare(req.body.password, user.password)) {
|
|
169
|
+
return res.status(401).json({ error: 'Invalid credentials' }); // ✅ Generic
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Secure Logging
|
|
175
|
+
|
|
176
|
+
Log security events without exposing sensitive data.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Bad - Logging sensitive data
|
|
180
|
+
logger.info('User login', {
|
|
181
|
+
email: user.email,
|
|
182
|
+
password: req.body.password, // ❌ Never log passwords
|
|
183
|
+
creditCard: user.creditCard // ❌ Never log PII
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Good - Log relevant info only
|
|
187
|
+
logger.info('User login', {
|
|
188
|
+
userId: user.id,
|
|
189
|
+
ip: req.ip,
|
|
190
|
+
userAgent: req.headers['user-agent'],
|
|
191
|
+
success: true
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Good - Mask sensitive data
|
|
195
|
+
const maskEmail = (email: string) => {
|
|
196
|
+
const [local, domain] = email.split('@');
|
|
197
|
+
return `${local.slice(0, 2)}***@${domain}`;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
logger.info('Password reset requested', {
|
|
201
|
+
email: maskEmail(user.email),
|
|
202
|
+
ip: req.ip
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Dependency Security
|
|
207
|
+
|
|
208
|
+
Keep dependencies updated and secure.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Check for vulnerabilities
|
|
212
|
+
npm audit
|
|
213
|
+
|
|
214
|
+
# Fix vulnerabilities
|
|
215
|
+
npm audit fix
|
|
216
|
+
|
|
217
|
+
# Update dependencies
|
|
218
|
+
npm update
|
|
219
|
+
|
|
220
|
+
# Use lock files
|
|
221
|
+
npm ci # In CI/CD
|
|
222
|
+
|
|
223
|
+
# Monitor dependencies
|
|
224
|
+
# Use Dependabot, Snyk, or similar
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Environment Variables
|
|
228
|
+
|
|
229
|
+
Store secrets in environment variables.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Bad - Hardcoded secrets
|
|
233
|
+
const dbPassword = 'mypassword123'; // ❌
|
|
234
|
+
const apiKey = 'sk_live_abc123'; // ❌
|
|
235
|
+
|
|
236
|
+
// Good - Environment variables
|
|
237
|
+
const dbPassword = process.env.DB_PASSWORD;
|
|
238
|
+
const apiKey = process.env.API_KEY;
|
|
239
|
+
|
|
240
|
+
// Good - Validate required env vars
|
|
241
|
+
const requiredEnvVars = ['DB_PASSWORD', 'API_KEY', 'JWT_SECRET'];
|
|
242
|
+
|
|
243
|
+
for (const envVar of requiredEnvVars) {
|
|
244
|
+
if (!process.env[envVar]) {
|
|
245
|
+
throw new Error(`Missing required environment variable: ${envVar}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// .env (never commit)
|
|
250
|
+
DB_PASSWORD=strong_password
|
|
251
|
+
API_KEY=sk_live_abc123
|
|
252
|
+
JWT_SECRET=random_secret
|
|
253
|
+
|
|
254
|
+
// .gitignore
|
|
255
|
+
.env
|
|
256
|
+
.env.local
|
|
257
|
+
.env.*.local
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Code Review
|
|
261
|
+
|
|
262
|
+
Implement security-focused code reviews.
|
|
263
|
+
|
|
264
|
+
```markdown
|
|
265
|
+
# Security Review Checklist
|
|
266
|
+
|
|
267
|
+
- [ ] Input validation on all user input
|
|
268
|
+
- [ ] Authentication required for protected endpoints
|
|
269
|
+
- [ ] Authorization checks for resource access
|
|
270
|
+
- [ ] Parameterized queries (no SQL injection)
|
|
271
|
+
- [ ] No hardcoded secrets
|
|
272
|
+
- [ ] Sensitive data encrypted
|
|
273
|
+
- [ ] Error messages don't leak information
|
|
274
|
+
- [ ] Rate limiting on sensitive endpoints
|
|
275
|
+
- [ ] HTTPS enforced
|
|
276
|
+
- [ ] Security headers set
|
|
277
|
+
- [ ] Dependencies up to date
|
|
278
|
+
- [ ] Logging doesn't expose sensitive data
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Best Practices
|
|
282
|
+
|
|
283
|
+
1. **Least privilege** - Minimum necessary permissions
|
|
284
|
+
2. **Defense in depth** - Multiple security layers
|
|
285
|
+
3. **Secure defaults** - Secure by default
|
|
286
|
+
4. **Fail securely** - Don't expose internals
|
|
287
|
+
5. **Validate server-side** - Never trust client
|
|
288
|
+
6. **Generic errors** - Don't leak information
|
|
289
|
+
7. **Secure logging** - Mask sensitive data
|
|
290
|
+
8. **Update dependencies** - Patch vulnerabilities
|
|
291
|
+
9. **Use env vars** - No hardcoded secrets
|
|
292
|
+
10. **Code review** - Security-focused reviews
|
|
293
|
+
|