@testsmith/api-spector 0.6.0 → 0.6.2

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 (42) hide show
  1. package/docs/ai/api-spector-scripting-reference.md +149 -0
  2. package/docs/ai/collection-file-format.md +110 -0
  3. package/docs/ai/functional-testing-guide.md +145 -0
  4. package/docs/ai/security-testing-guide.md +253 -0
  5. package/out/main/agents.js +2 -1
  6. package/out/main/chunks/{snapshots-Y33M1SEy.js → broker-client-WEymdqAK.js} +119 -10
  7. package/out/main/chunks/{cli-common-BKYgsbGB.js → cli-common-vASoDfQH.js} +3 -2
  8. package/out/main/chunks/data-files-Br2h3Dt2.js +85 -0
  9. package/out/main/chunks/escape-BwPM4Tf6.js +9 -0
  10. package/out/main/chunks/{handle-BFaeFA0A.js → handle-CL-nWtYA.js} +11 -2
  11. package/out/main/chunks/{import-D9LH0isB.js → import-DdXhO56X.js} +2 -2
  12. package/out/main/chunks/{request-collection-BQIo1765.js → request-collection-Hqh3I3rs.js} +75 -15
  13. package/out/main/chunks/{request-exec-5_3279w1.js → request-exec-DAFA6Fap.js} +19 -1
  14. package/out/main/chunks/{soap-handler-CWn3o6Py.js → soap-handler-BAPCALkN.js} +2 -2
  15. package/out/main/compare.js +2 -1
  16. package/out/main/contract.js +50 -147
  17. package/out/main/coverage.js +2 -1
  18. package/out/main/generate-tests.js +2 -1
  19. package/out/main/index.js +131 -36
  20. package/out/main/lib.js +2 -2
  21. package/out/main/mock.js +2 -1
  22. package/out/main/record.js +2 -1
  23. package/out/main/runner.js +95 -13
  24. package/out/main/wsdl.js +5 -4
  25. package/out/preload/index.js +21 -3
  26. package/out/renderer/assets/{GraphQLEditor-DITvPhtG.js → GraphQLEditor-DtppmZJO.js} +3 -1
  27. package/out/renderer/assets/{SoapEditor-Bf-oE-rS.js → SoapEditor-BwSm12wH.js} +4 -2
  28. package/out/renderer/assets/ajv-2MAg6kGF.js +6153 -0
  29. package/out/renderer/assets/codemirror-F6m3Hm1M.js +26552 -0
  30. package/out/renderer/assets/index-B3mtLdU3.css +2 -0
  31. package/out/renderer/assets/{index-MMIHKSKx.js → index-D6-zmXen.js} +17493 -49301
  32. package/out/renderer/index.html +4 -2
  33. package/package.json +4 -2
  34. package/src/cli/agent-templates/aider/conventions.md +10 -0
  35. package/src/cli/agent-templates/claude/skills/api-spector-api-audit.md +135 -0
  36. package/src/cli/agent-templates/claude/skills/api-spector-functional-tests.md +73 -0
  37. package/src/cli/agent-templates/claude/skills/api-spector-generate-mocks.md +182 -0
  38. package/src/cli/agent-templates/claude/skills/api-spector-security-tests.md +73 -0
  39. package/src/cli/agent-templates/copilot/copilot-instructions.md +38 -0
  40. package/src/cli/agent-templates/cursor/rules/api-spector.mdc +21 -0
  41. package/src/cli/agent-templates/windsurf/windsurfrules +19 -0
  42. package/out/renderer/assets/index-CkHujZU3.css +0 -2
