@comfanion/workflow 4.36.45 → 4.36.46
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/package.json +1 -1
- package/src/build-info.json +2 -2
- package/src/opencode/FLOW.yaml +34 -187
- package/src/opencode/agents/reviewer.md +170 -0
- package/src/opencode/commands/review-story.md +134 -0
- package/src/opencode/config.yaml +5 -0
- package/src/opencode/plugins/custom-compaction.ts +369 -88
- package/src/opencode/skills/coding-standards/template-security.md +325 -0
- package/src/opencode/skills/dev-story/SKILL.md +53 -5
- package/src/opencode/skills/story-writing/template.md +16 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Security Standards
|
|
2
|
+
|
|
3
|
+
**Project:** {{project_name}}
|
|
4
|
+
**Version:** 1.0
|
|
5
|
+
**Last Updated:** {{date}}
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Security is a first-class concern in this project. Every developer must follow these guidelines.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## OWASP Top 10 Mitigations
|
|
16
|
+
|
|
17
|
+
### 1. Injection (SQL, NoSQL, Command)
|
|
18
|
+
|
|
19
|
+
**Rule:** Never concatenate user input into queries.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// ❌ BAD - SQL Injection
|
|
23
|
+
const query = `SELECT * FROM users WHERE id = '${userId}'`;
|
|
24
|
+
|
|
25
|
+
// ✅ GOOD - Parameterized query
|
|
26
|
+
const query = `SELECT * FROM users WHERE id = $1`;
|
|
27
|
+
await db.query(query, [userId]);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Broken Authentication
|
|
31
|
+
|
|
32
|
+
**Rules:**
|
|
33
|
+
- Use industry-standard auth (OAuth2, JWT)
|
|
34
|
+
- Implement rate limiting on auth endpoints
|
|
35
|
+
- Use secure password hashing (bcrypt, Argon2)
|
|
36
|
+
- Session tokens must be unpredictable
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// ✅ GOOD - Secure password hashing
|
|
40
|
+
import { hash, verify } from 'argon2';
|
|
41
|
+
const hashedPassword = await hash(password);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Sensitive Data Exposure
|
|
45
|
+
|
|
46
|
+
**Rules:**
|
|
47
|
+
- Encrypt sensitive data at rest
|
|
48
|
+
- Use HTTPS for all communications
|
|
49
|
+
- Never log PII, passwords, tokens, or API keys
|
|
50
|
+
- Mask sensitive data in error messages
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// ❌ BAD - Logging sensitive data
|
|
54
|
+
console.log(`User login: ${email}, password: ${password}`);
|
|
55
|
+
|
|
56
|
+
// ✅ GOOD - Masked logging
|
|
57
|
+
console.log(`User login attempt: ${email.slice(0, 3)}***`);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 4. XML External Entities (XXE)
|
|
61
|
+
|
|
62
|
+
**Rule:** Disable external entity processing in XML parsers.
|
|
63
|
+
|
|
64
|
+
### 5. Broken Access Control
|
|
65
|
+
|
|
66
|
+
**Rules:**
|
|
67
|
+
- Verify user owns the resource before access
|
|
68
|
+
- Use principle of least privilege
|
|
69
|
+
- Deny by default
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// ✅ GOOD - Verify ownership
|
|
73
|
+
async function getTask(userId: string, taskId: string) {
|
|
74
|
+
const task = await db.findTask(taskId);
|
|
75
|
+
if (task.ownerId !== userId) {
|
|
76
|
+
throw new ForbiddenError('Access denied');
|
|
77
|
+
}
|
|
78
|
+
return task;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 6. Security Misconfiguration
|
|
83
|
+
|
|
84
|
+
**Rules:**
|
|
85
|
+
- Remove default credentials
|
|
86
|
+
- Disable debug mode in production
|
|
87
|
+
- Keep dependencies updated
|
|
88
|
+
- Use security headers (CSP, HSTS, X-Frame-Options)
|
|
89
|
+
|
|
90
|
+
### 7. Cross-Site Scripting (XSS)
|
|
91
|
+
|
|
92
|
+
**Rules:**
|
|
93
|
+
- Escape all output
|
|
94
|
+
- Use Content Security Policy
|
|
95
|
+
- Validate and sanitize HTML input
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// ✅ GOOD - Escape output (React does this by default)
|
|
99
|
+
return <div>{userInput}</div>;
|
|
100
|
+
|
|
101
|
+
// ⚠️ DANGEROUS - Only if absolutely necessary
|
|
102
|
+
return <div dangerouslySetInnerHTML={{__html: sanitized}} />;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 8. Insecure Deserialization
|
|
106
|
+
|
|
107
|
+
**Rule:** Validate deserialized data, use safe parsers.
|
|
108
|
+
|
|
109
|
+
### 9. Using Components with Known Vulnerabilities
|
|
110
|
+
|
|
111
|
+
**Rules:**
|
|
112
|
+
- Run `npm audit` / `snyk` regularly
|
|
113
|
+
- Keep dependencies updated
|
|
114
|
+
- Remove unused dependencies
|
|
115
|
+
|
|
116
|
+
### 10. Insufficient Logging & Monitoring
|
|
117
|
+
|
|
118
|
+
**Rules:**
|
|
119
|
+
- Log security events (login, logout, failed auth)
|
|
120
|
+
- Include timestamp, user ID, action, result
|
|
121
|
+
- Don't log sensitive data
|
|
122
|
+
- Set up alerts for suspicious activity
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Input Validation
|
|
127
|
+
|
|
128
|
+
### All User Input Must Be Validated
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { z } from 'zod';
|
|
132
|
+
|
|
133
|
+
const CreateUserSchema = z.object({
|
|
134
|
+
email: z.string().email().max(255),
|
|
135
|
+
name: z.string().min(1).max(100),
|
|
136
|
+
age: z.number().int().min(0).max(150),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
function createUser(input: unknown) {
|
|
140
|
+
const validated = CreateUserSchema.parse(input);
|
|
141
|
+
// Now safe to use
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Validate At System Boundaries
|
|
146
|
+
|
|
147
|
+
- HTTP handlers (API input)
|
|
148
|
+
- Message queue consumers
|
|
149
|
+
- File uploads
|
|
150
|
+
- External API responses
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Secrets Management
|
|
155
|
+
|
|
156
|
+
### Never Hardcode Secrets
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// ❌ BAD
|
|
160
|
+
const apiKey = 'sk-1234567890abcdef';
|
|
161
|
+
|
|
162
|
+
// ✅ GOOD
|
|
163
|
+
const apiKey = process.env.API_KEY;
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Environment Variables
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# .env.example (commit this)
|
|
170
|
+
DATABASE_URL=
|
|
171
|
+
API_KEY=
|
|
172
|
+
JWT_SECRET=
|
|
173
|
+
|
|
174
|
+
# .env (NEVER commit)
|
|
175
|
+
DATABASE_URL=postgres://...
|
|
176
|
+
API_KEY=sk-...
|
|
177
|
+
JWT_SECRET=...
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Gitignore
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
# Always ignore
|
|
184
|
+
.env
|
|
185
|
+
.env.local
|
|
186
|
+
*.pem
|
|
187
|
+
*.key
|
|
188
|
+
secrets/
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Authentication & Authorization
|
|
194
|
+
|
|
195
|
+
### JWT Best Practices
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// ✅ GOOD JWT configuration
|
|
199
|
+
const token = jwt.sign(
|
|
200
|
+
{ userId, role },
|
|
201
|
+
process.env.JWT_SECRET,
|
|
202
|
+
{
|
|
203
|
+
expiresIn: '1h', // Short expiration
|
|
204
|
+
algorithm: 'HS256', // Or RS256 for asymmetric
|
|
205
|
+
issuer: 'our-app',
|
|
206
|
+
audience: 'our-app',
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Authorization Middleware
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// ✅ GOOD - Check permissions before action
|
|
215
|
+
async function deleteTask(user: User, taskId: string) {
|
|
216
|
+
const task = await taskRepo.find(taskId);
|
|
217
|
+
|
|
218
|
+
if (!task) throw new NotFoundError();
|
|
219
|
+
if (task.ownerId !== user.id && !user.isAdmin) {
|
|
220
|
+
throw new ForbiddenError();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
await taskRepo.delete(taskId);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## API Security
|
|
230
|
+
|
|
231
|
+
### Rate Limiting
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// Apply to auth endpoints
|
|
235
|
+
app.use('/api/auth', rateLimit({
|
|
236
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
237
|
+
max: 10, // 10 requests per window
|
|
238
|
+
}));
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Security Headers
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import helmet from 'helmet';
|
|
245
|
+
|
|
246
|
+
app.use(helmet({
|
|
247
|
+
contentSecurityPolicy: true,
|
|
248
|
+
crossOriginEmbedderPolicy: true,
|
|
249
|
+
crossOriginOpenerPolicy: true,
|
|
250
|
+
crossOriginResourcePolicy: true,
|
|
251
|
+
dnsPrefetchControl: true,
|
|
252
|
+
frameguard: true,
|
|
253
|
+
hidePoweredBy: true,
|
|
254
|
+
hsts: true,
|
|
255
|
+
ieNoOpen: true,
|
|
256
|
+
noSniff: true,
|
|
257
|
+
originAgentCluster: true,
|
|
258
|
+
permittedCrossDomainPolicies: true,
|
|
259
|
+
referrerPolicy: true,
|
|
260
|
+
xssFilter: true,
|
|
261
|
+
}));
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Database Security
|
|
267
|
+
|
|
268
|
+
### Parameterized Queries
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// ✅ Always use parameterized queries
|
|
272
|
+
const result = await db.query(
|
|
273
|
+
'SELECT * FROM users WHERE email = $1 AND active = $2',
|
|
274
|
+
[email, true]
|
|
275
|
+
);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Principle of Least Privilege
|
|
279
|
+
|
|
280
|
+
- App database user should NOT have DROP, CREATE permissions
|
|
281
|
+
- Use read-only replicas for reporting
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## File Upload Security
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// ✅ GOOD - Validate uploads
|
|
289
|
+
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
|
|
290
|
+
const maxSize = 5 * 1024 * 1024; // 5MB
|
|
291
|
+
|
|
292
|
+
function validateUpload(file: UploadedFile) {
|
|
293
|
+
if (!allowedTypes.includes(file.mimetype)) {
|
|
294
|
+
throw new BadRequestError('Invalid file type');
|
|
295
|
+
}
|
|
296
|
+
if (file.size > maxSize) {
|
|
297
|
+
throw new BadRequestError('File too large');
|
|
298
|
+
}
|
|
299
|
+
// Generate random filename to prevent path traversal
|
|
300
|
+
const safeFilename = `${uuid()}.${extension}`;
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Security Checklist for Code Review
|
|
307
|
+
|
|
308
|
+
- [ ] No hardcoded secrets or API keys
|
|
309
|
+
- [ ] All user inputs validated
|
|
310
|
+
- [ ] Using parameterized queries
|
|
311
|
+
- [ ] Authentication required on protected endpoints
|
|
312
|
+
- [ ] Authorization checks before data access
|
|
313
|
+
- [ ] Sensitive data not logged
|
|
314
|
+
- [ ] Error messages don't leak internal details
|
|
315
|
+
- [ ] Dependencies don't have known vulnerabilities
|
|
316
|
+
- [ ] Security headers configured
|
|
317
|
+
- [ ] Rate limiting on sensitive endpoints
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Changelog
|
|
322
|
+
|
|
323
|
+
| Version | Date | Author | Changes |
|
|
324
|
+
|---------|------|--------|---------|
|
|
325
|
+
| 1.0 | {{date}} | {{author}} | Initial security standards |
|
|
@@ -489,11 +489,57 @@ Read from `config.yaml → development.methodology`:
|
|
|
489
489
|
- Tasks completed: {{completed_tasks_count}}
|
|
490
490
|
- Tests added: {{new_tests_count}}
|
|
491
491
|
- Files changed: {{changed_files_count}}
|
|
492
|
-
|
|
493
|
-
**Next Steps:**
|
|
494
|
-
- Run `code-review` skill for peer review
|
|
495
|
-
- After approval, mark story as "done"
|
|
496
492
|
</output>
|
|
493
|
+
|
|
494
|
+
<!-- AUTO-INVOKE: @reviewer for code review -->
|
|
495
|
+
<goto step="8">Automatic code review</goto>
|
|
496
|
+
</step>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Step 8: Automatic Code Review (by @reviewer)
|
|
500
|
+
|
|
501
|
+
```xml
|
|
502
|
+
<step n="8" goal="Automatic security and quality review before done">
|
|
503
|
+
<critical>ALWAYS invoke @reviewer after all tasks complete</critical>
|
|
504
|
+
|
|
505
|
+
<action>Invoke @reviewer agent with story path</action>
|
|
506
|
+
<action>@reviewer uses GPT-5.2 Codex for deep analysis</action>
|
|
507
|
+
|
|
508
|
+
<invoke agent="reviewer">
|
|
509
|
+
<param name="story_path">{{story_path}}</param>
|
|
510
|
+
<param name="files_changed">{{file_list}}</param>
|
|
511
|
+
<param name="focus">security, correctness, test coverage</param>
|
|
512
|
+
</invoke>
|
|
513
|
+
|
|
514
|
+
<check if="reviewer verdict = APPROVE">
|
|
515
|
+
<action>Mark story status as "done"</action>
|
|
516
|
+
<output>✅ Code review passed! Story complete.</output>
|
|
517
|
+
</check>
|
|
518
|
+
|
|
519
|
+
<check if="reviewer verdict = CHANGES_REQUESTED">
|
|
520
|
+
<action>Create follow-up tasks from review findings</action>
|
|
521
|
+
<action>Add tasks to story file</action>
|
|
522
|
+
<output>
|
|
523
|
+
🔄 **Code Review: Changes Requested**
|
|
524
|
+
|
|
525
|
+
Review found {{issues_count}} issues to fix.
|
|
526
|
+
New tasks added to story.
|
|
527
|
+
|
|
528
|
+
Run dev-story again to fix issues.
|
|
529
|
+
</output>
|
|
530
|
+
<goto step="4">Fix review issues</goto>
|
|
531
|
+
</check>
|
|
532
|
+
|
|
533
|
+
<check if="reviewer verdict = BLOCKED">
|
|
534
|
+
<action>Mark story status as "blocked"</action>
|
|
535
|
+
<output>
|
|
536
|
+
❌ **Code Review: Blocked**
|
|
537
|
+
|
|
538
|
+
Critical issues found. See review for details.
|
|
539
|
+
Cannot proceed until blocking issues resolved.
|
|
540
|
+
</output>
|
|
541
|
+
<halt reason="Blocked by code review"/>
|
|
542
|
+
</check>
|
|
497
543
|
</step>
|
|
498
544
|
```
|
|
499
545
|
|
|
@@ -519,6 +565,7 @@ Read from `config.yaml → development.methodology`:
|
|
|
519
565
|
|
|
520
566
|
- [ ] All tasks/subtasks marked complete
|
|
521
567
|
- [ ] Implementation satisfies every AC
|
|
568
|
+
- [ ] Security checklist passed
|
|
522
569
|
- [ ] Unit tests added for core functionality
|
|
523
570
|
- [ ] Integration tests added when required
|
|
524
571
|
- [ ] All tests pass (no regressions)
|
|
@@ -526,4 +573,5 @@ Read from `config.yaml → development.methodology`:
|
|
|
526
573
|
- [ ] File List includes all changes
|
|
527
574
|
- [ ] Dev Agent Record complete
|
|
528
575
|
- [ ] Change Log updated
|
|
529
|
-
- [ ]
|
|
576
|
+
- [ ] **@reviewer approved** (auto-invoked after Step 7)
|
|
577
|
+
- [ ] Status set to "done"
|
|
@@ -204,10 +204,26 @@ Story is complete when:
|
|
|
204
204
|
|
|
205
205
|
---
|
|
206
206
|
|
|
207
|
+
## Security Checklist
|
|
208
|
+
|
|
209
|
+
Before marking story as done, verify:
|
|
210
|
+
|
|
211
|
+
- [ ] **Input Validation**: All user inputs validated/sanitized
|
|
212
|
+
- [ ] **Authentication**: Protected endpoints require auth
|
|
213
|
+
- [ ] **Authorization**: User can only access their own data
|
|
214
|
+
- [ ] **Secrets**: No hardcoded passwords, tokens, API keys
|
|
215
|
+
- [ ] **SQL Injection**: Using parameterized queries / ORM
|
|
216
|
+
- [ ] **XSS**: Output properly escaped (if frontend)
|
|
217
|
+
- [ ] **Sensitive Data**: PII/secrets not logged
|
|
218
|
+
- [ ] **Error Messages**: No sensitive info in error responses
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
207
222
|
## Definition of Done
|
|
208
223
|
|
|
209
224
|
- [ ] All acceptance criteria met
|
|
210
225
|
- [ ] All tasks completed
|
|
226
|
+
- [ ] Security checklist passed
|
|
211
227
|
- [ ] Code follows `docs/coding-standards/`
|
|
212
228
|
- [ ] Tests pass
|
|
213
229
|
- [ ] Code reviewed
|