@jaimevalasek/aioson 1.28.0 → 1.29.1
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/CHANGELOG.md +23 -0
- package/docs/pt/4-agentes/briefing.md +2 -0
- package/docs/pt/4-agentes/copywriter.md +2 -0
- package/docs/pt/4-agentes/genome.md +1 -0
- package/docs/pt/4-agentes/profiler-enricher.md +2 -0
- package/docs/pt/4-agentes/profiler-forge.md +2 -0
- package/docs/pt/4-agentes/sheldon.md +2 -0
- package/docs/pt/4-agentes/squad.md +12 -10
- package/docs/pt/5-referencia/comandos-cli.md +2 -0
- package/docs/pt/5-referencia/memoria-e-contexto.md +60 -0
- package/package.json +1 -1
- package/src/cli.js +5 -0
- package/src/commands/rules-lint.js +116 -0
- package/src/context-selector.js +29 -5
- package/template/.aioson/agents/analyst.md +57 -41
- package/template/.aioson/agents/architect.md +40 -33
- package/template/.aioson/agents/briefing.md +96 -81
- package/template/.aioson/agents/copywriter.md +34 -2
- package/template/.aioson/agents/discover.md +5 -8
- package/template/.aioson/agents/discovery-design-doc.md +42 -35
- package/template/.aioson/agents/genome.md +3 -1
- package/template/.aioson/agents/orache.md +6 -15
- package/template/.aioson/agents/orchestrator.md +38 -31
- package/template/.aioson/agents/pm.md +7 -0
- package/template/.aioson/agents/product.md +146 -174
- package/template/.aioson/agents/profiler-enricher.md +19 -0
- package/template/.aioson/agents/profiler-forge.md +6 -0
- package/template/.aioson/agents/qa.md +18 -10
- package/template/.aioson/agents/scope-check.md +6 -0
- package/template/.aioson/agents/sheldon.md +30 -14
- package/template/.aioson/agents/site-forge.md +2 -6
- package/template/.aioson/agents/squad.md +5 -12
- package/template/.aioson/agents/tester.md +29 -23
- package/template/.aioson/agents/ux-ui.md +43 -36
- package/template/.aioson/agents/validator.md +2 -2
- package/template/.aioson/docs/LAYERS.md +2 -0
- package/template/.aioson/docs/autonomy-protocol.md +7 -5
- package/template/.aioson/docs/autopilot-handoff.md +2 -0
- package/template/.aioson/docs/dev/execution-discipline.md +3 -0
- package/template/.aioson/docs/dev/simple-plan-lane.md +95 -92
- package/template/.aioson/docs/dev/stack-conventions.md +3 -0
- package/template/.aioson/docs/deyvin/continuity-recovery.md +21 -18
- package/template/.aioson/docs/deyvin/debugging-escalation.md +3 -0
- package/template/.aioson/docs/deyvin/pair-execution.md +3 -0
- package/template/.aioson/docs/deyvin/runtime-handoffs.md +3 -0
- package/template/.aioson/docs/dossier/agent-templates.md +3 -0
- package/template/.aioson/docs/dossier/schema.md +3 -0
- package/template/.aioson/docs/example-external-api-context.md +2 -0
- package/template/.aioson/docs/handoff-persistence.md +96 -94
- package/template/.aioson/docs/pentester/app-playbooks.md +3 -0
- package/template/.aioson/docs/pentester/browser-dast-playbook.md +401 -398
- package/template/.aioson/docs/pentester/llm-supplychain.md +3 -0
- package/template/.aioson/docs/quality/code-health-analysis.md +2 -0
- package/template/.aioson/docs/sheldon/enrichment-paths.md +3 -0
- package/template/.aioson/docs/sheldon/harness-contract.md +3 -0
- package/template/.aioson/docs/sheldon/quality-lens.md +3 -0
- package/template/.aioson/docs/sheldon/research-loop.md +3 -0
- package/template/.aioson/docs/sheldon/web-intelligence.md +3 -0
- package/template/.aioson/docs/site-forge-build.md +4 -2
- package/template/.aioson/docs/site-forge-extraction.md +2 -0
- package/template/.aioson/docs/site-forge-qa.md +2 -0
- package/template/.aioson/docs/site-forge-recon.md +7 -5
- package/template/.aioson/docs/site-forge-transform.md +2 -0
- package/template/.aioson/docs/squad/content-output.md +3 -0
- package/template/.aioson/docs/squad/creation-flow.md +22 -1
- package/template/.aioson/docs/squad/domain-breadth.md +3 -0
- package/template/.aioson/docs/squad/domain-classification.md +3 -0
- package/template/.aioson/docs/squad/eval-gate.md +3 -0
- package/template/.aioson/docs/squad/genome-bindings.md +14 -0
- package/template/.aioson/docs/squad/package-contract.md +5 -0
- package/template/.aioson/docs/squad/persona-grounding.md +65 -62
- package/template/.aioson/docs/squad/quality-lens.md +3 -0
- package/template/.aioson/docs/squad/research-loop.md +3 -0
- package/template/.aioson/docs/squad/session-operations.md +3 -0
- package/template/.aioson/docs/squad/workflow-quality.md +3 -0
- package/template/.aioson/docs/tester/coverage-quality.md +3 -0
- package/template/.aioson/rules/README.md +28 -0
- package/template/.aioson/rules/agent-language-policy.md +26 -21
- package/template/.aioson/rules/agent-structural-contract.md +5 -0
- package/template/.aioson/rules/aioson-context-boundary.md +6 -1
- package/template/.aioson/rules/canonical-path-contract.md +15 -10
- package/template/.aioson/rules/data-format-convention.md +16 -11
- package/template/.aioson/rules/disk-first-artifacts.md +10 -6
- package/template/.aioson/rules/example-monetary-values.md +4 -0
- package/template/.aioson/rules/output-brevity.md +2 -0
- package/template/.aioson/rules/prd-section-ownership.md +17 -12
- package/template/.aioson/rules/security-baseline.md +4 -0
- package/template/.aioson/rules/simple-plan-lane.md +5 -0
- package/template/.aioson/rules/spec-level-ownership.md +10 -5
- package/template/.aioson/rules/squad-driver-pattern.md +5 -0
- package/template/.aioson/tasks/squad-create.md +11 -0
- package/template/.aioson/tasks/squad-design.md +3 -3
- package/template/AGENTS.md +1 -1
- package/template/CLAUDE.md +1 -1
|
@@ -1,398 +1,401 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Pentester browser DAST playbook — Playwright-based dynamic security probes for app_target surface TS-A08 (browser_exposure). Load when review_contract.target_mode = app_target AND the application has a browser-accessible UI."
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
aioson qa:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
|
41
|
-
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `X-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (!cookie.
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
|
122
|
-
|
|
123
|
-
|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
await page.
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
/
|
|
303
|
-
/
|
|
304
|
-
/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
"
|
|
360
|
-
"
|
|
361
|
-
"
|
|
362
|
-
"
|
|
363
|
-
"
|
|
364
|
-
"
|
|
365
|
-
"
|
|
366
|
-
"
|
|
367
|
-
"
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
"
|
|
371
|
-
"
|
|
372
|
-
"
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
|
382
|
-
|
|
383
|
-
|
|
|
384
|
-
|
|
|
385
|
-
|
|
|
386
|
-
|
|
|
387
|
-
|
|
|
388
|
-
|
|
|
389
|
-
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
-
|
|
1
|
+
---
|
|
2
|
+
description: "Pentester browser DAST playbook — Playwright-based dynamic security probes for app_target surface TS-A08 (browser_exposure). Load when review_contract.target_mode = app_target AND the application has a browser-accessible UI."
|
|
3
|
+
agents: [pentester]
|
|
4
|
+
task_types: [security, dast]
|
|
5
|
+
triggers: [browser dast, dynamic scan, playwright security]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Pentester — Browser DAST Playbook
|
|
9
|
+
|
|
10
|
+
Load this when `review_contract.target_mode = app_target` AND the target application serves a browser-accessible UI (web app, SPA, SSR page).
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
1. Application must be running and accessible at a known URL.
|
|
15
|
+
2. Playwright installed: `npm install -g playwright && npx playwright install chromium`
|
|
16
|
+
3. Verify readiness: `aioson qa:doctor`
|
|
17
|
+
|
|
18
|
+
## Phase 0 — Baseline DAST (mandatory pre-step)
|
|
19
|
+
|
|
20
|
+
Before running any manual Playwright probe, execute the AIOSON automated DAST baseline:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Full run with hacker persona (secrets, XSS, IDOR, open redirect, SQL injection, debug routes)
|
|
24
|
+
aioson qa:run --persona=hacker --url=<target-url>
|
|
25
|
+
|
|
26
|
+
# Autonomous crawl scan (discovers routes, probes each one)
|
|
27
|
+
aioson qa:scan --url=<target-url> --depth=3 --max-pages=50
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Read `aios-qa-report.json` and import any `critical` or `high` findings into the pentester findings artifact as `status: needs_validation`. The pentester validates or reclassifies each — `qa:run` automates detection, pentester applies adversarial judgment.
|
|
31
|
+
|
|
32
|
+
**Do NOT skip Phase 0.** The automated baseline catches low-hanging fruit (exposed secrets, accessible .env files, basic XSS) without burning manual probe time. Phase 1+ targets what automation misses.
|
|
33
|
+
|
|
34
|
+
## Phase 1 — Security Headers Audit
|
|
35
|
+
|
|
36
|
+
Navigate to the target URL with Playwright and capture response headers on the main document request.
|
|
37
|
+
|
|
38
|
+
### Mandatory headers to check
|
|
39
|
+
|
|
40
|
+
| Header | Expected | Severity if missing/weak |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `Content-Security-Policy` | Present, no `unsafe-inline` for scripts, no `unsafe-eval` | `high` if missing entirely, `medium` if has `unsafe-inline`/`unsafe-eval` |
|
|
43
|
+
| `Strict-Transport-Security` | `max-age>=31536000; includeSubDomains` | `medium` (localhost exempt) |
|
|
44
|
+
| `X-Content-Type-Options` | `nosniff` | `medium` |
|
|
45
|
+
| `X-Frame-Options` | `DENY` or `SAMEORIGIN` (check CSP `frame-ancestors` too) | `medium` |
|
|
46
|
+
| `Referrer-Policy` | `strict-origin-when-cross-origin` or stricter | `low` |
|
|
47
|
+
| `Permissions-Policy` | Present, restricts `camera`, `microphone`, `geolocation` at minimum | `low` |
|
|
48
|
+
| `X-XSS-Protection` | `0` (modern recommendation — CSP supersedes; `1; mode=block` causes info leaks in old IE) | `info` |
|
|
49
|
+
|
|
50
|
+
### Headers that SHOULD NOT be present
|
|
51
|
+
|
|
52
|
+
| Header | Why | Severity |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `Server` with version (e.g. `nginx/1.24.0`) | Reveals infrastructure version — aids targeted exploits | `low` |
|
|
55
|
+
| `X-Powered-By` | Reveals framework (Express, PHP, ASP.NET) | `low` |
|
|
56
|
+
| `X-AspNet-Version` / `X-AspNetMvc-Version` | .NET version disclosure | `low` |
|
|
57
|
+
|
|
58
|
+
### Playwright probe pattern
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
const response = await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
|
|
62
|
+
const headers = response.headers();
|
|
63
|
+
|
|
64
|
+
// Check presence and value of each security header
|
|
65
|
+
const csp = headers['content-security-policy'] || '';
|
|
66
|
+
const hsts = headers['strict-transport-security'] || '';
|
|
67
|
+
const xcto = headers['x-content-type-options'] || '';
|
|
68
|
+
const xfo = headers['x-frame-options'] || '';
|
|
69
|
+
const rp = headers['referrer-policy'] || '';
|
|
70
|
+
const pp = headers['permissions-policy'] || '';
|
|
71
|
+
|
|
72
|
+
// Disclosure headers (should NOT be present with version info)
|
|
73
|
+
const server = headers['server'] || '';
|
|
74
|
+
const poweredBy = headers['x-powered-by'] || '';
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**ASVS:** V3.4 (CSP), V12.1 (HSTS), V13.1.5 (security headers), V14.3.3 (server disclosure).
|
|
78
|
+
|
|
79
|
+
## Phase 2 — Cookie Security Audit
|
|
80
|
+
|
|
81
|
+
After authentication (if credentials are available), inspect all cookies set by the application.
|
|
82
|
+
|
|
83
|
+
### Required attributes for session/auth cookies
|
|
84
|
+
|
|
85
|
+
| Attribute | Expected | Severity if missing |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `Secure` | `true` (except localhost) | `high` |
|
|
88
|
+
| `HttpOnly` | `true` for session tokens | `high` |
|
|
89
|
+
| `SameSite` | `Lax` or `Strict` | `medium` |
|
|
90
|
+
| `Path` | Narrowest scope needed (prefer `/` only if necessary) | `info` |
|
|
91
|
+
| `__Host-` prefix | Recommended for session cookies — enforces Secure + Path=/ + no Domain | `info` |
|
|
92
|
+
| `__Secure-` prefix | Alternative — enforces Secure flag | `info` |
|
|
93
|
+
| Max-Age / Expires | Session cookies should not persist beyond browser close unless explicitly justified | `low` |
|
|
94
|
+
|
|
95
|
+
### Playwright probe pattern
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const cookies = await context.cookies();
|
|
99
|
+
for (const cookie of cookies) {
|
|
100
|
+
const isSession = /session|token|auth|sid|jwt/i.test(cookie.name);
|
|
101
|
+
if (isSession) {
|
|
102
|
+
// Check Secure flag
|
|
103
|
+
if (!cookie.secure) { /* HIGH finding */ }
|
|
104
|
+
// Check HttpOnly flag
|
|
105
|
+
if (!cookie.httpOnly) { /* HIGH finding */ }
|
|
106
|
+
// Check SameSite
|
|
107
|
+
if (cookie.sameSite === 'None' && !cookie.secure) { /* MEDIUM finding */ }
|
|
108
|
+
if (!cookie.sameSite || cookie.sameSite === 'None') { /* MEDIUM finding — CSRF surface */ }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**ASVS:** V7.1.1 (cookie attributes), V7.1.2 (HttpOnly), V7.1.3 (SameSite).
|
|
114
|
+
|
|
115
|
+
## Phase 3 — Client-Side Storage Audit
|
|
116
|
+
|
|
117
|
+
Check localStorage and sessionStorage for sensitive data that should never be stored client-side.
|
|
118
|
+
|
|
119
|
+
### Sensitive patterns to flag
|
|
120
|
+
|
|
121
|
+
| Pattern | Risk | Severity |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| JWT tokens (eyJ...) | Token theft via XSS gives full account takeover | `high` |
|
|
124
|
+
| API keys (sk-*, pk_*, AKIA*, AIzaSy*) | Direct API abuse | `critical` |
|
|
125
|
+
| Passwords / password hashes | Direct compromise | `critical` |
|
|
126
|
+
| PII (email + name + phone + address combined) | Privacy violation, GDPR exposure | `medium` |
|
|
127
|
+
| Credit card numbers / CVV | PCI-DSS violation | `critical` |
|
|
128
|
+
| Session IDs | Session hijacking via XSS | `high` |
|
|
129
|
+
|
|
130
|
+
### Playwright probe pattern
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const storageData = await page.evaluate(() => {
|
|
134
|
+
const data = { localStorage: {}, sessionStorage: {} };
|
|
135
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
136
|
+
const key = localStorage.key(i);
|
|
137
|
+
data.localStorage[key] = localStorage.getItem(key);
|
|
138
|
+
}
|
|
139
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
140
|
+
const key = sessionStorage.key(i);
|
|
141
|
+
data.sessionStorage[key] = sessionStorage.getItem(key);
|
|
142
|
+
}
|
|
143
|
+
return data;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Check each value against SECRET_PATTERNS and JWT regex
|
|
147
|
+
const jwtRegex = /eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**ASVS:** V14.1.3 (sensitive data in client storage), V8.2.2 (no sensitive data in browser storage).
|
|
151
|
+
|
|
152
|
+
## Phase 4 — CORS Misconfiguration
|
|
153
|
+
|
|
154
|
+
Test the server's CORS policy by sending requests with crafted `Origin` headers.
|
|
155
|
+
|
|
156
|
+
### Probes
|
|
157
|
+
|
|
158
|
+
1. **Wildcard origin**: send `Origin: https://evil.com` — if response has `Access-Control-Allow-Origin: *`, flag it.
|
|
159
|
+
2. **Reflected origin**: send `Origin: https://evil.com` — if response reflects `Access-Control-Allow-Origin: https://evil.com`, flag it.
|
|
160
|
+
3. **Null origin**: send `Origin: null` — some misconfigured servers allow this.
|
|
161
|
+
4. **Credentials with wildcard**: `Access-Control-Allow-Credentials: true` with `Access-Control-Allow-Origin: *` is a browser-rejected but server-misconfigured pattern.
|
|
162
|
+
5. **Subdomain regex bypass**: `Origin: https://evil-example.com` or `Origin: https://example.com.evil.com` — catches poor regex matching.
|
|
163
|
+
|
|
164
|
+
### Playwright probe pattern
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
// Playwright doesn't set custom Origin on navigation, use page.evaluate with fetch
|
|
168
|
+
const corsResult = await page.evaluate(async (targetUrl) => {
|
|
169
|
+
try {
|
|
170
|
+
const res = await fetch(targetUrl, { mode: 'cors', credentials: 'include' });
|
|
171
|
+
return {
|
|
172
|
+
acao: res.headers.get('access-control-allow-origin'),
|
|
173
|
+
acac: res.headers.get('access-control-allow-credentials')
|
|
174
|
+
};
|
|
175
|
+
} catch { return { blocked: true }; }
|
|
176
|
+
}, apiEndpoint);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
For deeper CORS testing, use `page.route()` to intercept and modify request headers, or use the Playwright `request` API directly.
|
|
180
|
+
|
|
181
|
+
**ASVS:** V4.3.1, V4.3.2 (CORS policy).
|
|
182
|
+
|
|
183
|
+
## Phase 5 — Source Map Exposure
|
|
184
|
+
|
|
185
|
+
Source maps (`.js.map`) in production reveal the full original source code, including comments, variable names, and internal logic.
|
|
186
|
+
|
|
187
|
+
### Probes
|
|
188
|
+
|
|
189
|
+
1. Capture all JS file URLs loaded by the page.
|
|
190
|
+
2. For each `*.js` URL, try fetching `*.js.map`.
|
|
191
|
+
3. Check the HTML source for `//# sourceMappingURL=` directives.
|
|
192
|
+
4. Check response headers for `SourceMap:` or `X-SourceMap:` headers.
|
|
193
|
+
|
|
194
|
+
### Playwright probe pattern
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
const jsFiles = [];
|
|
198
|
+
page.on('response', (response) => {
|
|
199
|
+
if (response.url().endsWith('.js') && response.status() === 200) {
|
|
200
|
+
jsFiles.push(response.url());
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await page.goto(targetUrl, { waitUntil: 'networkidle' });
|
|
205
|
+
|
|
206
|
+
for (const jsUrl of jsFiles) {
|
|
207
|
+
const mapUrl = jsUrl + '.map';
|
|
208
|
+
try {
|
|
209
|
+
const mapResponse = await page.goto(mapUrl, { waitUntil: 'commit', timeout: 3000 });
|
|
210
|
+
if (mapResponse && mapResponse.status() === 200) {
|
|
211
|
+
const body = await mapResponse.text();
|
|
212
|
+
if (body.includes('"sources"') || body.includes('"mappings"')) {
|
|
213
|
+
// HIGH finding — source map accessible
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch { /* not accessible — good */ }
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Severity:** `high` (reveals full source code including business logic, API routes, internal comments).
|
|
221
|
+
|
|
222
|
+
**Fix:** Remove source maps from production builds. Configure bundler (webpack/vite/next) to exclude `.map` files from production output, or restrict via web server rules.
|
|
223
|
+
|
|
224
|
+
## Phase 6 — Clickjacking (Frame Injection)
|
|
225
|
+
|
|
226
|
+
Test if the target page can be embedded in an iframe on an attacker-controlled domain.
|
|
227
|
+
|
|
228
|
+
### Probes
|
|
229
|
+
|
|
230
|
+
1. Check `X-Frame-Options` header (covered in Phase 1).
|
|
231
|
+
2. Check CSP `frame-ancestors` directive (more granular than X-Frame-Options).
|
|
232
|
+
3. If neither is set, attempt to iframe the page:
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
// Create a test page that iframes the target
|
|
236
|
+
await page.setContent(`
|
|
237
|
+
<iframe id="target" src="${targetUrl}" width="800" height="600"></iframe>
|
|
238
|
+
`);
|
|
239
|
+
await page.waitForTimeout(2000);
|
|
240
|
+
|
|
241
|
+
const iframeLoaded = await page.evaluate(() => {
|
|
242
|
+
const iframe = document.getElementById('target');
|
|
243
|
+
try {
|
|
244
|
+
// Cross-origin iframe access will throw — but loading without error means framing is allowed
|
|
245
|
+
return iframe.contentWindow.location.href !== 'about:blank';
|
|
246
|
+
} catch { return true; } // cross-origin = loaded but blocked by same-origin policy (still frameable)
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**ASVS:** V3.7 (clickjacking), V13.1.5 (frame protection).
|
|
251
|
+
|
|
252
|
+
## Phase 7 — Subresource Integrity (SRI)
|
|
253
|
+
|
|
254
|
+
Scripts and stylesheets loaded from CDNs or third-party origins should have `integrity` attributes to prevent supply-chain attacks (CDN compromise, DNS hijacking).
|
|
255
|
+
|
|
256
|
+
### Probes
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
const sriIssues = await page.evaluate(() => {
|
|
260
|
+
const issues = [];
|
|
261
|
+
const scripts = document.querySelectorAll('script[src]');
|
|
262
|
+
for (const s of scripts) {
|
|
263
|
+
const src = s.getAttribute('src') || '';
|
|
264
|
+
const isExternal = src.startsWith('http') && !src.includes(window.location.hostname);
|
|
265
|
+
if (isExternal && !s.getAttribute('integrity')) {
|
|
266
|
+
issues.push({ type: 'script', src: src.substring(0, 120) });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const links = document.querySelectorAll('link[rel="stylesheet"][href]');
|
|
270
|
+
for (const l of links) {
|
|
271
|
+
const href = l.getAttribute('href') || '';
|
|
272
|
+
const isExternal = href.startsWith('http') && !href.includes(window.location.hostname);
|
|
273
|
+
if (isExternal && !l.getAttribute('integrity')) {
|
|
274
|
+
issues.push({ type: 'stylesheet', src: href.substring(0, 120) });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return issues;
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Severity:** `medium` per external resource without SRI. `high` if the resource is a payment or auth SDK.
|
|
282
|
+
|
|
283
|
+
**ASVS:** V15.2 (SRI).
|
|
284
|
+
|
|
285
|
+
## Phase 8 — Error Page Information Disclosure
|
|
286
|
+
|
|
287
|
+
Trigger error conditions and inspect the response for framework-default error pages that leak internal information.
|
|
288
|
+
|
|
289
|
+
### Probes
|
|
290
|
+
|
|
291
|
+
1. **404 page**: navigate to a random non-existent path. Check for stack traces, framework names, file paths.
|
|
292
|
+
2. **500 trigger**: if any form/endpoint is accessible, submit malformed data designed to cause server errors.
|
|
293
|
+
3. **Verbose errors**: check if the response contains path separators (`/`, `\`), line numbers (`at line`), or framework markers (`Django`, `Rails`, `Express`, `Next.js`, `Laravel`, `Spring`).
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
const errorPage = await page.goto(`${targetUrl}/nonexistent-${Date.now()}`, {
|
|
297
|
+
waitUntil: 'domcontentloaded', timeout: 5000
|
|
298
|
+
});
|
|
299
|
+
const errorHtml = await page.content();
|
|
300
|
+
|
|
301
|
+
const leakPatterns = [
|
|
302
|
+
/at\s+\w+\s+\(.*:\d+:\d+\)/, // JS stack trace
|
|
303
|
+
/File ".*", line \d+/, // Python traceback
|
|
304
|
+
/vendor\/.*\.php:\d+/, // PHP stack trace
|
|
305
|
+
/SQLSTATE\[/, // SQL error
|
|
306
|
+
/Traceback \(most recent/, // Python traceback header
|
|
307
|
+
/(Django|Laravel|Rails|Express|Spring|Next\.js) (Debug|Error)/i
|
|
308
|
+
];
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Severity:** `medium` for framework name disclosure, `high` for full stack traces with file paths.
|
|
312
|
+
|
|
313
|
+
**ASVS:** V13.1.3 (error handling), V16.2 (information disclosure).
|
|
314
|
+
|
|
315
|
+
## Phase 9 — HTML Meta & Comment Leaks
|
|
316
|
+
|
|
317
|
+
Inspect the HTML source for metadata and comments that disclose internal information.
|
|
318
|
+
|
|
319
|
+
### Probes
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
const metaLeaks = await page.evaluate(() => {
|
|
323
|
+
const issues = [];
|
|
324
|
+
|
|
325
|
+
// Generator meta tags
|
|
326
|
+
const generator = document.querySelector('meta[name="generator"]');
|
|
327
|
+
if (generator) issues.push({ type: 'generator', value: generator.content });
|
|
328
|
+
|
|
329
|
+
// HTML comments with sensitive content
|
|
330
|
+
const walker = document.createTreeWalker(document, NodeFilter.SHOW_COMMENT);
|
|
331
|
+
while (walker.nextNode()) {
|
|
332
|
+
const text = walker.currentNode.textContent.trim();
|
|
333
|
+
if (/(TODO|FIXME|HACK|password|secret|key|token|debug)/i.test(text)) {
|
|
334
|
+
issues.push({ type: 'comment', value: text.substring(0, 100) });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Hidden inputs with sensitive-looking names
|
|
339
|
+
const hiddenInputs = document.querySelectorAll('input[type="hidden"]');
|
|
340
|
+
for (const input of hiddenInputs) {
|
|
341
|
+
const name = input.name || '';
|
|
342
|
+
if (/(token|secret|key|csrf|nonce)/i.test(name) && input.value.length > 20) {
|
|
343
|
+
issues.push({ type: 'hidden_input', name, valueLength: input.value.length });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return issues;
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Severity:** `low` for generator tags, `medium` for TODO/debug comments, `info` for CSRF tokens in hidden inputs (expected pattern, but verify they rotate).
|
|
352
|
+
|
|
353
|
+
## Integration with pentester findings artifact
|
|
354
|
+
|
|
355
|
+
Map every finding from this playbook to the standard pentester finding schema:
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"id": "SF-{slug}-NN",
|
|
360
|
+
"feature_slug": "{slug}",
|
|
361
|
+
"surface": "app_target_browser_exposure",
|
|
362
|
+
"severity": "...",
|
|
363
|
+
"title": "...",
|
|
364
|
+
"hypothesis": "Browser-based DAST probe via Playwright",
|
|
365
|
+
"preconditions": ["Application running at {url}", "Playwright + Chromium installed"],
|
|
366
|
+
"reproduction_steps": ["1. Run aioson qa:run --persona=hacker", "2. ..."],
|
|
367
|
+
"evidence": ["Response header dump", "Screenshot path"],
|
|
368
|
+
"impact": "...",
|
|
369
|
+
"affected_artifacts": ["URL or source file path"],
|
|
370
|
+
"suggested_fix": "...",
|
|
371
|
+
"recommended_owner": "dev",
|
|
372
|
+
"recommended_gate_status": "...",
|
|
373
|
+
"status": "open",
|
|
374
|
+
"safe_to_reproduce": true,
|
|
375
|
+
"asvs_ids": ["V..."]
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Summary — probe priority order
|
|
380
|
+
|
|
381
|
+
| Phase | What | Time estimate | Severity ceiling |
|
|
382
|
+
|---|---|---|---|
|
|
383
|
+
| 0 | `qa:run --persona=hacker` + `qa:scan` baseline | 2-5 min | critical |
|
|
384
|
+
| 1 | Security headers | 30s | high |
|
|
385
|
+
| 2 | Cookie attributes | 30s | high |
|
|
386
|
+
| 3 | Client-side storage | 30s | critical |
|
|
387
|
+
| 4 | CORS misconfiguration | 1 min | high |
|
|
388
|
+
| 5 | Source map exposure | 1 min | high |
|
|
389
|
+
| 6 | Clickjacking | 30s | medium |
|
|
390
|
+
| 7 | SRI (CDN resources) | 30s | medium |
|
|
391
|
+
| 8 | Error page disclosure | 30s | high |
|
|
392
|
+
| 9 | HTML meta & comment leaks | 30s | medium |
|
|
393
|
+
|
|
394
|
+
Total additional time beyond Phase 0: ~5-6 minutes of automated probing.
|
|
395
|
+
|
|
396
|
+
## References
|
|
397
|
+
|
|
398
|
+
- OWASP ASVS 5.0: Chapters V3, V4, V7, V8, V12, V13, V14, V15, V16
|
|
399
|
+
- OWASP Testing Guide v4.2: OTG-CONFIG, OTG-INFO, OTG-SESS
|
|
400
|
+
- Mozilla Observatory scoring methodology
|
|
401
|
+
- Playwright API: https://playwright.dev/docs/api/class-response
|