@@ -0,0 +1,149 @@
1
+ # API Spector: Scripting Reference
2
+
3
+ This document describes the scripting API available in API Spector's pre-request and post-request scripts. Use this as a reference when generating test scripts.
4
+
5
+ ## Script Context
6
+
7
+ Scripts run in a sandboxed environment. The global `sp` object provides all testing, assertion, and variable functionality.
8
+
9
+ ## Assertions
10
+
11
+ ### sp.test(name, fn)
12
+
13
+ Wrap assertions in a named test block:
14
+
15
+ ```javascript
16
+ sp.test('status is 200', function() {
17
+ sp.expect(sp.response.code).to.equal(200);
18
+ });
19
+ ```
20
+
21
+ ### sp.expect(value)
22
+
23
+ Returns a chainable asserter. Available matchers:
24
+
25
+ | Matcher | Example | Description |
26
+ |---|---|---|
27
+ | `.to.equal(v)` | `sp.expect(json.name).to.equal("Alice")` | Strict equality |
28
+ | `.to.eql(v)` | `sp.expect(json).to.eql({id: 1})` | Deep equality |
29
+ | `.to.include(v)` | `sp.expect(json.name).to.include("Ali")` | String/array contains |
30
+ | `.to.be.a(type)` | `sp.expect(json.age).to.be.a("number")` | Type check |
31
+ | `.to.be.above(n)` | `sp.expect(json.count).to.be.above(0)` | Greater than |
32
+ | `.to.be.below(n)` | `sp.expect(json.count).to.be.below(100)` | Less than |
33
+ | `.to.be.least(n)` | `sp.expect(json.count).to.be.least(1)` | >= |
34
+ | `.to.be.most(n)` | `sp.expect(json.count).to.be.most(50)` | <= |
35
+ | `.to.have.property(k)` | `sp.expect(json).to.have.property("id")` | Key exists |
36
+ | `.to.have.lengthOf(n)` | `sp.expect(json.items).to.have.lengthOf(3)` | Array/string length |
37
+ | `.to.match(re)` | `sp.expect(json.email).to.match(/^.+@.+$/)` | Regex match |
38
+ | `.to.be.oneOf([...])` | `sp.expect(json.role).to.be.oneOf(["admin","user"])` | Value in list |
39
+ | `.to.not.xxx` | `sp.expect(json.id).to.not.be.oneOf([null, undefined])` | Negation |
40
+
41
+ Chainable words (no-op, for readability): `.to`, `.be`, `.been`, `.have`, `.that`, `.and`, `.is`, `.deep`
42
+
43
+ Flag assertions (property-style): `.ok`, `.true`, `.false`, `.null`, `.undefined`
44
+
45
+ ## Response Object
46
+
47
+ Available in post-request scripts when a response has been received:
48
+
49
+ ```javascript
50
+ sp.response.code // HTTP status code (number)
51
+ sp.response.status // "200 OK" (string)
52
+ sp.response.statusText // "OK" (string)
53
+ sp.response.responseTime // Duration in ms
54
+ sp.response.responseSize // Body size in bytes
55
+ sp.response.json() // Parse body as JSON (cached)
56
+ sp.response.text() // Raw body as string
57
+ sp.response.xmlText(sel) // Extract text from XML/HTML using CSS selector
58
+ sp.response.headers.get(n) // Get response header by name
59
+ sp.response.headers.toObject() // All headers as {key: value}
60
+ sp.response.to.have.status(code) // Assert status code
61
+ ```
62
+
63
+ ## Variables
64
+
65
+ Four variable scopes, from most-local to most-global:
66
+
67
+ ```javascript
68
+ // Local variables (per-request, not persisted)
69
+ sp.variables.get("key")
70
+ sp.variables.set("key", "value")
71
+ sp.variables.has("key")
72
+ sp.variables.clear("key")
73
+
74
+ // Environment variables (persisted to the active environment)
75
+ sp.environment.get("key")
76
+ sp.environment.set("key", "value")
77
+
78
+ // Collection variables (persisted to the collection)
79
+ sp.collectionVariables.get("key")
80
+ sp.collectionVariables.set("key", "value")
81
+
82
+ // Global variables (persisted across collections)
83
+ sp.globals.get("key")
84
+ sp.globals.set("key", "value")
85
+
86
+ // Shorthand: read from all scopes (local wins)
87
+ sp.variables_get("key")
88
+ ```
89
+
90
+ ### Variable interpolation in scripts
91
+
92
+ Scripts support `{{variable}}` interpolation. Before execution, `{{var}}` tokens are replaced with the variable's value:
93
+
94
+ ```javascript
95
+ sp.variables.set("otp", sp.totp("{{secret}}"));
96
+ ```
97
+
98
+ ## JSONPath
99
+
100
+ Query JSON data with JSONPath expressions:
101
+
102
+ ```javascript
103
+ const matches = sp.jsonPath(sp.response.json(), '$.users[?(@.role=="admin")].name');
104
+ sp.expect(matches.length).to.be.above(0);
105
+ sp.expect(matches[0]).to.equal("Alice");
106
+ ```
107
+
108
+ ## TOTP (Time-based One-Time Password)
109
+
110
+ Generate TOTP codes from a base32 secret:
111
+
112
+ ```javascript
113
+ const code = sp.totp("JBSWY3DPEHPK3PXP");
114
+ sp.environment.set("otp", code);
115
+
116
+ // With options:
117
+ const code8 = sp.totp("JBSWY3DPEHPK3PXP", { digits: 8, period: 30, algorithm: "sha1" });
118
+ ```
119
+
120
+ ## Available globals
121
+
122
+ Scripts have access to: `JSON`, `Math`, `Date`, `parseInt`, `parseFloat`, `isNaN`, `isFinite`, `encodeURIComponent`, `decodeURIComponent`, `btoa`, `atob`, `console` (log/warn/error/info), `dayjs` (date library), `faker` (test data generation), `tv4` (JSON Schema validation), `DOMParser` (XML parsing).
123
+
124
+ ## Hooks
125
+
126
+ Requests can be designated as lifecycle hooks within their folder:
127
+
128
+ | Hook type | When it runs |
129
+ |---|---|
130
+ | `beforeAll` | Once before any request in the folder |
131
+ | `before` | Before each request in the folder |
132
+ | `after` | After each request in the folder |
133
+ | `afterAll` | Once after all requests in the folder |
134
+
135
+ Hooks from parent folders wrap child folders (outer-to-inner for before, inner-to-outer for after).
136
+
137
+ A common pattern is a login hook:
138
+
139
+ ```javascript
140
+ // Post-request script on a beforeAll hook that calls POST /login:
141
+ const json = sp.response.json();
142
+ sp.environment.set("access_token", json.access_token);
143
+ ```
144
+
145
+ Then other requests use `{{access_token}}` in their Bearer token auth field.
146
+
147
+ ## Schema validation
148
+
149
+ The `request.schema` field (JSON Schema) is automatically validated against every response. Failed validations appear as `[schema]` test results. The schema uses standard JSON Schema draft-07+ syntax.
@@ -0,0 +1,110 @@
1
+ # API Spector: Collection File Format
2
+
3
+ Collections are stored as `.spector` JSON files. This document describes the format so AI tools can read and modify collections programmatically.
4
+
5
+ ## File structure
6
+
7
+ ```json
8
+ {
9
+ "version": "1.0",
10
+ "id": "uuid",
11
+ "name": "My API",
12
+ "description": "Optional description",
13
+ "rootFolder": { ... },
14
+ "requests": { "request-id": { ... }, ... },
15
+ "collectionVariables": { "key": "value" },
16
+ "auth": { "type": "bearer", "token": "{{access_token}}" },
17
+ "headers": [{ "key": "Accept", "value": "application/json", "enabled": true }]
18
+ }
19
+ ```
20
+
21
+ ## Request object
22
+
23
+ ```json
24
+ {
25
+ "id": "unique-id",
26
+ "name": "Get Users",
27
+ "method": "GET",
28
+ "url": "{{baseUrl}}/users",
29
+ "headers": [
30
+ { "key": "Accept", "value": "application/json", "enabled": true }
31
+ ],
32
+ "params": [
33
+ { "key": "page", "value": "1", "enabled": true, "paramType": "query" },
34
+ { "key": "id", "value": "42", "enabled": true, "paramType": "path" }
35
+ ],
36
+ "auth": { "type": "bearer", "token": "{{access_token}}" },
37
+ "body": {
38
+ "mode": "json",
39
+ "json": "{ \"name\": \"Alice\" }"
40
+ },
41
+ "preRequestScript": "sp.environment.set('otp', sp.totp('{{secret}}'));",
42
+ "postRequestScript": "sp.test('status', function() { ... });",
43
+ "schema": "{ \"type\": \"object\", ... }",
44
+ "hookType": "beforeAll",
45
+ "disabled": false
46
+ }
47
+ ```
48
+
49
+ ### Auth types
50
+
51
+ ```json
52
+ { "type": "none" }
53
+ { "type": "bearer", "token": "{{access_token}}" }
54
+ { "type": "basic", "username": "admin", "password": "secret" }
55
+ { "type": "apikey", "apiKeyName": "X-API-Key", "apiKeyValue": "{{key}}", "apiKeyIn": "header" }
56
+ ```
57
+
58
+ ### Body modes
59
+
60
+ - `"none"`: no body
61
+ - `"json"`: `body.json` contains the JSON string
62
+ - `"form"`: `body.form` is an array of `{ key, value, enabled }` pairs
63
+ - `"raw"`: `body.raw` contains raw text, `body.rawContentType` is the MIME type
64
+ - `"graphql"`: `body.graphql` has `{ query, variables, operationName }`
65
+
66
+ ## Folder structure
67
+
68
+ ```json
69
+ {
70
+ "id": "folder-id",
71
+ "name": "Users",
72
+ "description": "",
73
+ "folders": [ ... ],
74
+ "requestIds": ["req-1", "req-2"],
75
+ "auth": { "type": "bearer", "token": "{{admin_token}}" },
76
+ "headers": [{ "key": "X-Tenant", "value": "{{tenant}}", "enabled": true }],
77
+ "tags": ["smoke"]
78
+ }
79
+ ```
80
+
81
+ Folders can nest. Each folder can define auth and headers that are inherited by all requests within it (overridable at the request level). The root folder is synthetic (name "root"); its subfolders are the visible top-level groups.
82
+
83
+ ## Environment files
84
+
85
+ Stored as `.env.json`:
86
+
87
+ ```json
88
+ {
89
+ "version": "1.0",
90
+ "id": "uuid",
91
+ "name": "Staging",
92
+ "variables": [
93
+ { "key": "baseUrl", "value": "https://staging.api.com", "enabled": true },
94
+ { "key": "admin_password", "value": "", "enabled": true, "secret": true }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ ## Workspace file
100
+
101
+ `workspace.json` ties collections and environments together:
102
+
103
+ ```json
104
+ {
105
+ "version": "1.0",
106
+ "collections": ["collections/users.spector", "collections/orders.spector"],
107
+ "environments": ["environments/staging.env.json"],
108
+ "activeEnvironmentId": "env-uuid"
109
+ }
110
+ ```
@@ -0,0 +1,145 @@
1
+ # API Spector: Functional API Testing Guide
2
+
3
+ Use this guide to generate functional test cases for REST APIs. Each endpoint should be tested across multiple categories.
4
+
5
+ ## Test categories per endpoint
6
+
7
+ ### 1. Happy path (positive)
8
+ - Send valid data, assert correct status code and response structure
9
+ - Verify all required response fields are present and correctly typed
10
+
11
+ ```javascript
12
+ sp.test('returns 200 with user data', function() {
13
+ const json = sp.response.json();
14
+ sp.expect(sp.response.code).to.equal(200);
15
+ sp.expect(json.id).to.not.be.oneOf([null, undefined]);
16
+ sp.expect(json.name).to.be.a("string");
17
+ sp.expect(json.email).to.match(/^.+@.+\..+$/);
18
+ });
19
+ ```
20
+
21
+ ### 2. Missing required fields (negative)
22
+ - Omit each required field one at a time
23
+ - Expect 422 or 400 with a descriptive error
24
+
25
+ ```javascript
26
+ sp.test('returns 422 when email is missing', function() {
27
+ sp.expect(sp.response.code).to.equal(422);
28
+ const json = sp.response.json();
29
+ sp.expect(JSON.stringify(json)).to.include("email");
30
+ });
31
+ ```
32
+
33
+ ### 3. Wrong types (negative)
34
+ - Send a string where a number is expected, an object where a string is expected, etc.
35
+ - Expect 422 or 400
36
+
37
+ ### 4. Boundary values
38
+ - Empty strings for string fields
39
+ - 0, -1, MAX_INT for numeric fields
40
+ - Very long strings (1000+ chars)
41
+ - Special characters: `<script>alert(1)</script>`, `'; DROP TABLE--`, unicode
42
+
43
+ ### 5. Authentication
44
+ - Missing auth header → 401
45
+ - Invalid/expired token → 401
46
+ - Valid token, wrong role → 403
47
+
48
+ ### 6. Resource not found
49
+ - Request a non-existent ID → 404
50
+ - Request with a malformed ID format → 400 or 404
51
+
52
+ ### 7. List/pagination endpoints
53
+ - Default pagination returns reasonable count
54
+ - `page` and `limit` params work correctly
55
+ - Out-of-range page returns empty list (not error)
56
+ - Sorting works if supported
57
+
58
+ ### 8. Idempotency
59
+ - PUT with same data twice → same result
60
+ - DELETE then DELETE again → 404 on second call
61
+
62
+ ## Test naming convention
63
+
64
+ Use descriptive names that include the endpoint and expected behavior:
65
+
66
+ ```javascript
67
+ sp.test('POST /users - valid data returns 201', function() { ... });
68
+ sp.test('POST /users - missing email returns 422', function() { ... });
69
+ sp.test('GET /users/:id - non-existent ID returns 404', function() { ... });
70
+ sp.test('GET /users - unauthorized returns 401', function() { ... });
71
+ ```
72
+
73
+ ## Variable extraction pattern
74
+
75
+ When a test creates a resource, extract its ID for subsequent tests:
76
+
77
+ ```javascript
78
+ // Post-request script on POST /users
79
+ const json = sp.response.json();
80
+ sp.environment.set("userId", String(json.id));
81
+
82
+ // Then GET /users/{{userId}} in a subsequent request uses the created ID
83
+ ```
84
+
85
+ ## Hook pattern for auth
86
+
87
+ Create a `beforeAll` hook that logs in and stores the token:
88
+
89
+ ```javascript
90
+ // Post-request script on the login hook (POST /auth/login)
91
+ const json = sp.response.json();
92
+ sp.environment.set("access_token", json.access_token);
93
+ ```
94
+
95
+ Then configure the folder/collection auth as Bearer with token `{{access_token}}`.
96
+
97
+ ## Test plan structure
98
+
99
+ When generating a test plan, organize by endpoint and include:
100
+
101
+ 1. **Endpoint**: method + path
102
+ 2. **Test cases**: list of scenarios with:
103
+ - Name (descriptive)
104
+ - Category (positive/negative/boundary/auth/edge)
105
+ - Request body (if applicable)
106
+ - Expected status code
107
+ - Assertions to make
108
+ 3. **Dependencies**: which tests depend on data from earlier tests
109
+ 4. **Hooks needed**: login, setup data, cleanup
110
+
111
+ ## Example test plan output
112
+
113
+ ```
114
+ ## POST /users
115
+
116
+ ### Positive
117
+ - [x] Valid user creation → 201, response has id, name, email
118
+ - [x] Create user with all optional fields → 201
119
+
120
+ ### Negative
121
+ - [x] Missing name → 422, error mentions "name"
122
+ - [x] Missing email → 422, error mentions "email"
123
+ - [x] Invalid email format → 422
124
+ - [x] Duplicate email → 409
125
+
126
+ ### Boundary
127
+ - [x] Name with 255 characters → 201 (or 422 if max exceeded)
128
+ - [x] Empty name → 422
129
+ - [x] Price = 0 → 201 or 422 depending on business rule
130
+ - [x] Price = -1 → 422
131
+
132
+ ### Auth
133
+ - [x] No token → 401
134
+ - [x] Expired token → 401
135
+
136
+ ## GET /users
137
+
138
+ ### Positive
139
+ - [x] List users → 200, returns array
140
+ - [x] Filter by name → 200, filtered results
141
+
142
+ ### Pagination
143
+ - [x] ?page=1&limit=10 → 200, max 10 results
144
+ - [x] ?page=9999 → 200, empty array
145
+ ```
@@ -0,0 +1,253 @@
1
+ # API Spector: Security API Testing Guide (OWASP API Top 10)
2
+
3
+ Use this guide to generate security test cases based on the OWASP API Security Top 10 (2023). Each test case should use the API Spector `sp.*` scripting API.
4
+
5
+ ## Prerequisites
6
+
7
+ Security tests typically require multiple user identities:
8
+ - **User A** (attacker): a regular user trying to access resources they shouldn't
9
+ - **User B** (victim): the owner of the resources being targeted
10
+ - **Admin**: for testing admin-only endpoints with regular credentials
11
+
12
+ Store tokens in environment variables: `{{user_a_token}}`, `{{user_b_token}}`, `{{admin_token}}`
13
+
14
+ ## API1:2023 - Broken Object-Level Authorization (BOLA)
15
+
16
+ **What to test**: Can User A access User B's resources by changing the ID?
17
+
18
+ ```javascript
19
+ // Request: GET /users/{{user_b_id}} with User A's token
20
+ sp.test('[BOLA] cannot access other user profile', function() {
21
+ sp.expect(sp.response.code).to.be.oneOf([403, 404]);
22
+ });
23
+
24
+ // Request: PUT /users/{{user_b_id}} with User A's token
25
+ sp.test('[BOLA] cannot modify other user profile', function() {
26
+ sp.expect(sp.response.code).to.be.oneOf([403, 404]);
27
+ });
28
+
29
+ // Request: DELETE /orders/{{user_b_order_id}} with User A's token
30
+ sp.test('[BOLA] cannot delete other user order', function() {
31
+ sp.expect(sp.response.code).to.be.oneOf([403, 404]);
32
+ });
33
+ ```
34
+
35
+ **For each endpoint with an ID parameter**: create a request where User A tries to access User B's resource.
36
+
37
+ ## API2:2023 - Broken Authentication
38
+
39
+ **What to test**: Authentication bypass, weak tokens, missing validation.
40
+
41
+ ```javascript
42
+ // Request with no Authorization header
43
+ sp.test('[AUTH] missing token returns 401', function() {
44
+ sp.expect(sp.response.code).to.equal(401);
45
+ });
46
+
47
+ // Request with an invalid/garbage token
48
+ sp.test('[AUTH] invalid token returns 401', function() {
49
+ sp.expect(sp.response.code).to.equal(401);
50
+ });
51
+
52
+ // Request with expired token
53
+ sp.test('[AUTH] expired token returns 401', function() {
54
+ sp.expect(sp.response.code).to.equal(401);
55
+ });
56
+ ```
57
+
58
+ ## API3:2023 - Broken Object Property-Level Authorization
59
+
60
+ **What to test**: Mass assignment. Can a user set fields they shouldn't?
61
+
62
+ ```javascript
63
+ // Request: PUT /users/{{user_a_id}} with body including role: "admin"
64
+ sp.test('[MASS-ASSIGN] cannot escalate own role', function() {
65
+ sp.expect(sp.response.code).to.be.oneOf([200, 422]);
66
+ // If 200, verify the role wasn't actually changed
67
+ const json = sp.response.json();
68
+ if (sp.response.code === 200) {
69
+ sp.expect(json.role).to.not.equal("admin");
70
+ }
71
+ });
72
+
73
+ // Request: POST /users with is_admin: true
74
+ sp.test('[MASS-ASSIGN] cannot create admin user', function() {
75
+ const json = sp.response.json();
76
+ if (json.is_admin !== undefined) {
77
+ sp.expect(json.is_admin).to.equal(false);
78
+ }
79
+ });
80
+ ```
81
+
82
+ **For each write endpoint**: include fields that shouldn't be user-settable (role, is_admin, balance, permissions).
83
+
84
+ ## API4:2023 - Unrestricted Resource Consumption
85
+
86
+ **What to test**: Rate limits, pagination abuse, large payloads.
87
+
88
+ ```javascript
89
+ // Request: GET /users?limit=999999
90
+ sp.test('[RESOURCE] excessive limit is capped or rejected', function() {
91
+ sp.expect(sp.response.code).to.be.oneOf([200, 400, 422]);
92
+ if (sp.response.code === 200) {
93
+ const json = sp.response.json();
94
+ const items = Array.isArray(json) ? json : json.data ?? [];
95
+ sp.expect(items.length).to.be.most(100);
96
+ }
97
+ });
98
+
99
+ // Request: POST /upload with an extremely large body
100
+ sp.test('[RESOURCE] rejects oversized payload', function() {
101
+ sp.expect(sp.response.code).to.be.oneOf([413, 400, 422]);
102
+ });
103
+ ```
104
+
105
+ ## API5:2023 - Broken Function-Level Authorization
106
+
107
+ **What to test**: Admin endpoints accessible by regular users.
108
+
109
+ ```javascript
110
+ // Request: GET /admin/users with regular user token
111
+ sp.test('[FUNC-AUTH] admin endpoint rejects regular user', function() {
112
+ sp.expect(sp.response.code).to.be.oneOf([403, 404]);
113
+ });
114
+
115
+ // Request: DELETE /users/{{user_b_id}} with regular user token
116
+ sp.test('[FUNC-AUTH] user cannot delete other users', function() {
117
+ sp.expect(sp.response.code).to.be.oneOf([403, 404]);
118
+ });
119
+ ```
120
+
121
+ **For each endpoint**: determine the expected minimum role and test with a lower-privilege token.
122
+
123
+ ## API6:2023 - Unrestricted Access to Sensitive Business Flows
124
+
125
+ **What to test**: Business logic abuse.
126
+
127
+ ```javascript
128
+ // Request: POST /orders (repeat the same order)
129
+ sp.test('[BIZ-FLOW] duplicate order is rejected', function() {
130
+ // Second call with same data should not create a duplicate
131
+ sp.expect(sp.response.code).to.be.oneOf([409, 422, 429]);
132
+ });
133
+ ```
134
+
135
+ ## API7:2023 - Server-Side Request Forgery (SSRF)
136
+
137
+ **What to test**: URL parameters that the server fetches.
138
+
139
+ ```javascript
140
+ // Request with URL field pointing to internal metadata
141
+ // e.g. POST /import with { "url": "http://169.254.169.254/latest/meta-data/" }
142
+ sp.test('[SSRF] internal URL is blocked', function() {
143
+ sp.expect(sp.response.code).to.be.oneOf([400, 403, 422]);
144
+ });
145
+
146
+ // Same with http://localhost, http://127.0.0.1, http://[::1]
147
+ ```
148
+
149
+ **For each endpoint that accepts a URL parameter**: test with internal addresses.
150
+
151
+ ## API8:2023 - Security Misconfiguration
152
+
153
+ **What to test**: Headers, error verbosity, CORS.
154
+
155
+ ```javascript
156
+ // Any endpoint: check security headers
157
+ sp.test('[CONFIG] has security headers', function() {
158
+ sp.expect(sp.response.headers.get('x-content-type-options')).to.equal('nosniff');
159
+ // Optional: check for other headers
160
+ // sp.expect(sp.response.headers.get('x-frame-options')).to.not.equal(null);
161
+ // sp.expect(sp.response.headers.get('strict-transport-security')).to.not.equal(null);
162
+ });
163
+
164
+ // Invalid endpoint: check that error doesn't leak stack traces
165
+ sp.test('[CONFIG] error response does not expose stack trace', function() {
166
+ const body = sp.response.text();
167
+ sp.expect(body).to.not.include("at Object.");
168
+ sp.expect(body).to.not.include("node_modules");
169
+ sp.expect(body).to.not.include("Exception in thread");
170
+ });
171
+
172
+ // OPTIONS request: check CORS
173
+ sp.test('[CONFIG] CORS does not allow *', function() {
174
+ const allow = sp.response.headers.get('access-control-allow-origin');
175
+ if (allow) {
176
+ sp.expect(allow).to.not.equal('*');
177
+ }
178
+ });
179
+ ```
180
+
181
+ ## API9:2023 - Improper Inventory Management
182
+
183
+ **What to test**: Deprecated or undocumented endpoints still accessible.
184
+
185
+ This is best tested by comparing the spec against actual behavior:
186
+ - Hit known deprecated endpoints → should return 404 or 410
187
+ - Hit versioned paths (v1 when v2 is current) → should be blocked or redirected
188
+
189
+ ## API10:2023 - Unsafe Consumption of APIs
190
+
191
+ **What to test**: Injection through fields that the API passes downstream.
192
+
193
+ ```javascript
194
+ // SQL injection in search/filter parameters
195
+ // Request: GET /users?search=' OR '1'='1
196
+ sp.test('[INJECTION] SQL injection in search returns 400 or safe results', function() {
197
+ sp.expect(sp.response.code).to.be.oneOf([200, 400, 422]);
198
+ if (sp.response.code === 200) {
199
+ const json = sp.response.json();
200
+ const items = Array.isArray(json) ? json : json.data ?? [];
201
+ sp.expect(items.length).to.be.below(50); // should not dump entire table
202
+ }
203
+ });
204
+
205
+ // XSS payload in input fields
206
+ // Request: POST /users with name: "<script>alert(1)</script>"
207
+ sp.test('[INJECTION] XSS payload is sanitized or rejected', function() {
208
+ sp.expect(sp.response.code).to.be.oneOf([201, 200, 400, 422]);
209
+ if (sp.response.code <= 201) {
210
+ const json = sp.response.json();
211
+ sp.expect(json.name).to.not.include("<script>");
212
+ }
213
+ });
214
+
215
+ // NoSQL injection
216
+ // Request: POST /login with { "email": {"$gt": ""}, "password": {"$gt": ""} }
217
+ sp.test('[INJECTION] NoSQL injection is rejected', function() {
218
+ sp.expect(sp.response.code).to.be.oneOf([400, 401, 422]);
219
+ });
220
+ ```
221
+
222
+ ## Test plan structure
223
+
224
+ When generating security tests, organize by OWASP category:
225
+
226
+ ```
227
+ Security/
228
+ ├── BOLA/
229
+ │ ├── [BOLA] GET /users/:id - access other user
230
+ │ ├── [BOLA] PUT /orders/:id - modify other user order
231
+ │ └── ...
232
+ ├── Auth/
233
+ │ ├── [AUTH] No token
234
+ │ ├── [AUTH] Invalid token
235
+ │ └── [AUTH] Expired token
236
+ ├── Mass Assignment/
237
+ │ ├── [MASS-ASSIGN] Escalate role via PUT /users
238
+ │ └── ...
239
+ ├── Injection/
240
+ │ ├── [INJECTION] SQL in search param
241
+ │ ├── [INJECTION] XSS in name field
242
+ │ └── ...
243
+ └── Config/
244
+ ├── [CONFIG] Security headers
245
+ ├── [CONFIG] Error verbosity
246
+ └── [CONFIG] CORS policy
247
+ ```
248
+
249
+ Each request should:
250
+ 1. Have a descriptive name prefixed with the OWASP category
251
+ 2. Be pre-filled with the attack payload in the body/params/headers
252
+ 3. Have a post-request script that asserts the expected safe behavior
253
+ 4. Use the attacker's token (User A) unless testing missing-auth scenarios
@@ -2,7 +2,8 @@
2
2
  "use strict";
3
3
  const promises = require("fs/promises");
4
4
  const path = require("path");
5
- const cliCommon = require("./chunks/cli-common-BKYgsbGB.js");
5
+ const cliCommon = require("./chunks/cli-common-vASoDfQH.js");
6
+ require("./chunks/data-files-Br2h3Dt2.js");
6
7
  require("crypto");
7
8
  const AGENTS = {
8
9
  claude: {