aigent-team 0.1.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +253 -0
  3. package/dist/chunk-N3RYHWTR.js +267 -0
  4. package/dist/cli.js +576 -0
  5. package/dist/index.d.ts +234 -0
  6. package/dist/index.js +27 -0
  7. package/package.json +67 -0
  8. package/templates/shared/git-workflow.md +44 -0
  9. package/templates/shared/project-conventions.md +48 -0
  10. package/templates/teams/ba/agent.yaml +25 -0
  11. package/templates/teams/ba/references/acceptance-criteria.md +87 -0
  12. package/templates/teams/ba/references/api-contract-design.md +110 -0
  13. package/templates/teams/ba/references/requirements-analysis.md +83 -0
  14. package/templates/teams/ba/references/user-story-mapping.md +73 -0
  15. package/templates/teams/ba/skill.md +85 -0
  16. package/templates/teams/be/agent.yaml +34 -0
  17. package/templates/teams/be/conventions.md +102 -0
  18. package/templates/teams/be/references/api-design.md +91 -0
  19. package/templates/teams/be/references/async-processing.md +86 -0
  20. package/templates/teams/be/references/auth-security.md +58 -0
  21. package/templates/teams/be/references/caching.md +79 -0
  22. package/templates/teams/be/references/database.md +65 -0
  23. package/templates/teams/be/references/error-handling.md +106 -0
  24. package/templates/teams/be/references/observability.md +83 -0
  25. package/templates/teams/be/references/review-checklist.md +50 -0
  26. package/templates/teams/be/references/testing.md +100 -0
  27. package/templates/teams/be/review-checklist.md +54 -0
  28. package/templates/teams/be/skill.md +71 -0
  29. package/templates/teams/devops/agent.yaml +35 -0
  30. package/templates/teams/devops/conventions.md +133 -0
  31. package/templates/teams/devops/references/ci-cd.md +218 -0
  32. package/templates/teams/devops/references/cost-optimization.md +218 -0
  33. package/templates/teams/devops/references/disaster-recovery.md +199 -0
  34. package/templates/teams/devops/references/docker.md +237 -0
  35. package/templates/teams/devops/references/infrastructure-as-code.md +238 -0
  36. package/templates/teams/devops/references/kubernetes.md +397 -0
  37. package/templates/teams/devops/references/monitoring.md +224 -0
  38. package/templates/teams/devops/references/review-checklist.md +149 -0
  39. package/templates/teams/devops/references/security.md +225 -0
  40. package/templates/teams/devops/review-checklist.md +72 -0
  41. package/templates/teams/devops/skill.md +131 -0
  42. package/templates/teams/fe/agent.yaml +28 -0
  43. package/templates/teams/fe/conventions.md +80 -0
  44. package/templates/teams/fe/references/accessibility.md +92 -0
  45. package/templates/teams/fe/references/component-architecture.md +87 -0
  46. package/templates/teams/fe/references/css-styling.md +89 -0
  47. package/templates/teams/fe/references/forms.md +73 -0
  48. package/templates/teams/fe/references/performance.md +104 -0
  49. package/templates/teams/fe/references/review-checklist.md +51 -0
  50. package/templates/teams/fe/references/security.md +90 -0
  51. package/templates/teams/fe/references/state-management.md +117 -0
  52. package/templates/teams/fe/references/testing.md +112 -0
  53. package/templates/teams/fe/review-checklist.md +53 -0
  54. package/templates/teams/fe/skill.md +68 -0
  55. package/templates/teams/lead/agent.yaml +18 -0
  56. package/templates/teams/lead/references/cross-team-coordination.md +68 -0
  57. package/templates/teams/lead/references/quality-gates.md +64 -0
  58. package/templates/teams/lead/references/task-decomposition.md +69 -0
  59. package/templates/teams/lead/skill.md +83 -0
  60. package/templates/teams/qa/agent.yaml +32 -0
  61. package/templates/teams/qa/conventions.md +130 -0
  62. package/templates/teams/qa/references/ci-integration.md +337 -0
  63. package/templates/teams/qa/references/e2e-testing.md +292 -0
  64. package/templates/teams/qa/references/mocking.md +249 -0
  65. package/templates/teams/qa/references/performance-testing.md +288 -0
  66. package/templates/teams/qa/references/review-checklist.md +143 -0
  67. package/templates/teams/qa/references/security-testing.md +271 -0
  68. package/templates/teams/qa/references/test-data.md +275 -0
  69. package/templates/teams/qa/references/test-strategy.md +192 -0
  70. package/templates/teams/qa/review-checklist.md +53 -0
  71. package/templates/teams/qa/skill.md +131 -0
