@qball-inc/the-bulwark 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/.claude-plugin/plugin.json +43 -0
- package/agents/bulwark-fix-validator.md +633 -0
- package/agents/bulwark-implementer.md +391 -0
- package/agents/bulwark-issue-analyzer.md +308 -0
- package/agents/bulwark-standards-reviewer.md +221 -0
- package/agents/plan-creation-architect.md +323 -0
- package/agents/plan-creation-eng-lead.md +352 -0
- package/agents/plan-creation-po.md +300 -0
- package/agents/plan-creation-qa-critic.md +334 -0
- package/agents/product-ideation-competitive-analyzer.md +298 -0
- package/agents/product-ideation-idea-validator.md +268 -0
- package/agents/product-ideation-market-researcher.md +292 -0
- package/agents/product-ideation-pattern-documenter.md +308 -0
- package/agents/product-ideation-segment-analyzer.md +303 -0
- package/agents/product-ideation-strategist.md +259 -0
- package/agents/statusline-setup.md +97 -0
- package/hooks/hooks.json +59 -0
- package/package.json +45 -0
- package/scripts/hooks/cleanup-stale.sh +13 -0
- package/scripts/hooks/enforce-quality.sh +166 -0
- package/scripts/hooks/implementer-quality.sh +256 -0
- package/scripts/hooks/inject-protocol.sh +52 -0
- package/scripts/hooks/suggest-pipeline.sh +175 -0
- package/scripts/hooks/track-pipeline-start.sh +37 -0
- package/scripts/hooks/track-pipeline-stop.sh +52 -0
- package/scripts/init-rules.sh +35 -0
- package/scripts/init.sh +151 -0
- package/skills/anthropic-validator/SKILL.md +607 -0
- package/skills/anthropic-validator/references/agents-checklist.md +131 -0
- package/skills/anthropic-validator/references/commands-checklist.md +102 -0
- package/skills/anthropic-validator/references/hooks-checklist.md +151 -0
- package/skills/anthropic-validator/references/mcp-checklist.md +136 -0
- package/skills/anthropic-validator/references/plugins-checklist.md +148 -0
- package/skills/anthropic-validator/references/skills-checklist.md +85 -0
- package/skills/assertion-patterns/SKILL.md +296 -0
- package/skills/bug-magnet-data/SKILL.md +284 -0
- package/skills/bug-magnet-data/context/cli-args.md +91 -0
- package/skills/bug-magnet-data/context/db-query.md +104 -0
- package/skills/bug-magnet-data/context/file-contents.md +103 -0
- package/skills/bug-magnet-data/context/http-body.md +91 -0
- package/skills/bug-magnet-data/context/process-spawn.md +123 -0
- package/skills/bug-magnet-data/data/booleans/boundaries.yaml +143 -0
- package/skills/bug-magnet-data/data/collections/arrays.yaml +114 -0
- package/skills/bug-magnet-data/data/collections/objects.yaml +123 -0
- package/skills/bug-magnet-data/data/concurrency/race-conditions.yaml +118 -0
- package/skills/bug-magnet-data/data/concurrency/state-machines.yaml +115 -0
- package/skills/bug-magnet-data/data/dates/boundaries.yaml +137 -0
- package/skills/bug-magnet-data/data/dates/invalid.yaml +132 -0
- package/skills/bug-magnet-data/data/dates/timezone.yaml +118 -0
- package/skills/bug-magnet-data/data/encoding/charset.yaml +79 -0
- package/skills/bug-magnet-data/data/encoding/normalization.yaml +105 -0
- package/skills/bug-magnet-data/data/formats/email.yaml +154 -0
- package/skills/bug-magnet-data/data/formats/json.yaml +187 -0
- package/skills/bug-magnet-data/data/formats/url.yaml +165 -0
- package/skills/bug-magnet-data/data/language-specific/javascript.yaml +182 -0
- package/skills/bug-magnet-data/data/language-specific/python.yaml +174 -0
- package/skills/bug-magnet-data/data/language-specific/rust.yaml +148 -0
- package/skills/bug-magnet-data/data/numbers/boundaries.yaml +161 -0
- package/skills/bug-magnet-data/data/numbers/precision.yaml +89 -0
- package/skills/bug-magnet-data/data/numbers/special.yaml +69 -0
- package/skills/bug-magnet-data/data/strings/boundaries.yaml +109 -0
- package/skills/bug-magnet-data/data/strings/injection.yaml +208 -0
- package/skills/bug-magnet-data/data/strings/special-chars.yaml +190 -0
- package/skills/bug-magnet-data/data/strings/unicode.yaml +139 -0
- package/skills/bug-magnet-data/references/external-lists.md +115 -0
- package/skills/bulwark-brainstorm/SKILL.md +563 -0
- package/skills/bulwark-brainstorm/references/at-teammate-prompts.md +60 -0
- package/skills/bulwark-brainstorm/references/role-critical-analyst.md +78 -0
- package/skills/bulwark-brainstorm/references/role-development-lead.md +66 -0
- package/skills/bulwark-brainstorm/references/role-product-delivery-lead.md +79 -0
- package/skills/bulwark-brainstorm/references/role-product-manager.md +62 -0
- package/skills/bulwark-brainstorm/references/role-project-sme.md +59 -0
- package/skills/bulwark-brainstorm/references/role-technical-architect.md +66 -0
- package/skills/bulwark-research/SKILL.md +298 -0
- package/skills/bulwark-research/references/viewpoint-contrarian.md +63 -0
- package/skills/bulwark-research/references/viewpoint-direct-investigation.md +62 -0
- package/skills/bulwark-research/references/viewpoint-first-principles.md +65 -0
- package/skills/bulwark-research/references/viewpoint-practitioner.md +62 -0
- package/skills/bulwark-research/references/viewpoint-prior-art.md +66 -0
- package/skills/bulwark-scaffold/SKILL.md +330 -0
- package/skills/bulwark-statusline/SKILL.md +161 -0
- package/skills/bulwark-statusline/scripts/statusline.sh +144 -0
- package/skills/bulwark-verify/SKILL.md +519 -0
- package/skills/code-review/SKILL.md +428 -0
- package/skills/code-review/examples/anti-patterns/linting.ts +181 -0
- package/skills/code-review/examples/anti-patterns/security.ts +91 -0
- package/skills/code-review/examples/anti-patterns/standards.ts +195 -0
- package/skills/code-review/examples/anti-patterns/type-safety.ts +108 -0
- package/skills/code-review/examples/recommended/linting.ts +195 -0
- package/skills/code-review/examples/recommended/security.ts +154 -0
- package/skills/code-review/examples/recommended/standards.ts +231 -0
- package/skills/code-review/examples/recommended/type-safety.ts +181 -0
- package/skills/code-review/frameworks/angular.md +218 -0
- package/skills/code-review/frameworks/django.md +235 -0
- package/skills/code-review/frameworks/express.md +207 -0
- package/skills/code-review/frameworks/flask.md +298 -0
- package/skills/code-review/frameworks/generic.md +146 -0
- package/skills/code-review/frameworks/react.md +152 -0
- package/skills/code-review/frameworks/vue.md +244 -0
- package/skills/code-review/references/linting-patterns.md +221 -0
- package/skills/code-review/references/security-patterns.md +125 -0
- package/skills/code-review/references/standards-patterns.md +246 -0
- package/skills/code-review/references/type-safety-patterns.md +130 -0
- package/skills/component-patterns/SKILL.md +131 -0
- package/skills/component-patterns/references/pattern-cli-command.md +118 -0
- package/skills/component-patterns/references/pattern-database.md +166 -0
- package/skills/component-patterns/references/pattern-external-api.md +139 -0
- package/skills/component-patterns/references/pattern-file-parser.md +168 -0
- package/skills/component-patterns/references/pattern-http-server.md +162 -0
- package/skills/component-patterns/references/pattern-process-spawner.md +133 -0
- package/skills/continuous-feedback/SKILL.md +327 -0
- package/skills/continuous-feedback/references/collect-instructions.md +81 -0
- package/skills/continuous-feedback/references/specialize-code-review.md +82 -0
- package/skills/continuous-feedback/references/specialize-general.md +98 -0
- package/skills/continuous-feedback/references/specialize-test-audit.md +81 -0
- package/skills/create-skill/SKILL.md +359 -0
- package/skills/create-skill/references/agent-conventions.md +194 -0
- package/skills/create-skill/references/agent-template.md +195 -0
- package/skills/create-skill/references/content-guidance.md +291 -0
- package/skills/create-skill/references/decision-framework.md +124 -0
- package/skills/create-skill/references/template-pipeline.md +217 -0
- package/skills/create-skill/references/template-reference-heavy.md +111 -0
- package/skills/create-skill/references/template-research.md +210 -0
- package/skills/create-skill/references/template-script-driven.md +172 -0
- package/skills/create-skill/references/template-simple.md +80 -0
- package/skills/create-subagent/SKILL.md +353 -0
- package/skills/create-subagent/references/agent-conventions.md +268 -0
- package/skills/create-subagent/references/content-guidance.md +232 -0
- package/skills/create-subagent/references/decision-framework.md +134 -0
- package/skills/create-subagent/references/template-single-agent.md +192 -0
- package/skills/fix-bug/SKILL.md +241 -0
- package/skills/governance-protocol/SKILL.md +116 -0
- package/skills/init/SKILL.md +341 -0
- package/skills/issue-debugging/SKILL.md +385 -0
- package/skills/issue-debugging/references/anti-patterns.md +245 -0
- package/skills/issue-debugging/references/debug-report-schema.md +227 -0
- package/skills/mock-detection/SKILL.md +511 -0
- package/skills/mock-detection/references/false-positive-prevention.md +402 -0
- package/skills/mock-detection/references/stub-patterns.md +236 -0
- package/skills/pipeline-templates/SKILL.md +215 -0
- package/skills/pipeline-templates/references/code-change-workflow.md +277 -0
- package/skills/pipeline-templates/references/code-review.md +336 -0
- package/skills/pipeline-templates/references/fix-validation.md +421 -0
- package/skills/pipeline-templates/references/new-feature.md +335 -0
- package/skills/pipeline-templates/references/research-brainstorm.md +161 -0
- package/skills/pipeline-templates/references/research-planning.md +257 -0
- package/skills/pipeline-templates/references/test-audit.md +389 -0
- package/skills/pipeline-templates/references/test-execution-fix.md +238 -0
- package/skills/plan-creation/SKILL.md +497 -0
- package/skills/product-ideation/SKILL.md +372 -0
- package/skills/product-ideation/references/analysis-frameworks.md +161 -0
- package/skills/session-handoff/SKILL.md +139 -0
- package/skills/session-handoff/references/examples.md +223 -0
- package/skills/setup-lsp/SKILL.md +312 -0
- package/skills/setup-lsp/references/server-registry.md +85 -0
- package/skills/setup-lsp/references/troubleshooting.md +135 -0
- package/skills/subagent-output-templating/SKILL.md +415 -0
- package/skills/subagent-output-templating/references/examples.md +440 -0
- package/skills/subagent-prompting/SKILL.md +364 -0
- package/skills/subagent-prompting/references/examples.md +342 -0
- package/skills/test-audit/SKILL.md +531 -0
- package/skills/test-audit/references/known-limitations.md +41 -0
- package/skills/test-audit/references/priority-classification.md +30 -0
- package/skills/test-audit/references/prompts/deep-mode-detection.md +83 -0
- package/skills/test-audit/references/prompts/synthesis.md +57 -0
- package/skills/test-audit/references/rewrite-instructions.md +46 -0
- package/skills/test-audit/references/schemas/audit-output.yaml +100 -0
- package/skills/test-audit/references/schemas/diagnostic-output.yaml +49 -0
- package/skills/test-audit/scripts/data-flow-analyzer.ts +509 -0
- package/skills/test-audit/scripts/integration-mock-detector.ts +462 -0
- package/skills/test-audit/scripts/package.json +20 -0
- package/skills/test-audit/scripts/skip-detector.ts +211 -0
- package/skills/test-audit/scripts/verification-counter.ts +295 -0
- package/skills/test-classification/SKILL.md +310 -0
- package/skills/test-fixture-creation/SKILL.md +295 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Django Framework Patterns
|
|
2
|
+
|
|
3
|
+
Security and quality patterns specific to Django applications.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Security Patterns
|
|
8
|
+
|
|
9
|
+
### SQL Injection
|
|
10
|
+
|
|
11
|
+
| Pattern | Risk | Detection |
|
|
12
|
+
|---------|------|-----------|
|
|
13
|
+
| `.raw()` with string format | Critical | `Model.objects.raw(f"SELECT...")` |
|
|
14
|
+
| `.extra()` with user input | Critical | `.extra(where=[user_input])` |
|
|
15
|
+
| Cursor with string concat | Critical | `cursor.execute(query % params)` |
|
|
16
|
+
|
|
17
|
+
**Safe Pattern:**
|
|
18
|
+
```python
|
|
19
|
+
# BAD
|
|
20
|
+
User.objects.raw(f"SELECT * FROM users WHERE id = {user_id}")
|
|
21
|
+
|
|
22
|
+
# GOOD - Parameterized query
|
|
23
|
+
User.objects.raw("SELECT * FROM users WHERE id = %s", [user_id])
|
|
24
|
+
|
|
25
|
+
# BEST - Use ORM
|
|
26
|
+
User.objects.filter(id=user_id)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### XSS Prevention
|
|
30
|
+
|
|
31
|
+
| Pattern | Risk | Detection |
|
|
32
|
+
|---------|------|-----------|
|
|
33
|
+
| `|safe` filter | High | `{{ user_input|safe }}` |
|
|
34
|
+
| `mark_safe()` | High | `mark_safe(user_content)` |
|
|
35
|
+
| `{% autoescape off %}` | High | Block disabling autoescape |
|
|
36
|
+
|
|
37
|
+
**Safe Pattern:**
|
|
38
|
+
```html
|
|
39
|
+
<!-- BAD -->
|
|
40
|
+
{{ user_comment|safe }}
|
|
41
|
+
|
|
42
|
+
<!-- GOOD - Django auto-escapes by default -->
|
|
43
|
+
{{ user_comment }}
|
|
44
|
+
|
|
45
|
+
<!-- If HTML needed, sanitize first -->
|
|
46
|
+
{% load bleach_tags %}
|
|
47
|
+
{{ user_comment|bleach }}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### CSRF Protection
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
# Ensure CSRF middleware is enabled
|
|
54
|
+
MIDDLEWARE = [
|
|
55
|
+
'django.middleware.csrf.CsrfViewMiddleware',
|
|
56
|
+
# ...
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
# In templates
|
|
60
|
+
<form method="post">
|
|
61
|
+
{% csrf_token %}
|
|
62
|
+
...
|
|
63
|
+
</form>
|
|
64
|
+
|
|
65
|
+
# For AJAX
|
|
66
|
+
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Authentication/Authorization
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
# BAD - No permission check
|
|
73
|
+
def get_document(request, doc_id):
|
|
74
|
+
return Document.objects.get(id=doc_id)
|
|
75
|
+
|
|
76
|
+
# GOOD - Check ownership
|
|
77
|
+
from django.core.exceptions import PermissionDenied
|
|
78
|
+
|
|
79
|
+
def get_document(request, doc_id):
|
|
80
|
+
doc = Document.objects.get(id=doc_id)
|
|
81
|
+
if doc.owner_id != request.user.id:
|
|
82
|
+
raise PermissionDenied
|
|
83
|
+
return doc
|
|
84
|
+
|
|
85
|
+
# BEST - Use built-in permissions
|
|
86
|
+
from django.contrib.auth.decorators import permission_required
|
|
87
|
+
|
|
88
|
+
@permission_required('app.view_document')
|
|
89
|
+
def get_document(request, doc_id):
|
|
90
|
+
return Document.objects.get(id=doc_id)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Secret Management
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# BAD
|
|
97
|
+
SECRET_KEY = 'django-insecure-abc123'
|
|
98
|
+
DATABASE_PASSWORD = 'password123'
|
|
99
|
+
|
|
100
|
+
# GOOD
|
|
101
|
+
import os
|
|
102
|
+
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
|
|
103
|
+
DATABASE_PASSWORD = os.environ['DB_PASSWORD']
|
|
104
|
+
|
|
105
|
+
# Or use django-environ
|
|
106
|
+
import environ
|
|
107
|
+
env = environ.Env()
|
|
108
|
+
SECRET_KEY = env('SECRET_KEY')
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Type Safety Patterns
|
|
114
|
+
|
|
115
|
+
### Model Typing
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# BAD - No type hints
|
|
119
|
+
class User(models.Model):
|
|
120
|
+
name = models.CharField(max_length=100)
|
|
121
|
+
|
|
122
|
+
def get_display_name(self):
|
|
123
|
+
return self.name.title()
|
|
124
|
+
|
|
125
|
+
# GOOD - With type hints
|
|
126
|
+
from typing import Optional
|
|
127
|
+
|
|
128
|
+
class User(models.Model):
|
|
129
|
+
name: models.CharField[str] = models.CharField(max_length=100)
|
|
130
|
+
|
|
131
|
+
def get_display_name(self) -> str:
|
|
132
|
+
return self.name.title()
|
|
133
|
+
|
|
134
|
+
def get_email(self) -> Optional[str]:
|
|
135
|
+
return self.email if self.email else None
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### View Typing
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from django.http import HttpRequest, HttpResponse, JsonResponse
|
|
142
|
+
from django.views import View
|
|
143
|
+
|
|
144
|
+
# Function-based view
|
|
145
|
+
def user_detail(request: HttpRequest, user_id: int) -> HttpResponse:
|
|
146
|
+
user = get_object_or_404(User, id=user_id)
|
|
147
|
+
return render(request, 'user_detail.html', {'user': user})
|
|
148
|
+
|
|
149
|
+
# Class-based view
|
|
150
|
+
class UserDetailView(View):
|
|
151
|
+
def get(self, request: HttpRequest, user_id: int) -> HttpResponse:
|
|
152
|
+
user = get_object_or_404(User, id=user_id)
|
|
153
|
+
return render(request, 'user_detail.html', {'user': user})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Form Typing
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from django import forms
|
|
160
|
+
from typing import Any, Dict
|
|
161
|
+
|
|
162
|
+
class UserForm(forms.Form):
|
|
163
|
+
name = forms.CharField(max_length=100)
|
|
164
|
+
email = forms.EmailField()
|
|
165
|
+
|
|
166
|
+
def clean(self) -> Dict[str, Any]:
|
|
167
|
+
cleaned_data = super().clean()
|
|
168
|
+
# Type-safe access to cleaned data
|
|
169
|
+
name: str = cleaned_data.get('name', '')
|
|
170
|
+
return cleaned_data
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Linting Patterns
|
|
176
|
+
|
|
177
|
+
### Query Optimization
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
# BAD - N+1 query problem
|
|
181
|
+
users = User.objects.all()
|
|
182
|
+
for user in users:
|
|
183
|
+
print(user.profile.avatar) # Query per user
|
|
184
|
+
|
|
185
|
+
# GOOD - Prefetch related
|
|
186
|
+
users = User.objects.select_related('profile').all()
|
|
187
|
+
for user in users:
|
|
188
|
+
print(user.profile.avatar) # No additional queries
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### View Complexity
|
|
192
|
+
|
|
193
|
+
| Metric | Threshold | Action |
|
|
194
|
+
|--------|-----------|--------|
|
|
195
|
+
| View function lines | >50 | Extract to service layer |
|
|
196
|
+
| Business logic in view | Any | Move to model or service |
|
|
197
|
+
| Multiple model operations | >3 | Create service class |
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Coding Standards
|
|
202
|
+
|
|
203
|
+
### Naming Conventions
|
|
204
|
+
|
|
205
|
+
| Element | Convention | Example |
|
|
206
|
+
|---------|------------|---------|
|
|
207
|
+
| Model | PascalCase | `UserProfile` |
|
|
208
|
+
| View function | snake_case | `user_detail` |
|
|
209
|
+
| View class | PascalCase + View suffix | `UserDetailView` |
|
|
210
|
+
| URL name | snake_case with dashes | `user-detail` |
|
|
211
|
+
|
|
212
|
+
### File Organization
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
app/
|
|
216
|
+
models/
|
|
217
|
+
__init__.py
|
|
218
|
+
user.py
|
|
219
|
+
views/
|
|
220
|
+
__init__.py
|
|
221
|
+
user.py
|
|
222
|
+
services/
|
|
223
|
+
user_service.py
|
|
224
|
+
tests/
|
|
225
|
+
test_user.py
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## What to Skip
|
|
231
|
+
|
|
232
|
+
- Django admin customization
|
|
233
|
+
- Management commands
|
|
234
|
+
- Migration files
|
|
235
|
+
- Settings for different environments
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Express/Node Framework Patterns
|
|
2
|
+
|
|
3
|
+
Security and quality patterns specific to Express.js and Node.js applications.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Security Patterns
|
|
8
|
+
|
|
9
|
+
### Injection Attacks
|
|
10
|
+
|
|
11
|
+
| Pattern | Risk | Detection |
|
|
12
|
+
|---------|------|-----------|
|
|
13
|
+
| SQL in string concat | Critical | `query + req.body.x` |
|
|
14
|
+
| NoSQL operator injection | Critical | `{ $where: userInput }` |
|
|
15
|
+
| Command injection | Critical | `exec(userInput)` |
|
|
16
|
+
| Path traversal | High | `path.join(base, userInput)` |
|
|
17
|
+
|
|
18
|
+
**Safe Patterns:**
|
|
19
|
+
```typescript
|
|
20
|
+
// SQL: Use parameterized queries
|
|
21
|
+
const user = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
|
|
22
|
+
|
|
23
|
+
// NoSQL: Sanitize operators
|
|
24
|
+
import mongoSanitize from 'express-mongo-sanitize';
|
|
25
|
+
app.use(mongoSanitize());
|
|
26
|
+
|
|
27
|
+
// Commands: Use array form
|
|
28
|
+
const { spawn } = require('child_process');
|
|
29
|
+
spawn('ls', ['-la', sanitizedPath]); // Not: exec(`ls -la ${path}`)
|
|
30
|
+
|
|
31
|
+
// Paths: Validate within allowed directory
|
|
32
|
+
const safePath = path.resolve(uploadDir, userFilename);
|
|
33
|
+
if (!safePath.startsWith(uploadDir)) throw new Error('Invalid path');
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Authentication/Authorization
|
|
37
|
+
|
|
38
|
+
| Pattern | Risk | Detection |
|
|
39
|
+
|---------|------|-----------|
|
|
40
|
+
| Missing auth middleware | Critical | Route without `isAuthenticated` |
|
|
41
|
+
| JWT in URL | High | Token in query string |
|
|
42
|
+
| Weak session config | Medium | Missing secure/httpOnly flags |
|
|
43
|
+
|
|
44
|
+
**Safe Pattern:**
|
|
45
|
+
```typescript
|
|
46
|
+
// Middleware chain
|
|
47
|
+
router.get('/admin',
|
|
48
|
+
isAuthenticated, // Verify JWT/session
|
|
49
|
+
requireRole('admin'), // Check role
|
|
50
|
+
adminController.dashboard
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Session config
|
|
54
|
+
app.use(session({
|
|
55
|
+
secret: process.env.SESSION_SECRET,
|
|
56
|
+
cookie: {
|
|
57
|
+
secure: true, // HTTPS only
|
|
58
|
+
httpOnly: true, // No JS access
|
|
59
|
+
sameSite: 'strict' // CSRF protection
|
|
60
|
+
}
|
|
61
|
+
}));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Input Validation
|
|
65
|
+
|
|
66
|
+
| Pattern | Risk | Detection |
|
|
67
|
+
|---------|------|-----------|
|
|
68
|
+
| Unvalidated body | High | Direct use of req.body |
|
|
69
|
+
| Missing sanitization | Medium | No express-validator/joi |
|
|
70
|
+
| Type coercion issues | Medium | `req.query.id` used as number |
|
|
71
|
+
|
|
72
|
+
**Safe Pattern:**
|
|
73
|
+
```typescript
|
|
74
|
+
import { body, validationResult } from 'express-validator';
|
|
75
|
+
|
|
76
|
+
router.post('/users',
|
|
77
|
+
body('email').isEmail().normalizeEmail(),
|
|
78
|
+
body('password').isLength({ min: 8 }),
|
|
79
|
+
(req, res) => {
|
|
80
|
+
const errors = validationResult(req);
|
|
81
|
+
if (!errors.isEmpty()) {
|
|
82
|
+
return res.status(400).json({ errors: errors.array() });
|
|
83
|
+
}
|
|
84
|
+
// Proceed with validated data
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Security Headers
|
|
90
|
+
|
|
91
|
+
| Header | Purpose | Check |
|
|
92
|
+
|--------|---------|-------|
|
|
93
|
+
| Content-Security-Policy | XSS prevention | `helmet.contentSecurityPolicy()` |
|
|
94
|
+
| X-Frame-Options | Clickjacking prevention | `helmet.frameguard()` |
|
|
95
|
+
| Strict-Transport-Security | Force HTTPS | `helmet.hsts()` |
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import helmet from 'helmet';
|
|
99
|
+
app.use(helmet()); // Enables all security headers
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Type Safety Patterns
|
|
105
|
+
|
|
106
|
+
### Request Typing
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// BAD: Untyped request
|
|
110
|
+
app.post('/users', (req, res) => {
|
|
111
|
+
const { name, email } = req.body; // any
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// GOOD: Typed request
|
|
115
|
+
interface CreateUserBody {
|
|
116
|
+
name: string;
|
|
117
|
+
email: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
app.post('/users', (req: Request<{}, {}, CreateUserBody>, res) => {
|
|
121
|
+
const { name, email } = req.body; // Typed
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Response Typing
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Define response types
|
|
129
|
+
interface ApiResponse<T> {
|
|
130
|
+
success: boolean;
|
|
131
|
+
data?: T;
|
|
132
|
+
error?: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function sendSuccess<T>(res: Response, data: T): void {
|
|
136
|
+
res.json({ success: true, data } as ApiResponse<T>);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function sendError(res: Response, status: number, error: string): void {
|
|
140
|
+
res.status(status).json({ success: false, error } as ApiResponse<never>);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Error Handling
|
|
147
|
+
|
|
148
|
+
### Async Error Handling
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// BAD: Unhandled promise rejection
|
|
152
|
+
app.get('/users', async (req, res) => {
|
|
153
|
+
const users = await db.users.findAll(); // If throws, crashes
|
|
154
|
+
res.json(users);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// GOOD: Wrapped async handler
|
|
158
|
+
const asyncHandler = (fn: RequestHandler) => (req, res, next) =>
|
|
159
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
160
|
+
|
|
161
|
+
app.get('/users', asyncHandler(async (req, res) => {
|
|
162
|
+
const users = await db.users.findAll();
|
|
163
|
+
res.json(users);
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
// Error middleware
|
|
167
|
+
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
168
|
+
console.error(err);
|
|
169
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Linting Patterns
|
|
176
|
+
|
|
177
|
+
### Middleware Order
|
|
178
|
+
|
|
179
|
+
Correct order:
|
|
180
|
+
1. Security middleware (helmet, cors)
|
|
181
|
+
2. Body parsers
|
|
182
|
+
3. Session/auth
|
|
183
|
+
4. Routes
|
|
184
|
+
5. Error handlers
|
|
185
|
+
|
|
186
|
+
### Route Organization
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// routes/users.ts
|
|
190
|
+
const router = Router();
|
|
191
|
+
router.get('/', userController.list);
|
|
192
|
+
router.get('/:id', userController.get);
|
|
193
|
+
router.post('/', userController.create);
|
|
194
|
+
export default router;
|
|
195
|
+
|
|
196
|
+
// app.ts
|
|
197
|
+
app.use('/api/users', usersRouter);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## What to Skip
|
|
203
|
+
|
|
204
|
+
- Development-only middleware (`morgan` in dev mode)
|
|
205
|
+
- Test fixtures with intentional vulnerabilities
|
|
206
|
+
- Mock servers for testing
|
|
207
|
+
- Documentation routes (Swagger)
|