Multi-User Testing¶
Multi-user testing enables cross-role access control validation, IDOR detection, and privilege escalation testing. Credentials are defined in credentials.json at the project root, and every phase of the pentest uses them to test with all configured roles.
credentials.json Format¶
Never Commit
credentials.json is in .gitignore. Never commit real credentials to version control. Use credentials.json.example as a template.
Mode 1: Pre-Authenticated Sessions¶
Provide existing session cookies or Bearer tokens. Simplest setup -- paste values from your browser's DevTools.
{
"users": [
{
"role": "admin",
"username": "admin@example.com",
"auth": "Cookie: session=abc123def456"
},
{
"role": "user",
"username": "user@example.com",
"auth": "Cookie: session=xyz789ghi012"
},
{
"role": "viewer",
"username": "viewer@example.com",
"auth": "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
}
],
"unauthenticated": true
}
Mode 2: Auto-Login¶
Provide login endpoint details and raw credentials. Sessions are acquired automatically during /context init.
{
"login": {
"endpoint": "/api/auth/login",
"method": "POST",
"content_type": "application/json",
"body_template": "{\"email\": \"{{username}}\", \"password\": \"{{password}}\"}",
"token_extract": "json:access_token",
"auth_format": "Authorization: Bearer {{token}}"
},
"users": [
{
"role": "admin",
"username": "admin@example.com",
"password": "AdminPass123!"
},
{
"role": "user",
"username": "user@example.com",
"password": "UserPass123!"
}
],
"unauthenticated": true
}
Token Extraction Options¶
The token_extract field supports multiple response formats:
| Value | Behavior |
|---|---|
json:access_token |
Extract access_token from JSON response body |
json:data.token |
Extract nested field from JSON response body |
cookie:session_id |
Extract session_id from Set-Cookie header |
header:X-Auth-Token |
Extract X-Auth-Token from response headers |
Login Section Requirement¶
Critical
The credentials.json must include a login section (e.g., "login": {"url": "http://target/login", "method": "form"}) so the crawler can authenticate each role. Without the login field, the crawler runs unauthenticated and misses all protected pages. Always verify login succeeded by checking login_success in results before proceeding.
Multi-Role Testing Workflow¶
Phase 0: Context Init (/context init)¶
- Reads
credentials.json - Logs in all users via the configured login endpoint
- Stores validated tokens in
discovery/api-tokens.json - Populates
context.jsonwith role information
Phase 0.5: Walkthrough (/walkthrough)¶
The Playwright-based crawler navigates every user role defined in credentials.json:
- Parallel crawling -- with 2+ roles, launches parallel Docker processes per role using
crawler.py --role <name> - Merge results -- combines all role-specific crawl data with
crawler.py --merge - Output --
app-map.json,crawled-urls.txt,api-endpoints.txtcontaining all discovered URLs across all roles
Max 3 Users Per Batch
When crawling 4+ users, split into batches of maximum 3 users. Crawling too many users in a single process causes timeouts and internal errors.
| Users | Batching |
|---|---|
| 1-3 | Single batch |
| 4-6 | Batch 1: users 1-3, Batch 2: users 4-6 |
| 7-9 | Batch 1: users 1-3, Batch 2: users 4-6, Batch 3: users 7-9 |
Use --role <name> per user or create temporary credentials.json files with max 3 users per batch.
Walkthrough Completeness Gate¶
All Users Must Be Crawled
The walkthrough is not complete until the app has been crawled with all users in credentials.json. Every user/role must have a successful crawl result in app-map.json. If some users were skipped or failed, go back and crawl them before proceeding to Phase 2.
Verify: roles_tested count in results must match the total user count in credentials.json.
Access Control Testing (/test-access)¶
Section F: Access Matrix Generation¶
/test-access Section F produces a comprehensive access matrix mapping every discovered endpoint against every role. The matrix is stored in evidence/access-matrix.md.
| Endpoint | Admin | Manager | Employee | Unauth |
|---|---|---|---|---|
GET /api/users |
200 | 200 | 403 | 401 |
POST /api/users |
200 | 403 | 403 | 401 |
DELETE /api/users/1 |
200 | 403 | 403 | 401 |
GET /api/salary |
200 | 200 | 403 | 401 |
Per-Endpoint Mandatory Checks¶
Every endpoint is tested with all of the following patterns:
- Without auth -- expect 401/403
- With wrong role (lowest privilege user)
- With neighbor ID (+/-1 from valid ID) for IDOR detection
- With extra fields in request body (
role_id,is_admin,salary_gross) - With negative numeric values
- With hidden flags (
?debug=1,?admin=1,?test=1,?verbose=1,?internal=1,?dev_mode=1,?env=dev,?dbg=1,?trace=1,?console=1,?profiler=1,?show_errors=1,?sandbox=1,?staging=1,?beta=1) - With
Content-Type: application/xml(XXE on JSON endpoints) - With
Content-Type: multipart/form-data(validation bypass)
Anomaly Detection¶
The multi-user testing framework automatically flags the following anomalies:
Vertical Privilege Escalation¶
A lower-privilege role (e.g., employee) successfully accesses an endpoint that should be restricted to a higher-privilege role (e.g., admin). Detected by comparing HTTP status codes across roles in the access matrix.
Horizontal Privilege Escalation (IDOR)¶
A user successfully accesses or modifies resources belonging to another user of the same privilege level. Detected by testing neighbor IDs (+/-1) and cross-user resource references.
Missing Authentication¶
An endpoint returns a 200 response without any authentication headers. The unauth sweep tests all discovered endpoints (not just a hardcoded list) across crawled-urls.txt, api-endpoints.txt, and resource-map.json, including POST/PUT/DELETE methods.
Dual Authentication Testing¶
If the application uses both Bearer token and session-cookie authentication, both are obtained for all roles and tested independently. All discovered endpoints are tested with session cookies as well as Bearer tokens.
Token Management¶
Loading and Validation¶
Every test skill loads tokens at startup via the shared boilerplate:
load_tokens() # Read discovery/api-tokens.json
validate_token "$ADMIN_TOKEN" "admin" # Verify token works on a real endpoint
Token Refresh¶
If validation fails (expired token, wrong type), the boilerplate automatically attempts re-authentication:
Refreshed tokens are saved back to api-tokens.json for subsequent skills.
Auth Endpoint Discovery Gate¶
All Auth Systems Must Be Discovered
Applications may have multiple authentication systems (JWT + Sanctum + session). All auth endpoints must be probed: /api/v1/auth/login, /api/v1/auth/token, /api/auth/login, /oauth/token, etc. Admin email may differ from config -- enumerate, do not assume.
Best Practices¶
- Include all roles -- admin, manager, employee, viewer, and any application-specific roles
- Set
unauthenticated: true-- always test unauthenticated access as a baseline - Use auto-login when possible -- tokens can be refreshed mid-engagement if they expire
- Verify login success -- check
login_successin crawl results before proceeding - Test endpoint variants -- both web form (
/admin/export/*) and REST API (/api/v1/*/export) variants of the same operation, as access control may differ between them