@pilllesss/yorn 1.0.182 → 1.0.183
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.
Potentially problematic release.
This version of @pilllesss/yorn might be problematic. Click here for more details.
- package/README.md +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/skills/code-review/LICENSE +21 -0
- package/dist/skills/code-review/SKILL.md +233 -0
- package/dist/skills/code-review/assets/pr-review-template.md +137 -0
- package/dist/skills/code-review/assets/review-checklist.md +123 -0
- package/dist/skills/code-review/reference/angular.md +768 -0
- package/dist/skills/code-review/reference/architecture-review-guide.md +472 -0
- package/dist/skills/code-review/reference/c.md +890 -0
- package/dist/skills/code-review/reference/code-quality-universal.md +488 -0
- package/dist/skills/code-review/reference/code-review-best-practices.md +136 -0
- package/dist/skills/code-review/reference/common-bugs-checklist.md +302 -0
- package/dist/skills/code-review/reference/cpp.md +893 -0
- package/dist/skills/code-review/reference/cross-cutting/async-concurrency-patterns.md +515 -0
- package/dist/skills/code-review/reference/cross-cutting/error-handling-principles.md +492 -0
- package/dist/skills/code-review/reference/cross-cutting/n-plus-one-queries.md +309 -0
- package/dist/skills/code-review/reference/cross-cutting/sql-injection-prevention.md +308 -0
- package/dist/skills/code-review/reference/cross-cutting/xss-prevention.md +264 -0
- package/dist/skills/code-review/reference/csharp.md +525 -0
- package/dist/skills/code-review/reference/css-less-sass.md +661 -0
- package/dist/skills/code-review/reference/dart.md +670 -0
- package/dist/skills/code-review/reference/django.md +985 -0
- package/dist/skills/code-review/reference/fastapi.md +580 -0
- package/dist/skills/code-review/reference/go.md +993 -0
- package/dist/skills/code-review/reference/java.md +409 -0
- package/dist/skills/code-review/reference/java8.md +586 -0
- package/dist/skills/code-review/reference/kotlin.md +1018 -0
- package/dist/skills/code-review/reference/nestjs.md +593 -0
- package/dist/skills/code-review/reference/performance-review-guide.md +816 -0
- package/dist/skills/code-review/reference/php.md +684 -0
- package/dist/skills/code-review/reference/python.md +1073 -0
- package/dist/skills/code-review/reference/qt.md +757 -0
- package/dist/skills/code-review/reference/react.md +871 -0
- package/dist/skills/code-review/reference/ruby.md +964 -0
- package/dist/skills/code-review/reference/rust.md +846 -0
- package/dist/skills/code-review/reference/security-review-guide.md +494 -0
- package/dist/skills/code-review/reference/svelte.md +1064 -0
- package/dist/skills/code-review/reference/swift.md +936 -0
- package/dist/skills/code-review/reference/typescript.md +1016 -0
- package/dist/skills/code-review/reference/vue.md +924 -0
- package/dist/skills/code-review/reference/zig.md +440 -0
- package/dist/skills/code-review/scripts/pr-analyzer.py +435 -0
- package/dist/skills/code-review/scripts/test_pr_analyzer.py +380 -0
- package/dist/yorn.cjs +628 -628
- package/package.json +2 -2
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# Security Review Guide
|
|
2
|
+
|
|
3
|
+
Security-focused code review checklist based on OWASP Top 10 and best practices.
|
|
4
|
+
|
|
5
|
+
## Authentication & Authorization
|
|
6
|
+
|
|
7
|
+
### Authentication
|
|
8
|
+
- [ ] Passwords hashed with strong algorithm (bcrypt, argon2)
|
|
9
|
+
- [ ] Password complexity requirements enforced
|
|
10
|
+
- [ ] Account lockout after failed attempts
|
|
11
|
+
- [ ] Secure password reset flow
|
|
12
|
+
- [ ] Multi-factor authentication for sensitive operations
|
|
13
|
+
- [ ] Session tokens are cryptographically random
|
|
14
|
+
- [ ] Session timeout implemented
|
|
15
|
+
|
|
16
|
+
### Authorization
|
|
17
|
+
- [ ] Authorization checks on every request
|
|
18
|
+
- [ ] Principle of least privilege applied
|
|
19
|
+
- [ ] Role-based access control (RBAC) properly implemented
|
|
20
|
+
- [ ] No privilege escalation paths
|
|
21
|
+
- [ ] Direct object reference checks (IDOR prevention)
|
|
22
|
+
- [ ] API endpoints protected appropriately
|
|
23
|
+
|
|
24
|
+
### JWT Security
|
|
25
|
+
```typescript
|
|
26
|
+
// ❌ Insecure JWT configuration
|
|
27
|
+
jwt.sign(payload, 'weak-secret');
|
|
28
|
+
|
|
29
|
+
// ✅ Secure JWT configuration
|
|
30
|
+
jwt.sign(payload, process.env.JWT_SECRET, {
|
|
31
|
+
algorithm: 'RS256',
|
|
32
|
+
expiresIn: '15m',
|
|
33
|
+
issuer: 'your-app',
|
|
34
|
+
audience: 'your-api'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ❌ Not verifying JWT properly
|
|
38
|
+
const decoded = jwt.decode(token); // No signature verification!
|
|
39
|
+
|
|
40
|
+
// ✅ Verify signature and claims
|
|
41
|
+
const decoded = jwt.verify(token, publicKey, {
|
|
42
|
+
algorithms: ['RS256'],
|
|
43
|
+
issuer: 'your-app',
|
|
44
|
+
audience: 'your-api'
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Input Validation
|
|
49
|
+
|
|
50
|
+
### SQL Injection Prevention
|
|
51
|
+
|
|
52
|
+
**The #1 rule**: Always use parameterized queries. Never concatenate user input into SQL strings.
|
|
53
|
+
|
|
54
|
+
Every major language and framework has a parameterized query mechanism:
|
|
55
|
+
- Python: `cursor.execute("SELECT ...", params)` / ORM filter methods
|
|
56
|
+
- Java: `PreparedStatement` / JPA `@Query` with `@Param`
|
|
57
|
+
- Go: `db.Query("SELECT ...", args...)`
|
|
58
|
+
- Node.js: `client.query("SELECT ...", [args])` / Prisma ORM
|
|
59
|
+
- PHP: PDO prepared statements / Laravel Eloquent
|
|
60
|
+
- C#: ADO.NET `SqlParameter` / Dapper / EF Core LINQ
|
|
61
|
+
|
|
62
|
+
> **See [SQL Injection Prevention Guide](cross-cutting/sql-injection-prevention.md) for complete cross-language examples, ORM unsafe patterns, dynamic identifier handling, and detection tools.**
|
|
63
|
+
|
|
64
|
+
### XSS Prevention
|
|
65
|
+
|
|
66
|
+
**The #1 rule**: Rely on framework auto-escaping. Audit every escape hatch.
|
|
67
|
+
|
|
68
|
+
Every major framework auto-escapes by default:
|
|
69
|
+
- React: JSX auto-escapes. Audit `dangerouslySetInnerHTML`.
|
|
70
|
+
- Vue: `{{ }}` auto-escapes. Audit `v-html`.
|
|
71
|
+
- Angular: Interpolation auto-escapes. Audit `bypassSecurityTrustHtml`.
|
|
72
|
+
- Svelte: `{ }` auto-escapes. Audit `{@html}`.
|
|
73
|
+
- Django: Templates auto-escape. Audit `mark_safe`.
|
|
74
|
+
|
|
75
|
+
For defense-in-depth, configure Content Security Policy (CSP) with nonce-based `script-src`.
|
|
76
|
+
|
|
77
|
+
> **See [XSS Prevention Guide](cross-cutting/xss-prevention.md) for complete cross-framework examples, CSP configuration, input validation vs output encoding, and detection tools.**
|
|
78
|
+
|
|
79
|
+
### CSRF Prevention
|
|
80
|
+
|
|
81
|
+
**CSRF Token Implementation**
|
|
82
|
+
```typescript
|
|
83
|
+
// ✅ Server: generate and validate CSRF token
|
|
84
|
+
import crypto from 'node:crypto';
|
|
85
|
+
|
|
86
|
+
function generateCsrfToken(): string {
|
|
87
|
+
return crypto.randomBytes(32).toString('hex');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Middleware: validate token on state-changing requests
|
|
91
|
+
app.post('/api/data', (req, res) => {
|
|
92
|
+
const token = req.headers['x-csrf-token'];
|
|
93
|
+
const sessionToken = req.session.csrfToken;
|
|
94
|
+
if (!token || token !== sessionToken) {
|
|
95
|
+
return res.status(403).json({ error: 'Invalid CSRF token' });
|
|
96
|
+
}
|
|
97
|
+
// ...handle request
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Python (Django)**
|
|
102
|
+
```python
|
|
103
|
+
# ✅ Django: built-in CSRF protection
|
|
104
|
+
# settings.py
|
|
105
|
+
MIDDLEWARE = [
|
|
106
|
+
'django.middleware.csrf.CsrfViewMiddleware', # 默认启用
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
# templates: include CSRF token
|
|
110
|
+
# <form method="post">
|
|
111
|
+
# {% csrf_token %}
|
|
112
|
+
# </form>
|
|
113
|
+
|
|
114
|
+
# ❌ Disabling CSRF on a view
|
|
115
|
+
@csrf_exempt # 除非绝对必要,否则不使用
|
|
116
|
+
def my_view(request):
|
|
117
|
+
...
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Java (Spring Boot)**
|
|
121
|
+
```java
|
|
122
|
+
// ✅ Spring Security: CSRF enabled by default
|
|
123
|
+
@Configuration
|
|
124
|
+
@EnableWebSecurity
|
|
125
|
+
public class SecurityConfig {
|
|
126
|
+
@Bean
|
|
127
|
+
public SecurityFilterChain filterChain(HttpSecurity http) {
|
|
128
|
+
http.csrf(csrf -> csrf
|
|
129
|
+
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
|
130
|
+
);
|
|
131
|
+
return http.build();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**SameSite Cookie**
|
|
137
|
+
```typescript
|
|
138
|
+
// ✅ Set SameSite cookie as additional defense
|
|
139
|
+
res.cookie('session', sessionId, {
|
|
140
|
+
httpOnly: true,
|
|
141
|
+
secure: true,
|
|
142
|
+
sameSite: 'strict', // 或 'lax' 用于允许导航 GET 请求
|
|
143
|
+
maxAge: 3600000,
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### SSRF Prevention
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# ❌ Vulnerable: user-controlled URL
|
|
151
|
+
import requests
|
|
152
|
+
url = request.GET.get('url')
|
|
153
|
+
response = requests.get(url)
|
|
154
|
+
|
|
155
|
+
# ✅ Validate URL against whitelist
|
|
156
|
+
ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com']
|
|
157
|
+
|
|
158
|
+
def is_safe_url(url: str) -> bool:
|
|
159
|
+
from urllib.parse import urlparse
|
|
160
|
+
parsed = urlparse(url)
|
|
161
|
+
return parsed.hostname in ALLOWED_HOSTS
|
|
162
|
+
|
|
163
|
+
if is_safe_url(url):
|
|
164
|
+
response = requests.get(url)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// ❌ Vulnerable: fetching arbitrary URLs
|
|
169
|
+
const url = req.query.url;
|
|
170
|
+
const response = await fetch(url);
|
|
171
|
+
|
|
172
|
+
// ✅ Validate URL before fetching
|
|
173
|
+
const ALLOWED_DOMAINS = ['api.internal.com'];
|
|
174
|
+
|
|
175
|
+
function isSafeUrl(url: string): boolean {
|
|
176
|
+
try {
|
|
177
|
+
const parsed = new URL(url);
|
|
178
|
+
// Block internal IPs
|
|
179
|
+
if (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1') {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (parsed.hostname.match(/^10\.|^172\.(1[6-9]|2\d|3[01])\.|^192\.168\./)) {
|
|
183
|
+
return false; // Block private IP ranges
|
|
184
|
+
}
|
|
185
|
+
return ALLOWED_DOMAINS.includes(parsed.hostname);
|
|
186
|
+
} catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```go
|
|
193
|
+
// ✅ Go: validate URL before making requests
|
|
194
|
+
import "net/url"
|
|
195
|
+
|
|
196
|
+
func isSafeURL(rawURL string) bool {
|
|
197
|
+
u, err := url.Parse(rawURL)
|
|
198
|
+
if err != nil {
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
// Block internal IPs
|
|
202
|
+
if u.Hostname() == "localhost" || u.Hostname() == "127.0.0.1" {
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
// Only allow HTTPS
|
|
206
|
+
if u.Scheme != "https" {
|
|
207
|
+
return false
|
|
208
|
+
}
|
|
209
|
+
return true
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### IDOR(不安全直接对象引用)
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
# ❌ Vulnerable: no ownership check
|
|
217
|
+
def get_order(request, order_id):
|
|
218
|
+
order = Order.objects.get(id=order_id) # 任何用户可查看任何订单
|
|
219
|
+
return JsonResponse(order.to_dict())
|
|
220
|
+
|
|
221
|
+
# ✅ Check ownership before returning
|
|
222
|
+
def get_order(request, order_id):
|
|
223
|
+
order = Order.objects.filter(id=order_id, user=request.user).first()
|
|
224
|
+
if not order:
|
|
225
|
+
return JsonResponse({'error': 'Not found'}, status=404)
|
|
226
|
+
return JsonResponse(order.to_dict())
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// ❌ Vulnerable: no authorization check
|
|
231
|
+
app.get('/api/orders/:id', async (req, res) => {
|
|
232
|
+
const order = await db.order.findUnique({
|
|
233
|
+
where: { id: Number(req.params.id) }
|
|
234
|
+
});
|
|
235
|
+
res.json(order);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// ✅ Include user context in query
|
|
239
|
+
app.get('/api/orders/:id', async (req, res) => {
|
|
240
|
+
const order = await db.order.findFirst({
|
|
241
|
+
where: {
|
|
242
|
+
id: Number(req.params.id),
|
|
243
|
+
userId: req.user.id, // Scope to current user
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
if (!order) return res.status(404).json({ error: 'Not found' });
|
|
247
|
+
res.json(order);
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```java
|
|
252
|
+
// ✅ Spring Security: method-level authorization
|
|
253
|
+
@GetMapping("/api/orders/{id}")
|
|
254
|
+
@PreAuthorize("@orderService.isOwner(#id, authentication.principal.id)")
|
|
255
|
+
public Order getOrder(@PathVariable Long id) {
|
|
256
|
+
return orderService.findById(id);
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**UUID vs 自增 ID**
|
|
261
|
+
```typescript
|
|
262
|
+
// ❌ 自增 ID 可被枚举
|
|
263
|
+
// GET /api/users/1, /api/users/2, /api/users/3 ...
|
|
264
|
+
|
|
265
|
+
// ✅ UUID 不可预测
|
|
266
|
+
// GET /api/users/550e8400-e29b-41d4-a716-446655440000
|
|
267
|
+
|
|
268
|
+
// ⚠️ UUID 只是防止枚举,不是权限控制
|
|
269
|
+
// 仍然需要验证当前用户是否有权访问该资源
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Command Injection Prevention
|
|
273
|
+
|
|
274
|
+
**Python**
|
|
275
|
+
```python
|
|
276
|
+
# ❌ Vulnerable: shell=True
|
|
277
|
+
import subprocess
|
|
278
|
+
subprocess.run(f"convert {filename} output.png", shell=True)
|
|
279
|
+
|
|
280
|
+
# ✅ Use list arguments without shell
|
|
281
|
+
subprocess.run(['convert', filename, 'output.png'], check=True)
|
|
282
|
+
|
|
283
|
+
# ✅ Validate and sanitize input
|
|
284
|
+
import shlex
|
|
285
|
+
safe_filename = shlex.quote(filename)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Node.js**
|
|
289
|
+
```typescript
|
|
290
|
+
// ❌ Vulnerable: exec with string interpolation
|
|
291
|
+
import { exec } from 'node:child_process';
|
|
292
|
+
exec(`convert ${filename} output.png`);
|
|
293
|
+
|
|
294
|
+
// ✅ Use execFile with array arguments
|
|
295
|
+
import { execFile } from 'node:child_process';
|
|
296
|
+
execFile('convert', [filename, 'output.png'], (error, stdout) => {
|
|
297
|
+
if (error) throw error;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// ❌ Never pass user input to shell
|
|
301
|
+
exec(`echo ${userInput}`); // userInput = "; rm -rf /"
|
|
302
|
+
|
|
303
|
+
// ✅ Sanitize or use non-shell alternatives
|
|
304
|
+
import { writeFile } from 'node:fs/promises';
|
|
305
|
+
await writeFile('output.txt', userInput); // No shell involved
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Go**
|
|
309
|
+
```go
|
|
310
|
+
// ❌ Vulnerable: shell command with user input
|
|
311
|
+
cmd := exec.Command("sh", "-c", "echo " + userInput)
|
|
312
|
+
|
|
313
|
+
// ✅ Use exec.Command with separate arguments
|
|
314
|
+
cmd := exec.Command("echo", userInput)
|
|
315
|
+
|
|
316
|
+
// ❌ Passing user input to shell
|
|
317
|
+
out, _ := exec.Command("bash", "-c", "cat "+filename).Output()
|
|
318
|
+
|
|
319
|
+
// ✅ Read file directly without shell
|
|
320
|
+
data, err := os.ReadFile(filename)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Java**
|
|
324
|
+
```java
|
|
325
|
+
// ❌ Vulnerable: Runtime.exec with string concatenation
|
|
326
|
+
Runtime.getRuntime().exec("convert " + filename + " output.png");
|
|
327
|
+
|
|
328
|
+
// ✅ Use ProcessBuilder with separate arguments
|
|
329
|
+
ProcessBuilder pb = new ProcessBuilder("convert", filename, "output.png");
|
|
330
|
+
Process process = pb.start();
|
|
331
|
+
|
|
332
|
+
// ❌ Dangerous: passing user input to shell
|
|
333
|
+
Runtime.getRuntime().exec(new String[]{"sh", "-c", "echo " + userInput});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Data Protection
|
|
337
|
+
|
|
338
|
+
### Sensitive Data Handling
|
|
339
|
+
- [ ] No secrets in source code
|
|
340
|
+
- [ ] Secrets stored in environment variables or secret manager
|
|
341
|
+
- [ ] Sensitive data encrypted at rest
|
|
342
|
+
- [ ] Sensitive data encrypted in transit (HTTPS)
|
|
343
|
+
- [ ] PII handled according to regulations (GDPR, etc.)
|
|
344
|
+
- [ ] Sensitive data not logged
|
|
345
|
+
- [ ] Secure data deletion when required
|
|
346
|
+
|
|
347
|
+
### Configuration Security
|
|
348
|
+
```yaml
|
|
349
|
+
# ❌ Secrets in config files
|
|
350
|
+
database:
|
|
351
|
+
password: "super-secret-password"
|
|
352
|
+
|
|
353
|
+
# ✅ Reference environment variables
|
|
354
|
+
database:
|
|
355
|
+
password: ${DATABASE_PASSWORD}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Error Messages
|
|
359
|
+
```typescript
|
|
360
|
+
// ❌ Leaking sensitive information
|
|
361
|
+
catch (error) {
|
|
362
|
+
return res.status(500).json({
|
|
363
|
+
error: error.stack, // Exposes internal details
|
|
364
|
+
query: sqlQuery // Exposes database structure
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ✅ Generic error messages
|
|
369
|
+
catch (error) {
|
|
370
|
+
logger.error('Database error', { error, userId }); // Log internally
|
|
371
|
+
return res.status(500).json({
|
|
372
|
+
error: 'An unexpected error occurred'
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## API Security
|
|
378
|
+
|
|
379
|
+
### Rate Limiting
|
|
380
|
+
- [ ] Rate limiting on all public endpoints
|
|
381
|
+
- [ ] Stricter limits on authentication endpoints
|
|
382
|
+
- [ ] Per-user and per-IP limits
|
|
383
|
+
- [ ] Graceful handling when limits exceeded
|
|
384
|
+
|
|
385
|
+
### CORS Configuration
|
|
386
|
+
```typescript
|
|
387
|
+
// ❌ Overly permissive CORS
|
|
388
|
+
app.use(cors({ origin: '*' }));
|
|
389
|
+
|
|
390
|
+
// ✅ Restrictive CORS
|
|
391
|
+
app.use(cors({
|
|
392
|
+
origin: ['https://your-app.com'],
|
|
393
|
+
methods: ['GET', 'POST'],
|
|
394
|
+
credentials: true
|
|
395
|
+
}));
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### HTTP Headers
|
|
399
|
+
```typescript
|
|
400
|
+
// Security headers to set
|
|
401
|
+
app.use(helmet({
|
|
402
|
+
contentSecurityPolicy: {
|
|
403
|
+
directives: {
|
|
404
|
+
defaultSrc: ["'self'"],
|
|
405
|
+
scriptSrc: ["'self'"],
|
|
406
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
hsts: { maxAge: 31536000, includeSubDomains: true },
|
|
410
|
+
noSniff: true,
|
|
411
|
+
xssFilter: true,
|
|
412
|
+
frameguard: { action: 'deny' }
|
|
413
|
+
}));
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Cryptography
|
|
417
|
+
|
|
418
|
+
### Secure Practices
|
|
419
|
+
- [ ] Using well-established algorithms (AES-256, RSA-2048+)
|
|
420
|
+
- [ ] Not implementing custom cryptography
|
|
421
|
+
- [ ] Using cryptographically secure random number generation
|
|
422
|
+
- [ ] Proper key management and rotation
|
|
423
|
+
- [ ] Secure key storage (HSM, KMS)
|
|
424
|
+
|
|
425
|
+
### Common Mistakes
|
|
426
|
+
```typescript
|
|
427
|
+
// ❌ Weak random generation
|
|
428
|
+
const token = Math.random().toString(36);
|
|
429
|
+
|
|
430
|
+
// ✅ Cryptographically secure random
|
|
431
|
+
const crypto = require('crypto');
|
|
432
|
+
const token = crypto.randomBytes(32).toString('hex');
|
|
433
|
+
|
|
434
|
+
// ❌ MD5/SHA1 for passwords
|
|
435
|
+
const hash = crypto.createHash('md5').update(password).digest('hex');
|
|
436
|
+
|
|
437
|
+
// ✅ Use bcrypt or argon2
|
|
438
|
+
const bcrypt = require('bcrypt');
|
|
439
|
+
const hash = await bcrypt.hash(password, 12);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Dependency Security
|
|
443
|
+
|
|
444
|
+
### Checklist
|
|
445
|
+
- [ ] Dependencies from trusted sources only
|
|
446
|
+
- [ ] No known vulnerabilities (npm audit, cargo audit)
|
|
447
|
+
- [ ] Dependencies kept up to date
|
|
448
|
+
- [ ] Lock files committed (package-lock.json, Cargo.lock)
|
|
449
|
+
- [ ] Minimal dependency usage
|
|
450
|
+
- [ ] License compliance verified
|
|
451
|
+
|
|
452
|
+
### Audit Commands
|
|
453
|
+
```bash
|
|
454
|
+
# Node.js
|
|
455
|
+
npm audit
|
|
456
|
+
npm audit fix
|
|
457
|
+
|
|
458
|
+
# Python
|
|
459
|
+
pip-audit
|
|
460
|
+
safety check
|
|
461
|
+
|
|
462
|
+
# Rust
|
|
463
|
+
cargo audit
|
|
464
|
+
|
|
465
|
+
# General
|
|
466
|
+
snyk test
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Logging & Monitoring
|
|
470
|
+
|
|
471
|
+
### Secure Logging
|
|
472
|
+
- [ ] No sensitive data in logs (passwords, tokens, PII)
|
|
473
|
+
- [ ] Logs protected from tampering
|
|
474
|
+
- [ ] Appropriate log retention
|
|
475
|
+
- [ ] Security events logged (login attempts, permission changes)
|
|
476
|
+
- [ ] Log injection prevented
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// ❌ Logging sensitive data
|
|
480
|
+
logger.info(`User login: ${email}, password: ${password}`);
|
|
481
|
+
|
|
482
|
+
// ✅ Safe logging
|
|
483
|
+
logger.info('User login attempt', { email, success: true });
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Security Review Severity Levels
|
|
487
|
+
|
|
488
|
+
| Severity | Description | Action |
|
|
489
|
+
|----------|-------------|--------|
|
|
490
|
+
| **Critical** | Immediate exploitation possible, data breach risk | Block merge, fix immediately |
|
|
491
|
+
| **High** | Significant vulnerability, requires specific conditions | Block merge, fix before release |
|
|
492
|
+
| **Medium** | Moderate risk, defense in depth concern | Should fix, can merge with tracking |
|
|
493
|
+
| **Low** | Minor issue, best practice violation | Nice to fix, non-blocking |
|
|
494
|
+
| **Info** | Suggestion for improvement | Optional enhancement |
|