@@ -0,0 +1,288 @@
1
+ # Performance Testing Reference
2
+
3
+ ## Load Scenario Types
4
+
5
+ | Scenario | Purpose | VUs | Duration | When to run |
6
+ |---|---|---|---|---|
7
+ | **Smoke** | Verify script works, baseline response | 1-5 | 1 min | Every PR (fast gate) |
8
+ | **Load** | Validate SLOs under expected traffic | Target VUs | 10-30 min | Nightly / pre-release |
9
+ | **Stress** | Find breaking point | Ramp beyond target | 15-30 min | Weekly / pre-release |
10
+ | **Soak** | Detect memory leaks, connection exhaustion | Target VUs | 2-8 hours | Weekly (off-hours) |
11
+ | **Spike** | Verify recovery from sudden traffic burst | 0 → peak → 0 | 5-10 min | Pre-release |
12
+
13
+ ---
14
+
15
+ ## k6 Script Examples
16
+
17
+ ### Basic Load Test
18
+
19
+ ```javascript
20
+ // tests/performance/load-api.js
21
+ import http from 'k6/http';
22
+ import { check, sleep } from 'k6';
23
+ import { Rate, Trend } from 'k6/metrics';
24
+
25
+ const errorRate = new Rate('errors');
26
+ const orderLatency = new Trend('order_latency');
27
+
28
+ export const options = {
29
+ scenarios: {
30
+ load: {
31
+ executor: 'ramping-vus',
32
+ startVUs: 0,
33
+ stages: [
34
+ { duration: '2m', target: 50 }, // ramp up
35
+ { duration: '5m', target: 50 }, // steady state
36
+ { duration: '2m', target: 0 }, // ramp down
37
+ ],
38
+ },
39
+ },
40
+ thresholds: {
41
+ http_req_duration: ['p(95)<500', 'p(99)<1500'],
42
+ errors: ['rate<0.01'],
43
+ order_latency: ['p(95)<800'],
44
+ },
45
+ };
46
+
47
+ const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';
48
+
49
+ export default function () {
50
+ // GET — list products
51
+ const listRes = http.get(`${BASE_URL}/api/products`);
52
+ check(listRes, {
53
+ 'list status 200': (r) => r.status === 200,
54
+ 'list has items': (r) => JSON.parse(r.body).length > 0,
55
+ }) || errorRate.add(1);
56
+
57
+ sleep(1); // think time between actions
58
+
59
+ // POST — create order
60
+ const payload = JSON.stringify({
61
+ items: [{ sku: 'WIDGET-1', quantity: 1 }],
62
+ });
63
+ const orderRes = http.post(`${BASE_URL}/api/orders`, payload, {
64
+ headers: { 'Content-Type': 'application/json' },
65
+ });
66
+ orderLatency.add(orderRes.timings.duration);
67
+ check(orderRes, {
68
+ 'order status 201': (r) => r.status === 201,
69
+ }) || errorRate.add(1);
70
+
71
+ sleep(2);
72
+ }
73
+ ```
74
+
75
+ ### Smoke Test (CI-friendly)
76
+
77
+ ```javascript
78
+ // tests/performance/smoke-api.js
79
+ import http from 'k6/http';
80
+ import { check } from 'k6';
81
+
82
+ export const options = {
83
+ vus: 1,
84
+ duration: '30s',
85
+ thresholds: {
86
+ http_req_duration: ['p(95)<300'],
87
+ http_req_failed: ['rate<0.01'],
88
+ },
89
+ };
90
+
91
+ export default function () {
92
+ const res = http.get(`${__ENV.BASE_URL}/api/health`);
93
+ check(res, {
94
+ 'status 200': (r) => r.status === 200,
95
+ });
96
+ }
97
+ ```
98
+
99
+ ### Stress Test
100
+
101
+ ```javascript
102
+ // tests/performance/stress-api.js
103
+ export const options = {
104
+ scenarios: {
105
+ stress: {
106
+ executor: 'ramping-vus',
107
+ startVUs: 0,
108
+ stages: [
109
+ { duration: '2m', target: 50 },
110
+ { duration: '3m', target: 100 },
111
+ { duration: '3m', target: 200 }, // beyond expected capacity
112
+ { duration: '3m', target: 400 }, // find the breaking point
113
+ { duration: '2m', target: 0 },
114
+ ],
115
+ },
116
+ },
117
+ thresholds: {
118
+ http_req_duration: ['p(95)<2000'], // relaxed for stress
119
+ http_req_failed: ['rate<0.05'], // 5% error rate acceptable
120
+ },
121
+ };
122
+ ```
123
+
124
+ ### Spike Test
125
+
126
+ ```javascript
127
+ export const options = {
128
+ scenarios: {
129
+ spike: {
130
+ executor: 'ramping-vus',
131
+ startVUs: 0,
132
+ stages: [
133
+ { duration: '1m', target: 10 }, // baseline
134
+ { duration: '10s', target: 500 }, // spike
135
+ { duration: '2m', target: 500 }, // hold spike
136
+ { duration: '10s', target: 10 }, // drop back
137
+ { duration: '2m', target: 10 }, // recovery
138
+ ],
139
+ },
140
+ },
141
+ };
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Artillery Script Example
147
+
148
+ ```yaml
149
+ # tests/performance/load.yml
150
+ config:
151
+ target: "http://localhost:3000"
152
+ phases:
153
+ - duration: 120
154
+ arrivalRate: 10
155
+ name: "Warm up"
156
+ - duration: 300
157
+ arrivalRate: 50
158
+ name: "Sustained load"
159
+ ensure:
160
+ p95: 500
161
+ maxErrorRate: 1
162
+
163
+ scenarios:
164
+ - name: "Browse and order"
165
+ flow:
166
+ - get:
167
+ url: "/api/products"
168
+ capture:
169
+ - json: "$[0].id"
170
+ as: "productId"
171
+ - think: 2
172
+ - post:
173
+ url: "/api/orders"
174
+ json:
175
+ items:
176
+ - productId: "{{ productId }}"
177
+ quantity: 1
178
+ expect:
179
+ - statusCode: 201
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Threshold Definitions
185
+
186
+ Thresholds must be derived from SLOs, not arbitrary numbers.
187
+
188
+ ### Mapping SLOs to Thresholds
189
+
190
+ | SLO | k6 Threshold |
191
+ |---|---|
192
+ | 99.9 % availability | `http_req_failed: ['rate<0.001']` |
193
+ | p95 latency < 500 ms | `http_req_duration: ['p(95)<500']` |
194
+ | p99 latency < 1500 ms | `http_req_duration: ['p(99)<1500']` |
195
+ | Zero errors on critical path | Custom metric with `rate<0.0` |
196
+
197
+ ### Custom Thresholds
198
+
199
+ ```javascript
200
+ export const options = {
201
+ thresholds: {
202
+ // Built-in metrics
203
+ http_req_duration: ['p(95)<500', 'p(99)<1500'],
204
+ http_req_failed: ['rate<0.01'],
205
+
206
+ // Custom metrics (tagged)
207
+ 'http_req_duration{name:createOrder}': ['p(95)<800'],
208
+ 'http_req_duration{name:listProducts}': ['p(95)<200'],
209
+
210
+ // Custom counters
211
+ errors: ['rate<0.01'],
212
+ },
213
+ };
214
+ ```
215
+
216
+ ### Tagging Requests
217
+
218
+ ```javascript
219
+ http.post(`${BASE_URL}/api/orders`, payload, {
220
+ tags: { name: 'createOrder' },
221
+ });
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Production-Like Environment Requirement
227
+
228
+ Performance tests are meaningless unless the environment matches production:
229
+
230
+ ### Checklist
231
+
232
+ - [ ] Same instance types / resource limits (CPU, memory)
233
+ - [ ] Same database engine, version, and dataset size (or representative subset)
234
+ - [ ] Same network topology (load balancer, CDN excluded or simulated)
235
+ - [ ] Same connection pool sizes and timeouts
236
+ - [ ] Same external service latencies (mock with realistic delays)
237
+ - [ ] Same application configuration (caching, rate limits, feature flags)
238
+
239
+ ### Data Volume
240
+
241
+ - Load a representative dataset before running tests.
242
+ - For a 10M-row production table, use at least 1M rows in perf test DB.
243
+ - Index behaviour changes with data volume — small datasets hide slow queries.
244
+
245
+ ### Isolation
246
+
247
+ - Never run performance tests against production.
248
+ - Never share the perf environment with other test suites during a run.
249
+ - Reset environment state between runs for reproducibility.
250
+
251
+ ---
252
+
253
+ ## Baseline Tracking
254
+
255
+ ### Why Track Baselines?
256
+
257
+ A single performance test run is a snapshot. Trends tell the real story.
258
+ Track baselines to detect gradual degradation.
259
+
260
+ ### Process
261
+
262
+ 1. **Establish baseline**: Run load test 3 times on a known-good build.
263
+ Average the results.
264
+ 2. **Store results**: Push metrics to a time-series database (InfluxDB,
265
+ Prometheus) or flat JSON files in the repo.
266
+ 3. **Compare on PR**: Run smoke perf test, compare against baseline.
267
+ Fail if p95 regresses by > 20 %.
268
+ 4. **Update baseline**: After each release, re-run and update.
269
+
270
+ ### k6 Output to JSON
271
+
272
+ ```bash
273
+ k6 run --out json=results.json tests/performance/load-api.js
274
+ ```
275
+
276
+ ### k6 Output to InfluxDB
277
+
278
+ ```bash
279
+ k6 run --out influxdb=http://influxdb:8086/k6 tests/performance/load-api.js
280
+ ```
281
+
282
+ ### Grafana Dashboard
283
+
284
+ Pair InfluxDB with Grafana for visual trend tracking. Key panels:
285
+ - p50 / p95 / p99 latency over time
286
+ - Error rate over time
287
+ - VUs vs response time (to spot saturation)
288
+ - Throughput (req/s) over time
@@ -0,0 +1,143 @@
1
+ # Test Code Review Checklist
2
+
3
+ Use this checklist when reviewing any PR that adds or modifies test code.
4
+ Not every section applies to every PR — skip sections that are not relevant.
5
+
6
+ ---
7
+
8
+ ## 1. Test Level Selection
9
+
10
+ - [ ] Test is at the lowest level that can catch the bug
11
+ - [ ] Unit tests do not involve I/O (network, file system, database)
12
+ - [ ] Integration tests are justified — they test module collaboration, not
13
+ logic that a unit test could cover
14
+ - [ ] E2E tests protect critical user journeys only — not redundant with
15
+ integration tests
16
+ - [ ] Contract tests exist for cross-service API calls
17
+ - [ ] Comment explains why E2E was chosen over integration (if applicable)
18
+
19
+ ---
20
+
21
+ ## 2. Test Quality
22
+
23
+ - [ ] Test name describes behaviour, not implementation
24
+ (`"calculates tax for multi-state order"`, not `"test calculateTax"`)
25
+ - [ ] Each test has a single clear assertion focus (one reason to fail)
26
+ - [ ] Tests are independent — no shared mutable state between tests
27
+ - [ ] No test ordering dependency (would pass with randomised order)
28
+ - [ ] Arrange-Act-Assert structure is clear
29
+ - [ ] No logic in tests (no `if`, no loops, no try/catch) — use
30
+ `test.each` for parameterised cases
31
+ - [ ] No `console.log` or debugging artefacts left in
32
+ - [ ] Test fails for the right reason when the feature is broken
33
+ (verify by mentally removing the feature code)
34
+
35
+ ---
36
+
37
+ ## 3. Test Data
38
+
39
+ - [ ] Uses factories for object creation (not inline literals)
40
+ - [ ] Only overrides fields relevant to the test (sensible defaults for rest)
41
+ - [ ] No hardcoded IDs that could collide across tests
42
+ - [ ] Faker seed is set if deterministic reproduction is needed
43
+ - [ ] Database state is isolated (transaction rollback, truncate, or unique IDs)
44
+ - [ ] No test data leaked between tests (check `beforeEach` / `afterEach`)
45
+
46
+ ---
47
+
48
+ ## 4. Mocking
49
+
50
+ - [ ] Mocks are at system boundaries only (HTTP, clock, random)
51
+ - [ ] No internal modules are mocked — use real implementations
52
+ - [ ] MSW handlers use `onUnhandledRequest: 'error'`
53
+ - [ ] Per-test overrides use `server.use()` and are reset in `afterEach`
54
+ - [ ] Mock responses are realistic (match actual API shape)
55
+ - [ ] Mock contracts are verified against real API (schema validation or
56
+ contract test exists)
57
+ - [ ] `vi.restoreAllMocks()` runs in global `afterEach`
58
+
59
+ ---
60
+
61
+ ## 5. E2E Tests
62
+
63
+ - [ ] Uses Page Object Model (no raw selectors in test files)
64
+ - [ ] Element selection follows priority: `getByRole` > `getByLabel` >
65
+ `getByTestId`
66
+ - [ ] No `waitForTimeout` / `sleep` — uses explicit waits
67
+ - [ ] Test data seeded via API, not through UI interactions
68
+ - [ ] Test is parallelisation-safe (no shared global state)
69
+ - [ ] Viewport is fixed in config (no layout-dependent flakiness)
70
+ - [ ] Animations disabled in test config
71
+ - [ ] Screenshots and traces captured on failure
72
+ - [ ] Test passes with `--repeat-each=5` locally
73
+
74
+ ---
75
+
76
+ ## 6. Assertions
77
+
78
+ - [ ] Assertions are specific (not just `toBeTruthy` or `toMatchSnapshot`)
79
+ - [ ] Error cases are tested, not just happy path
80
+ - [ ] Boundary values are tested where applicable
81
+ - [ ] Negative assertions are used correctly (`not.toContain` vs missing check)
82
+ - [ ] Async assertions use proper `await` / `resolves` / `rejects`
83
+ - [ ] No snapshot tests for logic (snapshots only for serialised output
84
+ like HTML or JSON schema)
85
+
86
+ ---
87
+
88
+ ## 7. Flakiness Risk
89
+
90
+ - [ ] No time-dependent logic without clock mocking
91
+ - [ ] No reliance on execution speed (no "this runs fast enough")
92
+ - [ ] No reliance on random values without seeding
93
+ - [ ] No reliance on network availability (all external calls mocked)
94
+ - [ ] No reliance on file system state (uses temp dirs or mocks)
95
+ - [ ] Database queries do not assume row ordering without `ORDER BY`
96
+
97
+ ---
98
+
99
+ ## 8. Performance
100
+
101
+ - [ ] Unit tests do not perform I/O (should complete in < 50 ms each)
102
+ - [ ] Integration test setup is batched (not N+1 inserts)
103
+ - [ ] E2E tests do not duplicate coverage available at lower levels
104
+ - [ ] No excessive `beforeEach` setup that could be `beforeAll`
105
+ (only when safe — consider isolation)
106
+ - [ ] Test suite stays within time budget (unit < 10 s, integration < 2 min,
107
+ E2E smoke < 10 min)
108
+
109
+ ---
110
+
111
+ ## 9. CI Integration
112
+
113
+ - [ ] Test runs in CI (not just locally)
114
+ - [ ] Test is in the correct pipeline stage (unit/integration/E2E)
115
+ - [ ] Test outputs JUnit XML or other parseable format
116
+ - [ ] Failure produces useful output (assertion messages, screenshots, traces)
117
+ - [ ] No CI-specific environment assumptions without fallback
118
+ (`process.env.CI` checks are guarded)
119
+
120
+ ---
121
+
122
+ ## 10. Coverage
123
+
124
+ - [ ] New code has tests (not just modified code)
125
+ - [ ] Tests cover happy path, error path, and edge cases
126
+ - [ ] If coverage decreased, there is a justification
127
+ - [ ] No tests written solely to increase coverage percentage
128
+ (every test must catch a real bug class)
129
+
130
+ ---
131
+
132
+ ## Quick Reject Criteria
133
+
134
+ Flag the PR immediately if any of these are present:
135
+
136
+ - `sleep()` or `waitForTimeout()` in E2E tests
137
+ - Mocking internal modules
138
+ - Shared mutable state between tests
139
+ - `test.skip` or `test.todo` without a linked issue
140
+ - Snapshot tests for business logic
141
+ - Hardcoded test data that will collide in parallel runs
142
+ - No assertions in a test (or only `toMatchSnapshot`)
143
+ - `console.log` left in test code
@@ -0,0 +1,271 @@
1
+ # Security Testing Reference
2
+
3
+ ## OWASP Top 10 — Test Coverage
4
+
5
+ ### SQL Injection
6
+
7
+ Test every user-controlled input that reaches a database query.
8
+
9
+ ```typescript
10
+ const sqlInjectionPayloads = [
11
+ "'; DROP TABLE users; --",
12
+ "1' OR '1'='1",
13
+ "1; SELECT * FROM users",
14
+ "' UNION SELECT username, password FROM users --",
15
+ "1' AND SLEEP(5) --",
16
+ ];
17
+
18
+ test.each(sqlInjectionPayloads)('rejects SQL injection: %s', async (payload) => {
19
+ const res = await request(app)
20
+ .get('/api/users')
21
+ .query({ search: payload });
22
+
23
+ // Should NOT return 500 (indicates unhandled query error)
24
+ expect(res.status).not.toBe(500);
25
+ // Should NOT return other users' data
26
+ expect(res.body).not.toContainEqual(
27
+ expect.objectContaining({ email: expect.stringContaining('@') }),
28
+ );
29
+ });
30
+ ```
31
+
32
+ **Prevention**: Use parameterised queries exclusively. Never concatenate user
33
+ input into SQL strings. ORM usage does not guarantee safety — verify raw
34
+ queries and `whereRaw` calls.
35
+
36
+ ### Cross-Site Scripting (XSS)
37
+
38
+ ```typescript
39
+ const xssPayloads = [
40
+ '<script>alert("xss")</script>',
41
+ '<img src=x onerror=alert("xss")>',
42
+ '"><svg onload=alert("xss")>',
43
+ "javascript:alert('xss')",
44
+ '<div onmouseover="alert(\'xss\')">hover</div>',
45
+ ];
46
+
47
+ test.each(xssPayloads)('sanitises XSS in user input: %s', async (payload) => {
48
+ // Submit payload via API
49
+ await request(app)
50
+ .post('/api/comments')
51
+ .send({ body: payload });
52
+
53
+ // Retrieve and verify output is escaped
54
+ const res = await request(app).get('/api/comments');
55
+ const comment = res.body[res.body.length - 1];
56
+ expect(comment.body).not.toContain('<script');
57
+ expect(comment.body).not.toContain('onerror');
58
+ expect(comment.body).not.toContain('onload');
59
+ expect(comment.body).not.toContain('onmouseover');
60
+ });
61
+ ```
62
+
63
+ **Prevention**: Escape output by default (React does this). Sanitise HTML
64
+ input with a strict allowlist (DOMPurify). Set `Content-Security-Policy`
65
+ headers.
66
+
67
+ ### Insecure Direct Object Reference (IDOR)
68
+
69
+ Test that users cannot access resources belonging to other users by
70
+ manipulating IDs.
71
+
72
+ ```typescript
73
+ test('user cannot access another user\'s order', async () => {
74
+ // Create order as user A
75
+ const orderRes = await request(app)
76
+ .post('/api/orders')
77
+ .set('Authorization', `Bearer ${userAToken}`)
78
+ .send({ items: [{ sku: 'WIDGET-1', qty: 1 }] });
79
+ const orderId = orderRes.body.id;
80
+
81
+ // Attempt access as user B
82
+ const res = await request(app)
83
+ .get(`/api/orders/${orderId}`)
84
+ .set('Authorization', `Bearer ${userBToken}`);
85
+
86
+ expect(res.status).toBe(403);
87
+ });
88
+
89
+ test('user cannot update another user\'s profile', async () => {
90
+ const res = await request(app)
91
+ .patch(`/api/users/${userAId}`)
92
+ .set('Authorization', `Bearer ${userBToken}`)
93
+ .send({ name: 'Hacked' });
94
+
95
+ expect(res.status).toBe(403);
96
+ });
97
+ ```
98
+
99
+ **Prevention**: Always check resource ownership in the authorization layer.
100
+ Never rely on obscured IDs (UUIDs) as a security mechanism.
101
+
102
+ ### Authentication Bypass
103
+
104
+ ```typescript
105
+ describe('Auth bypass checks', () => {
106
+ const protectedEndpoints = [
107
+ { method: 'GET', path: '/api/users/me' },
108
+ { method: 'POST', path: '/api/orders' },
109
+ { method: 'DELETE', path: '/api/users/123' },
110
+ { method: 'GET', path: '/api/admin/dashboard' },
111
+ ];
112
+
113
+ test.each(protectedEndpoints)(
114
+ '$method $path returns 401 without token',
115
+ async ({ method, path }) => {
116
+ const res = await request(app)[method.toLowerCase()](path);
117
+ expect(res.status).toBe(401);
118
+ },
119
+ );
120
+
121
+ test('expired token is rejected', async () => {
122
+ const expiredToken = signToken({ userId: '1' }, { expiresIn: '-1h' });
123
+ const res = await request(app)
124
+ .get('/api/users/me')
125
+ .set('Authorization', `Bearer ${expiredToken}`);
126
+ expect(res.status).toBe(401);
127
+ });
128
+
129
+ test('tampered token is rejected', async () => {
130
+ const token = signToken({ userId: '1', role: 'member' });
131
+ // Modify payload to escalate role
132
+ const [header, , signature] = token.split('.');
133
+ const tamperedPayload = btoa(JSON.stringify({ userId: '1', role: 'admin' }));
134
+ const tamperedToken = `${header}.${tamperedPayload}.${signature}`;
135
+
136
+ const res = await request(app)
137
+ .get('/api/admin/dashboard')
138
+ .set('Authorization', `Bearer ${tamperedToken}`);
139
+ expect(res.status).toBe(401);
140
+ });
141
+ });
142
+ ```
143
+
144
+ ---
145
+
146
+ ## DAST with OWASP ZAP
147
+
148
+ Dynamic Application Security Testing scans a running application for
149
+ vulnerabilities.
150
+
151
+ ### Automated Scan in CI
152
+
153
+ ```yaml
154
+ # .github/workflows/security.yml
155
+ name: DAST Scan
156
+ on:
157
+ schedule:
158
+ - cron: '0 3 * * 1' # Weekly Monday 3 AM
159
+ workflow_dispatch:
160
+
161
+ jobs:
162
+ zap-scan:
163
+ runs-on: ubuntu-latest
164
+ services:
165
+ app:
166
+ image: your-app:latest
167
+ ports: ['3000:3000']
168
+ steps:
169
+ - name: ZAP Baseline Scan
170
+ uses: zaproxy/action-baseline@v0.12.0
171
+ with:
172
+ target: 'http://localhost:3000'
173
+ rules_file_name: '.zap/rules.tsv'
174
+ cmd_options: '-a -j'
175
+
176
+ - name: Upload ZAP Report
177
+ uses: actions/upload-artifact@v4
178
+ if: always()
179
+ with:
180
+ name: zap-report
181
+ path: report_html.html
182
+ ```
183
+
184
+ ### Custom Rules File
185
+
186
+ ```tsv
187
+ # .zap/rules.tsv — customize alert thresholds
188
+ # ID Action (IGNORE, WARN, FAIL)
189
+ 10010 IGNORE # Cookie No HttpOnly Flag (handled by framework)
190
+ 10011 FAIL # Cookie Without Secure Flag
191
+ 10015 FAIL # Incomplete or No Cache-control Header
192
+ 10021 FAIL # X-Content-Type-Options Header Missing
193
+ 10038 FAIL # Content Security Policy Header Not Set
194
+ 40012 FAIL # Cross Site Scripting (Reflected)
195
+ 40014 FAIL # Cross Site Scripting (Persistent)
196
+ 90022 FAIL # Application Error Disclosure
197
+ ```
198
+
199
+ ### Authenticated Scan
200
+
201
+ For scanning behind login, provide ZAP with auth context:
202
+
203
+ ```yaml
204
+ - name: ZAP Full Scan
205
+ uses: zaproxy/action-full-scan@v0.10.0
206
+ with:
207
+ target: 'http://localhost:3000'
208
+ cmd_options: >
209
+ -z "-config auth.method=2
210
+ -config auth.method.loginindicator=Dashboard
211
+ -config auth.method.logoutindicator=Sign in"
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Rate Limit Verification
217
+
218
+ Test that rate limits are enforced and return proper responses.
219
+
220
+ ```typescript
221
+ describe('Rate limiting', () => {
222
+ test('login endpoint rate-limits after 5 attempts', async () => {
223
+ const results = [];
224
+
225
+ for (let i = 0; i < 10; i++) {
226
+ const res = await request(app)
227
+ .post('/api/auth/login')
228
+ .send({ email: 'test@test.com', password: 'wrong' });
229
+ results.push(res.status);
230
+ }
231
+
232
+ // First 5 should be 401 (wrong password)
233
+ expect(results.slice(0, 5).every((s) => s === 401)).toBe(true);
234
+ // Remaining should be 429 (rate limited)
235
+ expect(results.slice(5).every((s) => s === 429)).toBe(true);
236
+ });
237
+
238
+ test('rate limit returns Retry-After header', async () => {
239
+ // Exhaust rate limit
240
+ for (let i = 0; i < 6; i++) {
241
+ await request(app)
242
+ .post('/api/auth/login')
243
+ .send({ email: 'test@test.com', password: 'wrong' });
244
+ }
245
+
246
+ const res = await request(app)
247
+ .post('/api/auth/login')
248
+ .send({ email: 'test@test.com', password: 'wrong' });
249
+
250
+ expect(res.status).toBe(429);
251
+ expect(res.headers['retry-after']).toBeDefined();
252
+ expect(parseInt(res.headers['retry-after'])).toBeGreaterThan(0);
253
+ });
254
+
255
+ test('rate limit resets after window', async () => {
256
+ // This test requires clock mocking or a short rate limit window in test config
257
+ // Exhaust limit, advance time, verify access restored
258
+ });
259
+ });
260
+ ```
261
+
262
+ ### Additional Security Checks
263
+
264
+ - **CORS**: Verify `Access-Control-Allow-Origin` does not include `*` on
265
+ authenticated endpoints.
266
+ - **Headers**: Verify `X-Content-Type-Options: nosniff`,
267
+ `Strict-Transport-Security`, `X-Frame-Options`.
268
+ - **Sensitive data in URLs**: Verify tokens and passwords are never in query
269
+ strings (they appear in logs).
270
+ - **Error messages**: Verify error responses do not leak stack traces,
271
+ database schema, or internal paths.