codeforge-dev 1.5.7 → 1.7.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/.devcontainer/.env +2 -1
- package/.devcontainer/CHANGELOG.md +55 -9
- package/.devcontainer/CLAUDE.md +65 -15
- package/.devcontainer/README.md +67 -6
- package/.devcontainer/config/keybindings.json +5 -0
- package/.devcontainer/config/main-system-prompt.md +63 -2
- package/.devcontainer/config/settings.json +25 -6
- package/.devcontainer/devcontainer.json +23 -7
- package/.devcontainer/features/README.md +21 -7
- package/.devcontainer/features/ccburn/README.md +60 -0
- package/.devcontainer/features/ccburn/devcontainer-feature.json +38 -0
- package/.devcontainer/features/ccburn/install.sh +174 -0
- package/.devcontainer/features/ccstatusline/README.md +22 -21
- package/.devcontainer/features/ccstatusline/devcontainer-feature.json +1 -1
- package/.devcontainer/features/ccstatusline/install.sh +48 -16
- package/.devcontainer/features/claude-code/config/settings.json +60 -24
- package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +1 -1
- package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/__pycache__/format-on-stop.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py +21 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/__pycache__/lint-file.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +7 -10
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/REVIEW-RUBRIC.md +440 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +190 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/bash-exec.md +173 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +155 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +248 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +233 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +235 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +125 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/git-archaeologist.md +242 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/migrator.md +195 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/perf-profiler.md +265 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/refactorer.md +209 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/researcher.md +195 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/security-auditor.md +289 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +284 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/statusline-config.md +188 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/test-writer.md +245 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +12 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/guard-readonly-bash.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/redirect-builtin-agents.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/skill-suggester.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/syntax-validator.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-no-regression.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-tests-pass.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/guard-readonly-bash.py +611 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/redirect-builtin-agents.py +83 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +85 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/syntax-validator.py +9 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-no-regression.py +221 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-tests-pass.py +176 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/SKILL.md +599 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/references/sdk-typescript-reference.md +954 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/SKILL.md +276 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/advanced-commands.md +332 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/investigation-playbooks.md +319 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/SKILL.md +341 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/interpreting-results.md +235 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/tool-commands.md +395 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/SKILL.md +344 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/safe-transformations.md +247 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/smell-catalog.md +332 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/SKILL.md +277 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/owasp-patterns.md +269 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/secrets-patterns.md +253 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +288 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/criteria-patterns.md +245 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/ears-templates.md +239 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/__pycache__/guard-protected.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +40 -39
- package/.devcontainer/scripts/setup-aliases.sh +10 -20
- package/.devcontainer/scripts/setup-config.sh +2 -0
- package/.devcontainer/scripts/setup-plugins.sh +38 -46
- package/.devcontainer/scripts/setup-projects.sh +175 -0
- package/.devcontainer/scripts/setup-symlink-claude.sh +36 -0
- package/.devcontainer/scripts/setup-update-claude.sh +11 -8
- package/.devcontainer/scripts/setup.sh +4 -2
- package/package.json +1 -1
- package/.devcontainer/scripts/setup-irie-claude.sh +0 -32
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# Code Smell Catalog
|
|
2
|
+
|
|
3
|
+
Complete catalog of code smells with detection heuristics and before/after examples.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Long Method](#long-method)
|
|
8
|
+
- [Feature Envy](#feature-envy)
|
|
9
|
+
- [Data Clump](#data-clump)
|
|
10
|
+
- [Primitive Obsession](#primitive-obsession)
|
|
11
|
+
- [God Class](#god-class)
|
|
12
|
+
- [Shotgun Surgery](#shotgun-surgery)
|
|
13
|
+
- [Divergent Change and Message Chain](#divergent-change-and-message-chain)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Long Method
|
|
18
|
+
|
|
19
|
+
**Detection heuristics:**
|
|
20
|
+
- Method body exceeds ~20 lines
|
|
21
|
+
- You need to scroll to see the entire function
|
|
22
|
+
- Comments separate logical sections within the method
|
|
23
|
+
- Multiple levels of indentation (3+ nesting levels)
|
|
24
|
+
|
|
25
|
+
### Before
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
def process_order(order, db, mailer):
|
|
29
|
+
# Validate order
|
|
30
|
+
if not order.items:
|
|
31
|
+
raise ValueError("Order has no items")
|
|
32
|
+
if order.total <= 0:
|
|
33
|
+
raise ValueError("Order total must be positive")
|
|
34
|
+
for item in order.items:
|
|
35
|
+
stock = db.get_stock(item.product_id)
|
|
36
|
+
if stock < item.quantity:
|
|
37
|
+
raise ValueError(f"Insufficient stock for {item.product_id}")
|
|
38
|
+
|
|
39
|
+
# Calculate totals
|
|
40
|
+
subtotal = sum(item.price * item.quantity for item in order.items)
|
|
41
|
+
tax = subtotal * 0.08
|
|
42
|
+
if order.customer.is_tax_exempt:
|
|
43
|
+
tax = 0
|
|
44
|
+
shipping = 5.99 if subtotal < 50 else 0
|
|
45
|
+
total = subtotal + tax + shipping
|
|
46
|
+
|
|
47
|
+
# Save to database
|
|
48
|
+
order.subtotal = subtotal
|
|
49
|
+
order.tax = tax
|
|
50
|
+
order.shipping = shipping
|
|
51
|
+
order.total = total
|
|
52
|
+
order.status = "confirmed"
|
|
53
|
+
db.save_order(order)
|
|
54
|
+
|
|
55
|
+
# Send confirmation
|
|
56
|
+
subject = f"Order #{order.id} Confirmed"
|
|
57
|
+
body = f"Your order total is ${total:.2f}"
|
|
58
|
+
mailer.send(order.customer.email, subject, body)
|
|
59
|
+
|
|
60
|
+
return order
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### After
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
def process_order(order, db, mailer):
|
|
67
|
+
validate_order(order, db)
|
|
68
|
+
totals = calculate_totals(order)
|
|
69
|
+
save_order(order, totals, db)
|
|
70
|
+
send_confirmation(order, totals, mailer)
|
|
71
|
+
return order
|
|
72
|
+
|
|
73
|
+
def validate_order(order, db):
|
|
74
|
+
if not order.items:
|
|
75
|
+
raise ValueError("Order has no items")
|
|
76
|
+
if order.total <= 0:
|
|
77
|
+
raise ValueError("Order total must be positive")
|
|
78
|
+
for item in order.items:
|
|
79
|
+
stock = db.get_stock(item.product_id)
|
|
80
|
+
if stock < item.quantity:
|
|
81
|
+
raise ValueError(f"Insufficient stock for {item.product_id}")
|
|
82
|
+
|
|
83
|
+
def calculate_totals(order):
|
|
84
|
+
subtotal = sum(item.price * item.quantity for item in order.items)
|
|
85
|
+
tax = 0 if order.customer.is_tax_exempt else subtotal * 0.08
|
|
86
|
+
shipping = 5.99 if subtotal < 50 else 0
|
|
87
|
+
return {"subtotal": subtotal, "tax": tax, "shipping": shipping, "total": subtotal + tax + shipping}
|
|
88
|
+
|
|
89
|
+
def save_order(order, totals, db):
|
|
90
|
+
order.subtotal = totals["subtotal"]
|
|
91
|
+
order.tax = totals["tax"]
|
|
92
|
+
order.shipping = totals["shipping"]
|
|
93
|
+
order.total = totals["total"]
|
|
94
|
+
order.status = "confirmed"
|
|
95
|
+
db.save_order(order)
|
|
96
|
+
|
|
97
|
+
def send_confirmation(order, totals, mailer):
|
|
98
|
+
subject = f"Order #{order.id} Confirmed"
|
|
99
|
+
body = f"Your order total is ${totals['total']:.2f}"
|
|
100
|
+
mailer.send(order.customer.email, subject, body)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Feature Envy
|
|
106
|
+
|
|
107
|
+
**Detection heuristics:**
|
|
108
|
+
- A method accesses more fields/methods from another object than from its own
|
|
109
|
+
- Multiple chained attribute accesses (`obj.a.b.c`)
|
|
110
|
+
- The method could be moved to the other class and would need fewer parameters
|
|
111
|
+
|
|
112
|
+
### Before
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
class Order:
|
|
116
|
+
def calculate_shipping(self):
|
|
117
|
+
# Envies Customer's data
|
|
118
|
+
if self.customer.address.country == "US":
|
|
119
|
+
if self.customer.address.state in ("AK", "HI"):
|
|
120
|
+
return self.total * 0.15
|
|
121
|
+
return self.total * 0.05
|
|
122
|
+
elif self.customer.address.country == "CA":
|
|
123
|
+
return self.total * 0.10
|
|
124
|
+
return self.total * 0.20
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### After
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
class Address:
|
|
131
|
+
def shipping_rate(self):
|
|
132
|
+
if self.country == "US":
|
|
133
|
+
if self.state in ("AK", "HI"):
|
|
134
|
+
return 0.15
|
|
135
|
+
return 0.05
|
|
136
|
+
elif self.country == "CA":
|
|
137
|
+
return 0.10
|
|
138
|
+
return 0.20
|
|
139
|
+
|
|
140
|
+
class Order:
|
|
141
|
+
def calculate_shipping(self):
|
|
142
|
+
return self.total * self.customer.address.shipping_rate()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Data Clump
|
|
148
|
+
|
|
149
|
+
**Detection heuristics:**
|
|
150
|
+
- The same 3+ parameters appear together in multiple function signatures
|
|
151
|
+
- You find yourself passing the same group of variables through several functions
|
|
152
|
+
- Parameters have a logical relationship (e.g., `start_date, end_date` or `host, port, protocol`)
|
|
153
|
+
|
|
154
|
+
### Before
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
def connect(host: str, port: int, protocol: str, timeout: int): ...
|
|
158
|
+
def health_check(host: str, port: int, protocol: str): ...
|
|
159
|
+
def send_request(host: str, port: int, protocol: str, payload: bytes): ...
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### After
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from dataclasses import dataclass
|
|
166
|
+
|
|
167
|
+
@dataclass
|
|
168
|
+
class ServerConfig:
|
|
169
|
+
host: str
|
|
170
|
+
port: int
|
|
171
|
+
protocol: str = "https"
|
|
172
|
+
timeout: int = 30
|
|
173
|
+
|
|
174
|
+
def connect(config: ServerConfig): ...
|
|
175
|
+
def health_check(config: ServerConfig): ...
|
|
176
|
+
def send_request(config: ServerConfig, payload: bytes): ...
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Primitive Obsession
|
|
182
|
+
|
|
183
|
+
**Detection heuristics:**
|
|
184
|
+
- Business validation logic scattered across multiple call sites
|
|
185
|
+
- `str` used for structured values (emails, URLs, phone numbers)
|
|
186
|
+
- `int` or `float` used for money, percentages, or quantities with constraints
|
|
187
|
+
- `dict` used where a typed structure is needed
|
|
188
|
+
|
|
189
|
+
### Before
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
def create_user(email: str, age: int):
|
|
193
|
+
if "@" not in email or "." not in email.split("@")[1]:
|
|
194
|
+
raise ValueError("Invalid email")
|
|
195
|
+
if age < 0 or age > 150:
|
|
196
|
+
raise ValueError("Invalid age")
|
|
197
|
+
# ... same validation repeated in update_user, send_invite, etc.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### After
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
class Email:
|
|
204
|
+
def __init__(self, value: str):
|
|
205
|
+
if "@" not in value or "." not in value.split("@")[1]:
|
|
206
|
+
raise ValueError(f"Invalid email: {value}")
|
|
207
|
+
self.value = value
|
|
208
|
+
|
|
209
|
+
def __str__(self):
|
|
210
|
+
return self.value
|
|
211
|
+
|
|
212
|
+
class Age:
|
|
213
|
+
def __init__(self, value: int):
|
|
214
|
+
if value < 0 or value > 150:
|
|
215
|
+
raise ValueError(f"Invalid age: {value}")
|
|
216
|
+
self.value = value
|
|
217
|
+
|
|
218
|
+
def create_user(email: Email, age: Age):
|
|
219
|
+
# Validation is guaranteed by construction
|
|
220
|
+
...
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## God Class
|
|
226
|
+
|
|
227
|
+
**Detection heuristics:**
|
|
228
|
+
- Class has 500+ lines or 20+ methods
|
|
229
|
+
- Class name is vague: `Manager`, `Handler`, `Processor`, `Utils`, `Helper`
|
|
230
|
+
- Class imports from many unrelated modules
|
|
231
|
+
- Methods cluster into groups that don't share instance state
|
|
232
|
+
|
|
233
|
+
### Before
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
class UserManager:
|
|
237
|
+
def create_user(self, ...): ...
|
|
238
|
+
def update_user(self, ...): ...
|
|
239
|
+
def delete_user(self, ...): ...
|
|
240
|
+
def authenticate(self, ...): ...
|
|
241
|
+
def reset_password(self, ...): ...
|
|
242
|
+
def send_welcome_email(self, ...): ...
|
|
243
|
+
def send_password_reset_email(self, ...): ...
|
|
244
|
+
def generate_report(self, ...): ...
|
|
245
|
+
def export_to_csv(self, ...): ...
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### After
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
class UserRepository:
|
|
252
|
+
def create(self, ...): ...
|
|
253
|
+
def update(self, ...): ...
|
|
254
|
+
def delete(self, ...): ...
|
|
255
|
+
|
|
256
|
+
class AuthService:
|
|
257
|
+
def authenticate(self, ...): ...
|
|
258
|
+
def reset_password(self, ...): ...
|
|
259
|
+
|
|
260
|
+
class UserNotifier:
|
|
261
|
+
def send_welcome_email(self, ...): ...
|
|
262
|
+
def send_password_reset_email(self, ...): ...
|
|
263
|
+
|
|
264
|
+
class UserReporter:
|
|
265
|
+
def generate_report(self, ...): ...
|
|
266
|
+
def export_to_csv(self, ...): ...
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Shotgun Surgery
|
|
272
|
+
|
|
273
|
+
**Detection heuristics:**
|
|
274
|
+
- Adding a new enum value requires changes in 5+ files
|
|
275
|
+
- A conceptual change (e.g., "add a new user role") touches model, serializer, validator, template, and tests in a scattered pattern
|
|
276
|
+
- Developers frequently forget to update one of the locations
|
|
277
|
+
|
|
278
|
+
### Before
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
# models.py
|
|
282
|
+
STATUSES = ["draft", "published", "archived"]
|
|
283
|
+
|
|
284
|
+
# serializer.py
|
|
285
|
+
if status not in ["draft", "published", "archived"]:
|
|
286
|
+
raise ValidationError("Invalid status")
|
|
287
|
+
|
|
288
|
+
# template.html
|
|
289
|
+
# {% if article.status == "draft" or article.status == "published" or ... %}
|
|
290
|
+
|
|
291
|
+
# admin.py
|
|
292
|
+
list_filter = ["draft", "published", "archived"]
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### After
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
import enum
|
|
299
|
+
|
|
300
|
+
# models.py — single source of truth
|
|
301
|
+
class ArticleStatus(enum.Enum):
|
|
302
|
+
DRAFT = "draft"
|
|
303
|
+
PUBLISHED = "published"
|
|
304
|
+
ARCHIVED = "archived"
|
|
305
|
+
|
|
306
|
+
# All other files reference ArticleStatus
|
|
307
|
+
# serializer.py
|
|
308
|
+
if status not in ArticleStatus.__members__: ...
|
|
309
|
+
|
|
310
|
+
# admin.py
|
|
311
|
+
list_filter = [s.value for s in ArticleStatus]
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Divergent Change and Message Chain
|
|
317
|
+
|
|
318
|
+
**Divergent Change:** One class changes for unrelated reasons. Split responsibilities.
|
|
319
|
+
|
|
320
|
+
**Message Chain:** Long chains like `a.getB().getC().getD()`. Introduce delegate methods:
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
# Before
|
|
324
|
+
city = order.customer.address.city
|
|
325
|
+
|
|
326
|
+
# After — Order provides the method
|
|
327
|
+
class Order:
|
|
328
|
+
def shipping_city(self):
|
|
329
|
+
return self.customer.address.city
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Each of these smells is a signal, not a verdict. Investigate the context before applying transformations mechanically.
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-checklist
|
|
3
|
+
description: >-
|
|
4
|
+
This skill should be used when the user asks to "check for security issues",
|
|
5
|
+
"audit this code for vulnerabilities", "scan for secrets", "review for
|
|
6
|
+
injection attacks", "check OWASP compliance", "find security bugs",
|
|
7
|
+
"detect hardcoded credentials", "review authentication logic",
|
|
8
|
+
"check for XSS", "audit dependencies for vulnerabilities",
|
|
9
|
+
or discusses security review, vulnerability scanning, OWASP Top 10,
|
|
10
|
+
secrets detection, SQL injection, command injection, or dependency auditing.
|
|
11
|
+
version: 0.1.0
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Security Checklist
|
|
15
|
+
|
|
16
|
+
## Mental Model
|
|
17
|
+
|
|
18
|
+
Security is **defense in depth** -- no single layer protects you. Assume every input is hostile, every dependency is compromised, every network boundary is breached. Then build layers so that when (not if) one fails, the next catches the attack.
|
|
19
|
+
|
|
20
|
+
The practical consequence: validate at every boundary, not just the outermost one. A function that accepts user input should validate it even if the caller "should have" validated it. Database queries should use parameterized statements even if the application layer "should have" sanitized the input. This redundancy is not waste -- it's the safety net.
|
|
21
|
+
|
|
22
|
+
Security review has three phases:
|
|
23
|
+
1. **Input boundaries** -- Where does external data enter the system? (HTTP requests, file uploads, CLI args, environment variables, database reads, message queues)
|
|
24
|
+
2. **Trust transitions** -- Where does data cross from untrusted to trusted? (Deserialization, template rendering, shell execution, SQL queries)
|
|
25
|
+
3. **Output boundaries** -- Where does data leave the system? (Logs, error messages, API responses, HTML rendering)
|
|
26
|
+
|
|
27
|
+
Every vulnerability exists at a boundary where untrusted data is treated as trusted.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## OWASP Top 10 Quick Reference (2021)
|
|
32
|
+
|
|
33
|
+
### A01: Broken Access Control
|
|
34
|
+
Users act outside their intended permissions. Check: Can user A access user B's resources by changing an ID in the URL? Are admin endpoints protected by role checks, not just authentication?
|
|
35
|
+
|
|
36
|
+
### A02: Cryptographic Failures
|
|
37
|
+
Sensitive data transmitted or stored without proper encryption. Check: Is TLS enforced? Are passwords hashed with bcrypt/argon2 (not MD5/SHA1)? Are API keys in environment variables (not source code)?
|
|
38
|
+
|
|
39
|
+
### A03: Injection
|
|
40
|
+
Untrusted data sent to an interpreter as part of a command or query. Covers SQL injection, NoSQL injection, OS command injection, LDAP injection. Check: Are all database queries parameterized? Is user input ever passed to `eval()`, `exec()`, or shell commands?
|
|
41
|
+
|
|
42
|
+
### A04: Insecure Design
|
|
43
|
+
Missing or ineffective security controls at the design level. Check: Is there rate limiting on authentication endpoints? Are business logic constraints enforced server-side?
|
|
44
|
+
|
|
45
|
+
### A05: Security Misconfiguration
|
|
46
|
+
Default credentials, unnecessary features enabled, overly permissive CORS, verbose error messages in production. Check: Are stack traces hidden in production? Is directory listing disabled? Are default accounts removed?
|
|
47
|
+
|
|
48
|
+
### A06: Vulnerable and Outdated Components
|
|
49
|
+
Using libraries with known vulnerabilities. Check: When were dependencies last updated? Are there known CVEs in the dependency tree?
|
|
50
|
+
|
|
51
|
+
### A07: Identification and Authentication Failures
|
|
52
|
+
Weak passwords allowed, missing brute-force protection, session tokens in URLs. Check: Is there account lockout or rate limiting? Are session tokens regenerated after login?
|
|
53
|
+
|
|
54
|
+
### A08: Software and Data Integrity Failures
|
|
55
|
+
Code or data from untrusted sources without verification. Check: Are CI/CD pipelines protected? Are dependencies verified by checksum or signature?
|
|
56
|
+
|
|
57
|
+
### A09: Security Logging and Monitoring Failures
|
|
58
|
+
Insufficient logging of security events. Check: Are login failures logged? Are access control failures logged? Is there alerting on anomalous patterns?
|
|
59
|
+
|
|
60
|
+
### A10: Server-Side Request Forgery (SSRF)
|
|
61
|
+
Application fetches a URL from user input without validation. Check: Are user-supplied URLs validated against an allowlist? Is the application prevented from accessing internal services?
|
|
62
|
+
|
|
63
|
+
> **Deep dive:** See `references/owasp-patterns.md` for detailed OWASP patterns with vulnerable and fixed code examples.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Language-Specific Vulnerability Patterns
|
|
68
|
+
|
|
69
|
+
### Python
|
|
70
|
+
|
|
71
|
+
**Dangerous deserialization:**
|
|
72
|
+
```python
|
|
73
|
+
# VULNERABLE: pickle executes arbitrary code on load
|
|
74
|
+
import pickle
|
|
75
|
+
data = pickle.loads(user_input) # Remote code execution
|
|
76
|
+
|
|
77
|
+
# SAFE: use JSON or a schema-validated format
|
|
78
|
+
import json
|
|
79
|
+
data = json.loads(user_input)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Code execution:**
|
|
83
|
+
```python
|
|
84
|
+
import ast
|
|
85
|
+
from typing import Any
|
|
86
|
+
|
|
87
|
+
# VULNERABLE: eval/exec on user input
|
|
88
|
+
result = eval(user_expression)
|
|
89
|
+
|
|
90
|
+
# SAFE: use ast.literal_eval for data, or a parser for expressions
|
|
91
|
+
def safe_eval(expression: str) -> Any:
|
|
92
|
+
return ast.literal_eval(expression) # only literals: strings, numbers, tuples, lists, dicts, bools, None
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Command injection:**
|
|
96
|
+
```python
|
|
97
|
+
# VULNERABLE: shell=True with user input
|
|
98
|
+
import subprocess
|
|
99
|
+
subprocess.run(f"grep {user_query} /var/log/app.log", shell=True)
|
|
100
|
+
|
|
101
|
+
# SAFE: pass args as list, never use shell=True with user input
|
|
102
|
+
subprocess.run(["grep", user_query, "/var/log/app.log"])
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**SQL injection:**
|
|
106
|
+
```python
|
|
107
|
+
# VULNERABLE: string formatting in queries
|
|
108
|
+
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
|
|
109
|
+
|
|
110
|
+
# SAFE: parameterized queries
|
|
111
|
+
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### JavaScript / TypeScript
|
|
115
|
+
|
|
116
|
+
**Prototype pollution:**
|
|
117
|
+
```javascript
|
|
118
|
+
// VULNERABLE: deep merge from user input
|
|
119
|
+
function merge(target, source) {
|
|
120
|
+
for (const key in source) {
|
|
121
|
+
target[key] = source[key]; // __proto__ can be overwritten
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// SAFE: validate keys, use Object.create(null), or use structuredClone
|
|
126
|
+
function safeMerge(target, source) {
|
|
127
|
+
for (const key of Object.keys(source)) {
|
|
128
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
129
|
+
target[key] = source[key];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**ReDoS (Regular Expression Denial of Service):**
|
|
135
|
+
```javascript
|
|
136
|
+
// VULNERABLE: catastrophic backtracking
|
|
137
|
+
const emailRegex = /^([a-zA-Z0-9]+\.)*[a-zA-Z0-9]+@([a-zA-Z0-9]+\.)+[a-zA-Z]{2,}$/;
|
|
138
|
+
|
|
139
|
+
// SAFE: use linear-time regex or a dedicated validation library
|
|
140
|
+
const { z } = require("zod");
|
|
141
|
+
const email = z.string().email();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Path traversal:**
|
|
145
|
+
```javascript
|
|
146
|
+
// VULNERABLE: user controls file path
|
|
147
|
+
const filePath = path.join("/uploads", req.params.filename);
|
|
148
|
+
|
|
149
|
+
// SAFE: resolve and verify the path stays within the allowed directory
|
|
150
|
+
const safePath = path.resolve("/uploads", req.params.filename);
|
|
151
|
+
if (!safePath.startsWith("/uploads/")) {
|
|
152
|
+
throw new Error("Path traversal detected");
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Go
|
|
157
|
+
|
|
158
|
+
**Integer overflow:**
|
|
159
|
+
```go
|
|
160
|
+
// VULNERABLE: unchecked conversion
|
|
161
|
+
length := int32(userProvidedInt64) // silent truncation
|
|
162
|
+
|
|
163
|
+
// SAFE: bounds check before conversion
|
|
164
|
+
if userProvidedInt64 > math.MaxInt32 || userProvidedInt64 < math.MinInt32 {
|
|
165
|
+
return fmt.Errorf("value out of range")
|
|
166
|
+
}
|
|
167
|
+
length := int32(userProvidedInt64)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Race conditions:**
|
|
171
|
+
```go
|
|
172
|
+
// VULNERABLE: unsynchronized map access
|
|
173
|
+
var cache = make(map[string]string) // data race in concurrent use
|
|
174
|
+
|
|
175
|
+
// SAFE: use sync.Map or mutex
|
|
176
|
+
var cache sync.Map
|
|
177
|
+
cache.Store("key", "value")
|
|
178
|
+
val, ok := cache.Load("key")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Secrets Detection
|
|
184
|
+
|
|
185
|
+
Scan code for accidentally committed secrets using these regex patterns:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# AWS Access Key
|
|
189
|
+
AKIA[0-9A-Z]{16}
|
|
190
|
+
|
|
191
|
+
# AWS Secret Key
|
|
192
|
+
(?i)aws(.{0,20})?(?-i)['\"][0-9a-zA-Z/+]{40}['\"]
|
|
193
|
+
|
|
194
|
+
# Generic API Key patterns
|
|
195
|
+
(?i)(api[_-]?key|apikey|api[_-]?secret)[\s]*[=:]\s*['\"][a-zA-Z0-9]{16,}['\"]
|
|
196
|
+
|
|
197
|
+
# GitHub Token
|
|
198
|
+
gh[pousr]_[A-Za-z0-9_]{36,}
|
|
199
|
+
|
|
200
|
+
# Generic high-entropy strings (base64, 32+ chars)
|
|
201
|
+
['\"][A-Za-z0-9+/]{32,}={0,2}['\"]
|
|
202
|
+
|
|
203
|
+
# Private keys
|
|
204
|
+
-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----
|
|
205
|
+
|
|
206
|
+
# Connection strings with passwords
|
|
207
|
+
(?i)(postgres|mysql|mongodb)://[^:]+:[^@]+@
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Prevention:**
|
|
211
|
+
- Use `.gitignore` to exclude `.env`, `*.pem`, `credentials.*`
|
|
212
|
+
- Add a pre-commit hook with tools like `gitleaks` or `detect-secrets`
|
|
213
|
+
- Store secrets in environment variables or a secrets manager, never in code
|
|
214
|
+
- If a secret is committed, rotate it immediately -- removing it from git history is not sufficient
|
|
215
|
+
|
|
216
|
+
> **Deep dive:** See `references/secrets-patterns.md` for comprehensive regex patterns and detection strategies.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Dependency Audit Commands
|
|
221
|
+
|
|
222
|
+
Run these commands regularly and in CI/CD pipelines:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# JavaScript/TypeScript (npm)
|
|
226
|
+
npm audit
|
|
227
|
+
npm audit --audit-level=high # fail only on high+ severity
|
|
228
|
+
npx audit-ci --high # CI-friendly wrapper
|
|
229
|
+
|
|
230
|
+
# Python
|
|
231
|
+
pip-audit # PyPI advisory database
|
|
232
|
+
pip-audit --fix # auto-fix where possible
|
|
233
|
+
safety check # alternative scanner
|
|
234
|
+
|
|
235
|
+
# Rust
|
|
236
|
+
cargo audit # RustSec advisory database
|
|
237
|
+
cargo deny check advisories # stricter policy engine
|
|
238
|
+
|
|
239
|
+
# Go
|
|
240
|
+
govulncheck ./... # official Go vulnerability checker
|
|
241
|
+
|
|
242
|
+
# Container images
|
|
243
|
+
trivy image myapp:latest # scan container for OS + app vulns
|
|
244
|
+
grype myapp:latest # alternative container scanner
|
|
245
|
+
|
|
246
|
+
# Multi-language / CI
|
|
247
|
+
trivy fs . # scan filesystem for all languages
|
|
248
|
+
snyk test # commercial scanner with free tier
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Best practices:**
|
|
252
|
+
- Run audits in CI -- fail the build on high/critical findings
|
|
253
|
+
- Pin dependency versions in lock files (`package-lock.json`, `poetry.lock`, `Cargo.lock`)
|
|
254
|
+
- Update dependencies weekly, not quarterly -- smaller updates are easier to review
|
|
255
|
+
- Subscribe to security advisories for critical dependencies
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Ambiguity Policy
|
|
260
|
+
|
|
261
|
+
These defaults apply when the user does not specify a preference. State the assumption when making a choice:
|
|
262
|
+
|
|
263
|
+
- **Scan scope:** Default to the current project directory. Do not scan `node_modules`, `venv`, or other dependency directories directly -- use audit tools for those.
|
|
264
|
+
- **Severity threshold:** Flag all findings but prioritize high and critical. Informational findings are reported but not treated as blockers.
|
|
265
|
+
- **Secrets in tests:** Flag hardcoded credentials in test files as warnings, not errors -- but recommend using fixtures or environment variables instead.
|
|
266
|
+
- **Dependency updates:** Recommend updating vulnerable dependencies to the latest patch version, not the latest major version, to minimize breaking changes.
|
|
267
|
+
- **False positives:** When a finding is a false positive, explain why and suggest adding it to an ignore list rather than silently skipping it.
|
|
268
|
+
- **Framework:** Default to the security patterns of the framework in use (Django ORM over raw SQL, Express middleware over manual checks).
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Reference Files
|
|
273
|
+
|
|
274
|
+
| File | Contents |
|
|
275
|
+
|------|----------|
|
|
276
|
+
| `references/owasp-patterns.md` | Detailed OWASP Top 10 patterns with vulnerable and fixed code examples for Python, JavaScript, and Go |
|
|
277
|
+
| `references/secrets-patterns.md` | Comprehensive regex patterns for detecting API keys, tokens, certificates, and connection strings, plus prevention strategies |
|