cap-pro 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/README.md +26 -0
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/LICENSE +21 -0
- package/README.ja-JP.md +834 -0
- package/README.ko-KR.md +823 -0
- package/README.md +806 -0
- package/README.pt-BR.md +452 -0
- package/README.zh-CN.md +800 -0
- package/agents/cap-architect.md +269 -0
- package/agents/cap-brainstormer.md +207 -0
- package/agents/cap-curator.md +276 -0
- package/agents/cap-debugger.md +365 -0
- package/agents/cap-designer.md +246 -0
- package/agents/cap-historian.md +464 -0
- package/agents/cap-migrator.md +291 -0
- package/agents/cap-prototyper.md +197 -0
- package/agents/cap-validator.md +308 -0
- package/bin/install.js +5433 -0
- package/cap/bin/cap-tools.cjs +853 -0
- package/cap/bin/lib/arc-scanner.cjs +344 -0
- package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
- package/cap/bin/lib/cap-anchor.cjs +228 -0
- package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
- package/cap/bin/lib/cap-checkpoint.cjs +434 -0
- package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
- package/cap/bin/lib/cap-cluster-display.cjs +52 -0
- package/cap/bin/lib/cap-cluster-format.cjs +245 -0
- package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
- package/cap/bin/lib/cap-cluster-io.cjs +212 -0
- package/cap/bin/lib/cap-completeness.cjs +540 -0
- package/cap/bin/lib/cap-deps.cjs +583 -0
- package/cap/bin/lib/cap-design-families.cjs +332 -0
- package/cap/bin/lib/cap-design.cjs +966 -0
- package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
- package/cap/bin/lib/cap-doctor.cjs +752 -0
- package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
- package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
- package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
- package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
- package/cap/bin/lib/cap-feature-map.cjs +1943 -0
- package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
- package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
- package/cap/bin/lib/cap-learn-review.cjs +1072 -0
- package/cap/bin/lib/cap-learning-signals.cjs +627 -0
- package/cap/bin/lib/cap-loader.cjs +227 -0
- package/cap/bin/lib/cap-logger.cjs +57 -0
- package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
- package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
- package/cap/bin/lib/cap-memory-dir.cjs +987 -0
- package/cap/bin/lib/cap-memory-engine.cjs +698 -0
- package/cap/bin/lib/cap-memory-extends.cjs +398 -0
- package/cap/bin/lib/cap-memory-graph.cjs +790 -0
- package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
- package/cap/bin/lib/cap-memory-pin.cjs +183 -0
- package/cap/bin/lib/cap-memory-platform.cjs +490 -0
- package/cap/bin/lib/cap-memory-prune.cjs +707 -0
- package/cap/bin/lib/cap-memory-schema.cjs +812 -0
- package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
- package/cap/bin/lib/cap-migrate.cjs +540 -0
- package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
- package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
- package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
- package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
- package/cap/bin/lib/cap-reconcile.cjs +570 -0
- package/cap/bin/lib/cap-research-gate.cjs +218 -0
- package/cap/bin/lib/cap-scope-filter.cjs +402 -0
- package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
- package/cap/bin/lib/cap-session-extract.cjs +987 -0
- package/cap/bin/lib/cap-session.cjs +445 -0
- package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
- package/cap/bin/lib/cap-stack-docs.cjs +646 -0
- package/cap/bin/lib/cap-tag-observer.cjs +371 -0
- package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
- package/cap/bin/lib/cap-telemetry.cjs +466 -0
- package/cap/bin/lib/cap-test-audit.cjs +1438 -0
- package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
- package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
- package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
- package/cap/bin/lib/cap-trace.cjs +399 -0
- package/cap/bin/lib/cap-trust-mode.cjs +336 -0
- package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
- package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
- package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
- package/cap/bin/lib/cap-ui.cjs +1245 -0
- package/cap/bin/lib/cap-upgrade.cjs +1028 -0
- package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
- package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
- package/cap/bin/lib/cli/init-router.cjs +68 -0
- package/cap/bin/lib/cli/phase-router.cjs +102 -0
- package/cap/bin/lib/cli/state-router.cjs +61 -0
- package/cap/bin/lib/cli/template-router.cjs +37 -0
- package/cap/bin/lib/cli/uat-router.cjs +29 -0
- package/cap/bin/lib/cli/validation-router.cjs +26 -0
- package/cap/bin/lib/cli/verification-router.cjs +31 -0
- package/cap/bin/lib/cli/workstream-router.cjs +39 -0
- package/cap/bin/lib/commands.cjs +961 -0
- package/cap/bin/lib/config.cjs +467 -0
- package/cap/bin/lib/convention-reader.cjs +258 -0
- package/cap/bin/lib/core.cjs +1241 -0
- package/cap/bin/lib/feature-aggregator.cjs +423 -0
- package/cap/bin/lib/frontmatter.cjs +337 -0
- package/cap/bin/lib/init.cjs +1443 -0
- package/cap/bin/lib/manifest-generator.cjs +383 -0
- package/cap/bin/lib/milestone.cjs +253 -0
- package/cap/bin/lib/model-profiles.cjs +69 -0
- package/cap/bin/lib/monorepo-context.cjs +226 -0
- package/cap/bin/lib/monorepo-migrator.cjs +509 -0
- package/cap/bin/lib/phase.cjs +889 -0
- package/cap/bin/lib/profile-output.cjs +989 -0
- package/cap/bin/lib/profile-pipeline.cjs +540 -0
- package/cap/bin/lib/roadmap.cjs +330 -0
- package/cap/bin/lib/security.cjs +394 -0
- package/cap/bin/lib/session-manager.cjs +292 -0
- package/cap/bin/lib/skeleton-generator.cjs +179 -0
- package/cap/bin/lib/state.cjs +1032 -0
- package/cap/bin/lib/template.cjs +231 -0
- package/cap/bin/lib/test-detector.cjs +62 -0
- package/cap/bin/lib/uat.cjs +283 -0
- package/cap/bin/lib/verify.cjs +889 -0
- package/cap/bin/lib/workspace-detector.cjs +371 -0
- package/cap/bin/lib/workstream.cjs +492 -0
- package/cap/commands/gsd/workstreams.md +63 -0
- package/cap/references/arc-standard.md +315 -0
- package/cap/references/cap-agent-architecture.md +101 -0
- package/cap/references/cap-gitignore-template +9 -0
- package/cap/references/cap-zero-deps.md +158 -0
- package/cap/references/checkpoints.md +778 -0
- package/cap/references/continuation-format.md +249 -0
- package/cap/references/contract-test-templates.md +312 -0
- package/cap/references/feature-map-template.md +25 -0
- package/cap/references/git-integration.md +295 -0
- package/cap/references/git-planning-commit.md +38 -0
- package/cap/references/model-profiles.md +174 -0
- package/cap/references/phase-numbering.md +126 -0
- package/cap/references/planning-config.md +202 -0
- package/cap/references/property-test-templates.md +316 -0
- package/cap/references/security-test-templates.md +347 -0
- package/cap/references/session-template.json +8 -0
- package/cap/references/tdd.md +263 -0
- package/cap/references/user-profiling.md +681 -0
- package/cap/references/verification-patterns.md +612 -0
- package/cap/templates/UAT.md +265 -0
- package/cap/templates/claude-md.md +175 -0
- package/cap/templates/codebase/architecture.md +255 -0
- package/cap/templates/codebase/concerns.md +310 -0
- package/cap/templates/codebase/conventions.md +307 -0
- package/cap/templates/codebase/integrations.md +280 -0
- package/cap/templates/codebase/stack.md +186 -0
- package/cap/templates/codebase/structure.md +285 -0
- package/cap/templates/codebase/testing.md +480 -0
- package/cap/templates/config.json +44 -0
- package/cap/templates/context.md +352 -0
- package/cap/templates/continue-here.md +78 -0
- package/cap/templates/copilot-instructions.md +7 -0
- package/cap/templates/debug-subagent-prompt.md +91 -0
- package/cap/templates/discussion-log.md +63 -0
- package/cap/templates/milestone-archive.md +123 -0
- package/cap/templates/milestone.md +115 -0
- package/cap/templates/phase-prompt.md +610 -0
- package/cap/templates/planner-subagent-prompt.md +117 -0
- package/cap/templates/project.md +186 -0
- package/cap/templates/requirements.md +231 -0
- package/cap/templates/research-project/ARCHITECTURE.md +204 -0
- package/cap/templates/research-project/FEATURES.md +147 -0
- package/cap/templates/research-project/PITFALLS.md +200 -0
- package/cap/templates/research-project/STACK.md +120 -0
- package/cap/templates/research-project/SUMMARY.md +170 -0
- package/cap/templates/research.md +552 -0
- package/cap/templates/roadmap.md +202 -0
- package/cap/templates/state.md +176 -0
- package/cap/templates/summary.md +364 -0
- package/cap/templates/user-preferences.md +498 -0
- package/cap/templates/verification-report.md +322 -0
- package/cap/workflows/add-phase.md +112 -0
- package/cap/workflows/add-tests.md +351 -0
- package/cap/workflows/add-todo.md +158 -0
- package/cap/workflows/audit-milestone.md +340 -0
- package/cap/workflows/audit-uat.md +109 -0
- package/cap/workflows/autonomous.md +891 -0
- package/cap/workflows/check-todos.md +177 -0
- package/cap/workflows/cleanup.md +152 -0
- package/cap/workflows/complete-milestone.md +767 -0
- package/cap/workflows/diagnose-issues.md +231 -0
- package/cap/workflows/discovery-phase.md +289 -0
- package/cap/workflows/discuss-phase-assumptions.md +653 -0
- package/cap/workflows/discuss-phase.md +1049 -0
- package/cap/workflows/do.md +104 -0
- package/cap/workflows/execute-phase.md +846 -0
- package/cap/workflows/execute-plan.md +514 -0
- package/cap/workflows/fast.md +105 -0
- package/cap/workflows/forensics.md +265 -0
- package/cap/workflows/health.md +181 -0
- package/cap/workflows/help.md +660 -0
- package/cap/workflows/insert-phase.md +130 -0
- package/cap/workflows/list-phase-assumptions.md +178 -0
- package/cap/workflows/list-workspaces.md +56 -0
- package/cap/workflows/manager.md +362 -0
- package/cap/workflows/map-codebase.md +377 -0
- package/cap/workflows/milestone-summary.md +223 -0
- package/cap/workflows/new-milestone.md +486 -0
- package/cap/workflows/new-project.md +1250 -0
- package/cap/workflows/new-workspace.md +237 -0
- package/cap/workflows/next.md +97 -0
- package/cap/workflows/node-repair.md +92 -0
- package/cap/workflows/note.md +156 -0
- package/cap/workflows/pause-work.md +176 -0
- package/cap/workflows/plan-milestone-gaps.md +273 -0
- package/cap/workflows/plan-phase.md +857 -0
- package/cap/workflows/plant-seed.md +169 -0
- package/cap/workflows/pr-branch.md +129 -0
- package/cap/workflows/profile-user.md +449 -0
- package/cap/workflows/progress.md +507 -0
- package/cap/workflows/quick.md +757 -0
- package/cap/workflows/remove-phase.md +155 -0
- package/cap/workflows/remove-workspace.md +90 -0
- package/cap/workflows/research-phase.md +82 -0
- package/cap/workflows/resume-project.md +326 -0
- package/cap/workflows/review.md +228 -0
- package/cap/workflows/session-report.md +146 -0
- package/cap/workflows/settings.md +283 -0
- package/cap/workflows/ship.md +228 -0
- package/cap/workflows/stats.md +60 -0
- package/cap/workflows/transition.md +671 -0
- package/cap/workflows/ui-phase.md +298 -0
- package/cap/workflows/ui-review.md +161 -0
- package/cap/workflows/update.md +323 -0
- package/cap/workflows/validate-phase.md +170 -0
- package/cap/workflows/verify-phase.md +254 -0
- package/cap/workflows/verify-work.md +637 -0
- package/commands/cap/annotate.md +165 -0
- package/commands/cap/brainstorm.md +393 -0
- package/commands/cap/checkpoint.md +106 -0
- package/commands/cap/completeness.md +94 -0
- package/commands/cap/continue.md +72 -0
- package/commands/cap/debug.md +588 -0
- package/commands/cap/deps.md +169 -0
- package/commands/cap/design.md +479 -0
- package/commands/cap/init.md +354 -0
- package/commands/cap/iterate.md +249 -0
- package/commands/cap/learn.md +459 -0
- package/commands/cap/memory.md +275 -0
- package/commands/cap/migrate-feature-map.md +91 -0
- package/commands/cap/migrate-memory.md +108 -0
- package/commands/cap/migrate-tags.md +91 -0
- package/commands/cap/migrate.md +131 -0
- package/commands/cap/prototype.md +510 -0
- package/commands/cap/reconcile.md +121 -0
- package/commands/cap/review.md +360 -0
- package/commands/cap/save.md +72 -0
- package/commands/cap/scan.md +404 -0
- package/commands/cap/start.md +356 -0
- package/commands/cap/status.md +118 -0
- package/commands/cap/test-audit.md +262 -0
- package/commands/cap/test.md +394 -0
- package/commands/cap/trace.md +133 -0
- package/commands/cap/ui.md +167 -0
- package/hooks/dist/cap-check-update.js +115 -0
- package/hooks/dist/cap-context-monitor.js +185 -0
- package/hooks/dist/cap-learn-review-hook.js +114 -0
- package/hooks/dist/cap-learning-hook.js +192 -0
- package/hooks/dist/cap-memory.js +299 -0
- package/hooks/dist/cap-prompt-guard.js +97 -0
- package/hooks/dist/cap-statusline.js +157 -0
- package/hooks/dist/cap-tag-observer.js +115 -0
- package/hooks/dist/cap-version-check.js +112 -0
- package/hooks/dist/cap-workflow-guard.js +175 -0
- package/hooks/hooks.json +55 -0
- package/package.json +58 -0
- package/scripts/base64-scan.sh +262 -0
- package/scripts/build-hooks.js +93 -0
- package/scripts/cap-removal-checklist.md +202 -0
- package/scripts/prompt-injection-scan.sh +199 -0
- package/scripts/run-tests.cjs +181 -0
- package/scripts/secret-scan.sh +227 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Security Test Templates
|
|
2
|
+
|
|
3
|
+
Reference document for the cap-validator agent (test mode) when generating security-focused tests. Use these templates as starting points, adapting table names, column names, and auth patterns to the target project.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. RLS Policy Tests (Supabase)
|
|
8
|
+
|
|
9
|
+
Row-Level Security tests verify that database policies enforce data isolation between users.
|
|
10
|
+
|
|
11
|
+
### User isolation -- read
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { describe, it, expect } from 'vitest';
|
|
15
|
+
import { createClient } from '@supabase/supabase-js';
|
|
16
|
+
|
|
17
|
+
describe('RLS: TABLE_NAME read isolation', () => {
|
|
18
|
+
it('user cannot read other user data', async () => {
|
|
19
|
+
// Arrange: create client authenticated as User A
|
|
20
|
+
const supabaseA = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
21
|
+
global: { headers: { Authorization: `Bearer ${userAToken}` } },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Act: query data belonging to User B
|
|
25
|
+
const { data, error } = await supabaseA
|
|
26
|
+
.from('TABLE_NAME')
|
|
27
|
+
.select('*')
|
|
28
|
+
.eq('user_id', userBId);
|
|
29
|
+
|
|
30
|
+
// Assert: no data returned (RLS blocks access)
|
|
31
|
+
expect(error).toBeNull();
|
|
32
|
+
expect(data).toHaveLength(0);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('user can read own data', async () => {
|
|
36
|
+
const supabaseA = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
37
|
+
global: { headers: { Authorization: `Bearer ${userAToken}` } },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const { data, error } = await supabaseA
|
|
41
|
+
.from('TABLE_NAME')
|
|
42
|
+
.select('*')
|
|
43
|
+
.eq('user_id', userAId);
|
|
44
|
+
|
|
45
|
+
expect(error).toBeNull();
|
|
46
|
+
expect(data.length).toBeGreaterThan(0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### User isolation -- write
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
describe('RLS: TABLE_NAME write isolation', () => {
|
|
55
|
+
it('user cannot update other user data', async () => {
|
|
56
|
+
const supabaseA = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
57
|
+
global: { headers: { Authorization: `Bearer ${userAToken}` } },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const { error } = await supabaseA
|
|
61
|
+
.from('TABLE_NAME')
|
|
62
|
+
.update({ name: 'hacked' })
|
|
63
|
+
.eq('user_id', userBId);
|
|
64
|
+
|
|
65
|
+
// Either error or zero affected rows
|
|
66
|
+
expect(error || true).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('user cannot delete other user data', async () => {
|
|
70
|
+
const supabaseA = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
71
|
+
global: { headers: { Authorization: `Bearer ${userAToken}` } },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const { error } = await supabaseA
|
|
75
|
+
.from('TABLE_NAME')
|
|
76
|
+
.delete()
|
|
77
|
+
.eq('user_id', userBId);
|
|
78
|
+
|
|
79
|
+
expect(error || true).toBeTruthy();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Anon access blocked
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
describe('RLS: TABLE_NAME anon access', () => {
|
|
88
|
+
it('anonymous user cannot read any data', async () => {
|
|
89
|
+
const supabaseAnon = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
|
90
|
+
|
|
91
|
+
const { data, error } = await supabaseAnon
|
|
92
|
+
.from('TABLE_NAME')
|
|
93
|
+
.select('*');
|
|
94
|
+
|
|
95
|
+
// Either empty result or permission error
|
|
96
|
+
expect(data?.length ?? 0).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 2. Auth Bypass Tests
|
|
104
|
+
|
|
105
|
+
Tests that verify authentication cannot be bypassed through common attack vectors.
|
|
106
|
+
|
|
107
|
+
### JWT validation
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
describe('Auth: JWT validation', () => {
|
|
111
|
+
it('rejects expired JWT token', async () => {
|
|
112
|
+
const expiredToken = createJWT({ sub: userId, exp: Math.floor(Date.now() / 1000) - 3600 });
|
|
113
|
+
const response = await fetch(`${API_URL}/protected`, {
|
|
114
|
+
headers: { Authorization: `Bearer ${expiredToken}` },
|
|
115
|
+
});
|
|
116
|
+
expect(response.status).toBe(401);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('rejects modified JWT payload', async () => {
|
|
120
|
+
const validToken = createJWT({ sub: userId, role: 'user' });
|
|
121
|
+
// Tamper with payload (change role to admin without re-signing)
|
|
122
|
+
const parts = validToken.split('.');
|
|
123
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
|
|
124
|
+
payload.role = 'admin';
|
|
125
|
+
parts[1] = Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
126
|
+
const tamperedToken = parts.join('.');
|
|
127
|
+
|
|
128
|
+
const response = await fetch(`${API_URL}/protected`, {
|
|
129
|
+
headers: { Authorization: `Bearer ${tamperedToken}` },
|
|
130
|
+
});
|
|
131
|
+
expect(response.status).toBe(401);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('rejects missing auth header', async () => {
|
|
135
|
+
const response = await fetch(`${API_URL}/protected`);
|
|
136
|
+
expect(response.status).toBe(401);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('rejects token from wrong issuer', async () => {
|
|
140
|
+
const wrongIssuerToken = createJWT({ sub: userId, iss: 'https://evil.example.com' });
|
|
141
|
+
const response = await fetch(`${API_URL}/protected`, {
|
|
142
|
+
headers: { Authorization: `Bearer ${wrongIssuerToken}` },
|
|
143
|
+
});
|
|
144
|
+
expect(response.status).toBe(401);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('rejects token with invalid signature', async () => {
|
|
148
|
+
const token = createJWT({ sub: userId });
|
|
149
|
+
const corruptedToken = token.slice(0, -5) + 'XXXXX';
|
|
150
|
+
const response = await fetch(`${API_URL}/protected`, {
|
|
151
|
+
headers: { Authorization: `Bearer ${corruptedToken}` },
|
|
152
|
+
});
|
|
153
|
+
expect(response.status).toBe(401);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Role escalation
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
describe('Auth: role escalation prevention', () => {
|
|
162
|
+
it('regular user cannot access admin endpoint', async () => {
|
|
163
|
+
const userToken = await loginAs('regular-user');
|
|
164
|
+
const response = await fetch(`${API_URL}/admin/users`, {
|
|
165
|
+
headers: { Authorization: `Bearer ${userToken}` },
|
|
166
|
+
});
|
|
167
|
+
expect(response.status).toBe(403);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('regular user cannot modify own role', async () => {
|
|
171
|
+
const userToken = await loginAs('regular-user');
|
|
172
|
+
const response = await fetch(`${API_URL}/users/me`, {
|
|
173
|
+
method: 'PATCH',
|
|
174
|
+
headers: {
|
|
175
|
+
Authorization: `Bearer ${userToken}`,
|
|
176
|
+
'Content-Type': 'application/json',
|
|
177
|
+
},
|
|
178
|
+
body: JSON.stringify({ role: 'admin' }),
|
|
179
|
+
});
|
|
180
|
+
// Should either reject or ignore the role field
|
|
181
|
+
expect(response.status).not.toBe(200);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 3. Input Sanitization Tests
|
|
189
|
+
|
|
190
|
+
Tests that verify user input is properly sanitized against injection attacks.
|
|
191
|
+
|
|
192
|
+
### XSS prevention
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
describe('Input: XSS prevention', () => {
|
|
196
|
+
it('rejects script tags in text fields', async () => {
|
|
197
|
+
const response = await createResource({
|
|
198
|
+
name: '<script>alert("xss")</script>',
|
|
199
|
+
});
|
|
200
|
+
// Should either reject or escape the input
|
|
201
|
+
if (response.status === 200) {
|
|
202
|
+
const data = await response.json();
|
|
203
|
+
expect(data.name).not.toContain('<script>');
|
|
204
|
+
} else {
|
|
205
|
+
expect(response.status).toBe(400);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('rejects event handler injection', async () => {
|
|
210
|
+
const response = await createResource({
|
|
211
|
+
name: '" onmouseover="alert(1)',
|
|
212
|
+
});
|
|
213
|
+
if (response.status === 200) {
|
|
214
|
+
const data = await response.json();
|
|
215
|
+
expect(data.name).not.toContain('onmouseover');
|
|
216
|
+
} else {
|
|
217
|
+
expect(response.status).toBe(400);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### SQL injection prevention
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
describe('Input: SQL injection prevention', () => {
|
|
227
|
+
it('rejects SQL injection in search parameter', async () => {
|
|
228
|
+
const response = await fetch(`${API_URL}/search?q=' OR '1'='1`);
|
|
229
|
+
// Should not return all records
|
|
230
|
+
expect(response.status).toBe(200);
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
expect(data.length).toBeLessThan(100); // Sanity check
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('rejects UNION-based injection', async () => {
|
|
236
|
+
const response = await fetch(`${API_URL}/search?q=' UNION SELECT * FROM users --`);
|
|
237
|
+
expect(response.status).toBe(200);
|
|
238
|
+
const data = await response.json();
|
|
239
|
+
// Should not contain user table data
|
|
240
|
+
expect(data.some(d => d.password_hash)).toBe(false);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Path traversal prevention
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
describe('Input: path traversal prevention', () => {
|
|
249
|
+
it('rejects directory traversal in file path', async () => {
|
|
250
|
+
const response = await fetch(`${API_URL}/files/../../../etc/passwd`);
|
|
251
|
+
expect(response.status).toBe(400);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('rejects encoded directory traversal', async () => {
|
|
255
|
+
const response = await fetch(`${API_URL}/files/%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd`);
|
|
256
|
+
expect([400, 403, 404]).toContain(response.status);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 4. Data Leakage Tests
|
|
264
|
+
|
|
265
|
+
Tests that verify sensitive data is never exposed in API responses or error messages.
|
|
266
|
+
|
|
267
|
+
### Sensitive field filtering
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
describe('Data leakage: sensitive field filtering', () => {
|
|
271
|
+
it('API does not return password hash', async () => {
|
|
272
|
+
const response = await fetch(`${API_URL}/users/me`, {
|
|
273
|
+
headers: { Authorization: `Bearer ${validToken}` },
|
|
274
|
+
});
|
|
275
|
+
const data = await response.json();
|
|
276
|
+
expect(data).not.toHaveProperty('password_hash');
|
|
277
|
+
expect(data).not.toHaveProperty('password');
|
|
278
|
+
expect(data).not.toHaveProperty('hashed_password');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('API does not return internal database IDs in list endpoints', async () => {
|
|
282
|
+
const response = await fetch(`${API_URL}/resources`, {
|
|
283
|
+
headers: { Authorization: `Bearer ${validToken}` },
|
|
284
|
+
});
|
|
285
|
+
const data = await response.json();
|
|
286
|
+
for (const item of data) {
|
|
287
|
+
expect(item).not.toHaveProperty('_id'); // MongoDB internal ID
|
|
288
|
+
expect(item).not.toHaveProperty('internal_id');
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('API does not expose other users email addresses', async () => {
|
|
293
|
+
const response = await fetch(`${API_URL}/users`, {
|
|
294
|
+
headers: { Authorization: `Bearer ${validToken}` },
|
|
295
|
+
});
|
|
296
|
+
const data = await response.json();
|
|
297
|
+
const otherUsers = data.filter(u => u.id !== currentUserId);
|
|
298
|
+
for (const user of otherUsers) {
|
|
299
|
+
expect(user).not.toHaveProperty('email');
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Error message safety
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
describe('Data leakage: error messages', () => {
|
|
309
|
+
it('error responses do not leak stack traces', async () => {
|
|
310
|
+
const response = await fetch(`${API_URL}/trigger-error`);
|
|
311
|
+
const body = await response.text();
|
|
312
|
+
expect(body).not.toMatch(/at\s+\w+\s+\(/); // Stack trace pattern
|
|
313
|
+
expect(body).not.toContain('node_modules');
|
|
314
|
+
expect(body).not.toContain('.js:');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('error responses do not leak database details', async () => {
|
|
318
|
+
const response = await fetch(`${API_URL}/trigger-error`);
|
|
319
|
+
const body = await response.text();
|
|
320
|
+
expect(body).not.toMatch(/SELECT|INSERT|UPDATE|DELETE/i);
|
|
321
|
+
expect(body).not.toContain('postgresql://');
|
|
322
|
+
expect(body).not.toContain('SQLSTATE');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('404 does not reveal valid resource IDs', async () => {
|
|
326
|
+
const response = await fetch(`${API_URL}/users/nonexistent-id`);
|
|
327
|
+
const body = await response.text();
|
|
328
|
+
expect(body).not.toMatch(/valid IDs include/i);
|
|
329
|
+
expect(body).not.toMatch(/did you mean/i);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Usage Notes
|
|
337
|
+
|
|
338
|
+
When generating security tests:
|
|
339
|
+
|
|
340
|
+
1. **Adapt table/column names** to the target project's schema
|
|
341
|
+
2. **Use the project's actual auth mechanism** (Supabase, NextAuth, custom JWT, etc.)
|
|
342
|
+
3. **Test both happy and sad paths** -- a secure system rejects bad input AND accepts good input
|
|
343
|
+
4. **Cover the OWASP Top 10** relevant to the application type
|
|
344
|
+
5. **For RLS tests**: create two test users, try cross-user access in both directions
|
|
345
|
+
6. **For auth tests**: test expired, tampered, missing, and wrong-issuer tokens
|
|
346
|
+
7. **For input tests**: use payloads from OWASP cheat sheets
|
|
347
|
+
8. **For leakage tests**: check every API endpoint that returns user data
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
<overview>
|
|
2
|
+
TDD is about design quality, not coverage metrics. The red-green-refactor cycle forces you to think about behavior before implementation, producing cleaner interfaces and more testable code.
|
|
3
|
+
|
|
4
|
+
**Principle:** If you can describe the behavior as `expect(fn(input)).toBe(output)` before writing `fn`, TDD improves the result.
|
|
5
|
+
|
|
6
|
+
**Key insight:** TDD work is fundamentally heavier than standard tasks—it requires 2-3 execution cycles (RED → GREEN → REFACTOR), each with file reads, test runs, and potential debugging. TDD features get dedicated plans to ensure full context is available throughout the cycle.
|
|
7
|
+
</overview>
|
|
8
|
+
|
|
9
|
+
<when_to_use_tdd>
|
|
10
|
+
## When TDD Improves Quality
|
|
11
|
+
|
|
12
|
+
**TDD candidates (create a TDD plan):**
|
|
13
|
+
- Business logic with defined inputs/outputs
|
|
14
|
+
- API endpoints with request/response contracts
|
|
15
|
+
- Data transformations, parsing, formatting
|
|
16
|
+
- Validation rules and constraints
|
|
17
|
+
- Algorithms with testable behavior
|
|
18
|
+
- State machines and workflows
|
|
19
|
+
- Utility functions with clear specifications
|
|
20
|
+
|
|
21
|
+
**Skip TDD (use standard plan with `type="auto"` tasks):**
|
|
22
|
+
- UI layout, styling, visual components
|
|
23
|
+
- Configuration changes
|
|
24
|
+
- Glue code connecting existing components
|
|
25
|
+
- One-off scripts and migrations
|
|
26
|
+
- Simple CRUD with no business logic
|
|
27
|
+
- Exploratory prototyping
|
|
28
|
+
|
|
29
|
+
**Heuristic:** Can you write `expect(fn(input)).toBe(output)` before writing `fn`?
|
|
30
|
+
→ Yes: Create a TDD plan
|
|
31
|
+
→ No: Use standard plan, add tests after if needed
|
|
32
|
+
</when_to_use_tdd>
|
|
33
|
+
|
|
34
|
+
<tdd_plan_structure>
|
|
35
|
+
## TDD Plan Structure
|
|
36
|
+
|
|
37
|
+
Each TDD plan implements **one feature** through the full RED-GREEN-REFACTOR cycle.
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
---
|
|
41
|
+
phase: XX-name
|
|
42
|
+
plan: NN
|
|
43
|
+
type: tdd
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<objective>
|
|
47
|
+
[What feature and why]
|
|
48
|
+
Purpose: [Design benefit of TDD for this feature]
|
|
49
|
+
Output: [Working, tested feature]
|
|
50
|
+
</objective>
|
|
51
|
+
|
|
52
|
+
<context>
|
|
53
|
+
@.planning/PROJECT.md
|
|
54
|
+
@.planning/ROADMAP.md
|
|
55
|
+
@relevant/source/files.ts
|
|
56
|
+
</context>
|
|
57
|
+
|
|
58
|
+
<feature>
|
|
59
|
+
<name>[Feature name]</name>
|
|
60
|
+
<files>[source file, test file]</files>
|
|
61
|
+
<behavior>
|
|
62
|
+
[Expected behavior in testable terms]
|
|
63
|
+
Cases: input → expected output
|
|
64
|
+
</behavior>
|
|
65
|
+
<implementation>[How to implement once tests pass]</implementation>
|
|
66
|
+
</feature>
|
|
67
|
+
|
|
68
|
+
<verification>
|
|
69
|
+
[Test command that proves feature works]
|
|
70
|
+
</verification>
|
|
71
|
+
|
|
72
|
+
<success_criteria>
|
|
73
|
+
- Failing test written and committed
|
|
74
|
+
- Implementation passes test
|
|
75
|
+
- Refactor complete (if needed)
|
|
76
|
+
- All 2-3 commits present
|
|
77
|
+
</success_criteria>
|
|
78
|
+
|
|
79
|
+
<output>
|
|
80
|
+
After completion, create SUMMARY.md with:
|
|
81
|
+
- RED: What test was written, why it failed
|
|
82
|
+
- GREEN: What implementation made it pass
|
|
83
|
+
- REFACTOR: What cleanup was done (if any)
|
|
84
|
+
- Commits: List of commits produced
|
|
85
|
+
</output>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**One feature per TDD plan.** If features are trivial enough to batch, they're trivial enough to skip TDD—use a standard plan and add tests after.
|
|
89
|
+
</tdd_plan_structure>
|
|
90
|
+
|
|
91
|
+
<execution_flow>
|
|
92
|
+
## Red-Green-Refactor Cycle
|
|
93
|
+
|
|
94
|
+
**RED - Write failing test:**
|
|
95
|
+
1. Create test file following project conventions
|
|
96
|
+
2. Write test describing expected behavior (from `<behavior>` element)
|
|
97
|
+
3. Run test - it MUST fail
|
|
98
|
+
4. If test passes: feature exists or test is wrong. Investigate.
|
|
99
|
+
5. Commit: `test({phase}-{plan}): add failing test for [feature]`
|
|
100
|
+
|
|
101
|
+
**GREEN - Implement to pass:**
|
|
102
|
+
1. Write minimal code to make test pass
|
|
103
|
+
2. No cleverness, no optimization - just make it work
|
|
104
|
+
3. Run test - it MUST pass
|
|
105
|
+
4. Commit: `feat({phase}-{plan}): implement [feature]`
|
|
106
|
+
|
|
107
|
+
**REFACTOR (if needed):**
|
|
108
|
+
1. Clean up implementation if obvious improvements exist
|
|
109
|
+
2. Run tests - MUST still pass
|
|
110
|
+
3. Only commit if changes made: `refactor({phase}-{plan}): clean up [feature]`
|
|
111
|
+
|
|
112
|
+
**Result:** Each TDD plan produces 2-3 atomic commits.
|
|
113
|
+
</execution_flow>
|
|
114
|
+
|
|
115
|
+
<test_quality>
|
|
116
|
+
## Good Tests vs Bad Tests
|
|
117
|
+
|
|
118
|
+
**Test behavior, not implementation:**
|
|
119
|
+
- Good: "returns formatted date string"
|
|
120
|
+
- Bad: "calls formatDate helper with correct params"
|
|
121
|
+
- Tests should survive refactors
|
|
122
|
+
|
|
123
|
+
**One concept per test:**
|
|
124
|
+
- Good: Separate tests for valid input, empty input, malformed input
|
|
125
|
+
- Bad: Single test checking all edge cases with multiple assertions
|
|
126
|
+
|
|
127
|
+
**Descriptive names:**
|
|
128
|
+
- Good: "should reject empty email", "returns null for invalid ID"
|
|
129
|
+
- Bad: "test1", "handles error", "works correctly"
|
|
130
|
+
|
|
131
|
+
**No implementation details:**
|
|
132
|
+
- Good: Test public API, observable behavior
|
|
133
|
+
- Bad: Mock internals, test private methods, assert on internal state
|
|
134
|
+
</test_quality>
|
|
135
|
+
|
|
136
|
+
<framework_setup>
|
|
137
|
+
## Test Framework Setup (If None Exists)
|
|
138
|
+
|
|
139
|
+
When executing a TDD plan but no test framework is configured, set it up as part of the RED phase:
|
|
140
|
+
|
|
141
|
+
**1. Detect project type:**
|
|
142
|
+
```bash
|
|
143
|
+
# JavaScript/TypeScript
|
|
144
|
+
if [ -f package.json ]; then echo "node"; fi
|
|
145
|
+
|
|
146
|
+
# Python
|
|
147
|
+
if [ -f requirements.txt ] || [ -f pyproject.toml ]; then echo "python"; fi
|
|
148
|
+
|
|
149
|
+
# Go
|
|
150
|
+
if [ -f go.mod ]; then echo "go"; fi
|
|
151
|
+
|
|
152
|
+
# Rust
|
|
153
|
+
if [ -f Cargo.toml ]; then echo "rust"; fi
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**2. Install minimal framework:**
|
|
157
|
+
| Project | Framework | Install |
|
|
158
|
+
|---------|-----------|---------|
|
|
159
|
+
| Node.js | Jest | `npm install -D jest @types/jest ts-jest` |
|
|
160
|
+
| Node.js (Vite) | Vitest | `npm install -D vitest` |
|
|
161
|
+
| Python | pytest | `pip install pytest` |
|
|
162
|
+
| Go | testing | Built-in |
|
|
163
|
+
| Rust | cargo test | Built-in |
|
|
164
|
+
|
|
165
|
+
**3. Create config if needed:**
|
|
166
|
+
- Jest: `jest.config.js` with ts-jest preset
|
|
167
|
+
- Vitest: `vitest.config.ts` with test globals
|
|
168
|
+
- pytest: `pytest.ini` or `pyproject.toml` section
|
|
169
|
+
|
|
170
|
+
**4. Verify setup:**
|
|
171
|
+
```bash
|
|
172
|
+
# Run empty test suite - should pass with 0 tests
|
|
173
|
+
npm test # Node
|
|
174
|
+
pytest # Python
|
|
175
|
+
go test ./... # Go
|
|
176
|
+
cargo test # Rust
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**5. Create first test file:**
|
|
180
|
+
Follow project conventions for test location:
|
|
181
|
+
- `*.test.ts` / `*.spec.ts` next to source
|
|
182
|
+
- `__tests__/` directory
|
|
183
|
+
- `tests/` directory at root
|
|
184
|
+
|
|
185
|
+
Framework setup is a one-time cost included in the first TDD plan's RED phase.
|
|
186
|
+
</framework_setup>
|
|
187
|
+
|
|
188
|
+
<error_handling>
|
|
189
|
+
## Error Handling
|
|
190
|
+
|
|
191
|
+
**Test doesn't fail in RED phase:**
|
|
192
|
+
- Feature may already exist - investigate
|
|
193
|
+
- Test may be wrong (not testing what you think)
|
|
194
|
+
- Fix before proceeding
|
|
195
|
+
|
|
196
|
+
**Test doesn't pass in GREEN phase:**
|
|
197
|
+
- Debug implementation
|
|
198
|
+
- Don't skip to refactor
|
|
199
|
+
- Keep iterating until green
|
|
200
|
+
|
|
201
|
+
**Tests fail in REFACTOR phase:**
|
|
202
|
+
- Undo refactor
|
|
203
|
+
- Commit was premature
|
|
204
|
+
- Refactor in smaller steps
|
|
205
|
+
|
|
206
|
+
**Unrelated tests break:**
|
|
207
|
+
- Stop and investigate
|
|
208
|
+
- May indicate coupling issue
|
|
209
|
+
- Fix before proceeding
|
|
210
|
+
</error_handling>
|
|
211
|
+
|
|
212
|
+
<commit_pattern>
|
|
213
|
+
## Commit Pattern for TDD Plans
|
|
214
|
+
|
|
215
|
+
TDD plans produce 2-3 atomic commits (one per phase):
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
test(08-02): add failing test for email validation
|
|
219
|
+
|
|
220
|
+
- Tests valid email formats accepted
|
|
221
|
+
- Tests invalid formats rejected
|
|
222
|
+
- Tests empty input handling
|
|
223
|
+
|
|
224
|
+
feat(08-02): implement email validation
|
|
225
|
+
|
|
226
|
+
- Regex pattern matches RFC 5322
|
|
227
|
+
- Returns boolean for validity
|
|
228
|
+
- Handles edge cases (empty, null)
|
|
229
|
+
|
|
230
|
+
refactor(08-02): extract regex to constant (optional)
|
|
231
|
+
|
|
232
|
+
- Moved pattern to EMAIL_REGEX constant
|
|
233
|
+
- No behavior changes
|
|
234
|
+
- Tests still pass
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Comparison with standard plans:**
|
|
238
|
+
- Standard plans: 1 commit per task, 2-4 commits per plan
|
|
239
|
+
- TDD plans: 2-3 commits for single feature
|
|
240
|
+
|
|
241
|
+
Both follow same format: `{type}({phase}-{plan}): {description}`
|
|
242
|
+
|
|
243
|
+
**Benefits:**
|
|
244
|
+
- Each commit independently revertable
|
|
245
|
+
- Git bisect works at commit level
|
|
246
|
+
- Clear history showing TDD discipline
|
|
247
|
+
- Consistent with overall commit strategy
|
|
248
|
+
</commit_pattern>
|
|
249
|
+
|
|
250
|
+
<context_budget>
|
|
251
|
+
## Context Budget
|
|
252
|
+
|
|
253
|
+
TDD plans target **~40% context usage** (lower than standard plans' ~50%).
|
|
254
|
+
|
|
255
|
+
Why lower:
|
|
256
|
+
- RED phase: write test, run test, potentially debug why it didn't fail
|
|
257
|
+
- GREEN phase: implement, run test, potentially iterate on failures
|
|
258
|
+
- REFACTOR phase: modify code, run tests, verify no regressions
|
|
259
|
+
|
|
260
|
+
Each phase involves reading files, running commands, analyzing output. The back-and-forth is inherently heavier than linear task execution.
|
|
261
|
+
|
|
262
|
+
Single feature focus ensures full quality throughout the cycle.
|
|
263
|
+
</context_budget>
|