@kirrosh/zond 0.23.0 → 0.26.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 +176 -1
- package/README.md +8 -7
- package/package.json +2 -3
- package/src/CLAUDE.md +112 -0
- package/src/cli/commands/add-api.ts +19 -7
- package/src/cli/commands/api/annotate/index.ts +359 -4
- package/src/cli/commands/api/annotate/lifecycle.ts +1 -1
- package/src/cli/commands/api/annotate/overlay.ts +1 -1
- package/src/cli/commands/api/annotate/pagination.ts +10 -6
- package/src/cli/commands/api/annotate/prompts.ts +39 -2
- package/src/cli/commands/audit.ts +360 -54
- package/src/cli/commands/check.ts +15 -2
- package/src/cli/commands/checks.ts +352 -36
- package/src/cli/commands/cleanup.ts +4 -30
- package/src/cli/commands/coverage.ts +275 -57
- package/src/cli/commands/db.ts +311 -8
- package/src/cli/commands/discover.ts +281 -161
- package/src/cli/commands/doctor.ts +57 -3
- package/src/cli/commands/fixtures.ts +1 -1
- package/src/cli/commands/generate.ts +24 -7
- package/src/cli/commands/init/bootstrap.ts +4 -1
- package/src/cli/commands/init/index.ts +2 -2
- package/src/cli/commands/init/skills.ts +43 -0
- package/src/cli/commands/init/templates/agents.md +12 -3
- package/src/cli/commands/init/templates/skills/warm-up-target.md +122 -0
- package/src/cli/commands/init/templates/skills/zond-checks.md +268 -44
- package/src/cli/commands/init/templates/skills/zond-seed.md +114 -0
- package/src/cli/commands/init/templates/skills/zond-triage.md +88 -26
- package/src/cli/commands/init/templates/skills/zond.md +274 -64
- package/src/cli/commands/init/templates/zond-config.yml +1 -1
- package/src/cli/commands/prepare-fixtures.ts +14 -52
- package/src/cli/commands/probe/_seed-bodies.ts +52 -0
- package/src/cli/commands/probe/mass-assignment.ts +101 -10
- package/src/cli/commands/probe/security.ts +95 -12
- package/src/cli/commands/probe/webhooks.ts +2 -0
- package/src/cli/commands/probe.ts +87 -11
- package/src/cli/commands/refresh-api.ts +59 -1
- package/src/cli/commands/report-bundle.ts +3 -11
- package/src/cli/commands/request.ts +116 -0
- package/src/cli/commands/run.ts +53 -5
- package/src/cli/commands/schema-from-runs.ts +128 -0
- package/src/cli/commands/secrets.ts +133 -0
- package/src/cli/json-envelope.ts +0 -20
- package/src/cli/json-schemas.ts +51 -0
- package/src/cli/output.ts +17 -1
- package/src/cli/program.ts +5 -4
- package/src/cli/safe-live.ts +24 -0
- package/src/cli/status-filter.ts +0 -10
- package/src/core/audit/persist.ts +183 -0
- package/src/core/checks/budget.ts +59 -0
- package/src/core/checks/checks/cross_call_references.ts +17 -4
- package/src/core/checks/checks/cursor_boundary_fuzzing.ts +219 -0
- package/src/core/checks/checks/idempotency_replay.ts +1 -5
- package/src/core/checks/checks/ignored_auth.ts +44 -1
- package/src/core/checks/checks/index.ts +3 -0
- package/src/core/checks/checks/lifecycle_transitions.ts +169 -26
- package/src/core/checks/checks/negative_data_rejection.ts +119 -16
- package/src/core/checks/checks/not_a_server_error.ts +8 -0
- package/src/core/checks/checks/open_cors_on_sensitive.ts +47 -18
- package/src/core/checks/checks/pagination_invariants.ts +298 -117
- package/src/core/checks/checks/positive_data_acceptance.ts +1 -4
- package/src/core/checks/checks/status_code_conformance.ts +78 -7
- package/src/core/checks/mode.ts +3 -0
- package/src/core/checks/recommended-action.ts +5 -1
- package/src/core/checks/runner.ts +614 -27
- package/src/core/checks/spec-findings.ts +308 -0
- package/src/core/checks/types.ts +117 -1
- package/src/core/checks/zond-extensions.ts +73 -0
- package/src/core/classifier/recommended-action.ts +35 -6
- package/src/core/coverage/loader.ts +31 -0
- package/src/core/diagnostics/db-analysis.ts +200 -106
- package/src/core/diagnostics/failure-class.ts +21 -1
- package/src/core/diagnostics/failure-hints.ts +4 -208
- package/src/core/diagnostics/suggested-fixes.ts +2 -3
- package/src/core/generator/chunker.ts +1 -8
- package/src/core/generator/data-factory.ts +199 -61
- package/src/core/generator/fixtures-builder.ts +38 -31
- package/src/core/generator/index.ts +0 -2
- package/src/core/generator/openapi-reader.ts +98 -4
- package/src/core/generator/path-param-disambig.ts +30 -4
- package/src/core/generator/resources-builder.ts +276 -26
- package/src/core/generator/schema-utils.ts +22 -0
- package/src/core/generator/suite-generator.ts +168 -15
- package/src/core/generator/types.ts +6 -0
- package/src/core/identity/identity-file.ts +0 -0
- package/src/core/output/README.md +11 -29
- package/src/core/output/index.ts +1 -1
- package/src/core/output/run.ts +0 -35
- package/src/core/output/types.ts +0 -7
- package/src/core/parser/dynamic-values.ts +160 -0
- package/src/core/parser/variables.ts +0 -0
- package/src/core/probe/dry-run-envelope.ts +4 -0
- package/src/core/probe/mass-assignment/classify.ts +175 -0
- package/src/core/probe/mass-assignment/cleanup.ts +52 -0
- package/src/core/probe/mass-assignment/digest.ts +114 -0
- package/src/core/probe/mass-assignment/orchestrator.ts +459 -0
- package/src/core/probe/mass-assignment/regression.ts +141 -0
- package/src/core/probe/mass-assignment/suspects.ts +92 -0
- package/src/core/probe/mass-assignment/types.ts +135 -0
- package/src/core/probe/mass-assignment-probe.ts +23 -1118
- package/src/core/probe/mass-assignment-template.ts +32 -4
- package/src/core/probe/path-discovery.ts +3 -4
- package/src/core/probe/probe-harness.ts +21 -22
- package/src/core/probe/security/baseline.ts +174 -0
- package/src/core/probe/security/classify.ts +341 -0
- package/src/core/probe/security/cleanup.ts +125 -0
- package/src/core/probe/security/detectors.ts +71 -0
- package/src/core/probe/security/digest.ts +104 -0
- package/src/core/probe/security/orchestrator.ts +398 -0
- package/src/core/probe/security/regression.ts +103 -0
- package/src/core/probe/security/types.ts +151 -0
- package/src/core/probe/security-probe-class.ts +8 -2
- package/src/core/probe/security-probe.ts +28 -1449
- package/src/core/probe/shared.ts +26 -0
- package/src/core/probe/webhooks-probe.ts +5 -7
- package/src/core/runner/assertions.ts +1 -1
- package/src/core/runner/executor.ts +3 -18
- package/src/core/runner/form-encode.ts +8 -18
- package/src/core/runner/http-client.ts +38 -1
- package/src/core/runner/preflight-vars.ts +19 -15
- package/src/core/runner/rate-limiter.ts +11 -29
- package/src/core/runner/run-kind.ts +7 -1
- package/src/core/runner/schema-validator.ts +2 -6
- package/src/core/runner/send-request.ts +11 -6
- package/src/core/runner/types.ts +6 -0
- package/src/core/setup-api.ts +53 -15
- package/src/core/severity/index.ts +0 -63
- package/src/core/spec/infer-schema.ts +102 -0
- package/src/core/spec/merge-specs.ts +156 -0
- package/src/core/spec/schema-from-runs.ts +117 -0
- package/src/core/spec/schema-overlay.ts +130 -0
- package/src/core/util/ajv.ts +13 -0
- package/src/core/util/headers.ts +9 -0
- package/src/core/util/url.ts +24 -0
- package/src/core/workspace/fixture-gap-report.ts +84 -0
- package/src/core/workspace/fixture-gaps.ts +71 -0
- package/src/core/workspace/root.ts +13 -11
- package/src/db/migrate.ts +2 -0
- package/src/db/migrations/0002_run_kind_request.sql +59 -0
- package/src/db/queries/collections.ts +2 -2
- package/src/db/queries/results.ts +88 -0
- package/src/db/queries/runs.ts +56 -2
- package/src/db/queries.ts +3 -0
- package/src/db/schema.ts +7 -7
- package/src/cli/commands/bootstrap.ts +0 -710
- package/src/core/anti-fp/bootstrap.ts +0 -34
- package/src/core/anti-fp/index.ts +0 -33
- package/src/core/anti-fp/registry.ts +0 -44
- package/src/core/anti-fp/rules/baseline-echo.ts +0 -74
- package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +0 -52
- package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +0 -38
- package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +0 -35
- package/src/core/anti-fp/rules/schemathesis/index.ts +0 -24
- package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +0 -53
- package/src/core/anti-fp/rules/subscription-gated/index.ts +0 -11
- package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +0 -75
- package/src/core/anti-fp/types.ts +0 -68
- package/src/core/generator/create-body.ts +0 -89
|
@@ -3,13 +3,39 @@
|
|
|
3
3
|
* The runner builds a single-site negative case (one mutation against
|
|
4
4
|
* a valid body, see `_negative_mutator.ts`); if the server still
|
|
5
5
|
* accepts it (status outside 4xx/5xx + 401/403/404 admin set), we
|
|
6
|
-
* raise a finding
|
|
6
|
+
* raise a finding with raw evidence. The agent triages / re-severitizes.
|
|
7
7
|
*
|
|
8
8
|
* Default expected: 400 / 401 / 403 / 404 / 422 / 428 / 5xx.
|
|
9
9
|
* 2xx and 3xx with our payload are findings.
|
|
10
|
+
*
|
|
11
|
+
* Severity matrix (ARV-284, dispatched per finding via outcome.severity
|
|
12
|
+
* — see `severityForEvidence` below):
|
|
13
|
+
*
|
|
14
|
+
* - MEDIUM: concrete schema breach silently accepted (maxLength+1,
|
|
15
|
+
* type-mismatch, pattern-violation, format-invalid,
|
|
16
|
+
* drop-required, uniqueItems-violation, drop-required-query).
|
|
17
|
+
* Server should reject; evidence single-signal but breach
|
|
18
|
+
* is concrete.
|
|
19
|
+
* - LOW: `additionalProperties-violation` (unknown body fields
|
|
20
|
+
* silently dropped is documented forward-compat in many
|
|
21
|
+
* APIs — Stripe by design) and `wrong-type` query params
|
|
22
|
+
* on GET list endpoints (often "invalid id → empty
|
|
23
|
+
* result" documented behaviour). Single-signal, ambiguous
|
|
24
|
+
* intent.
|
|
25
|
+
*
|
|
26
|
+
* Note: 5xx on a negative mutation does NOT escalate this check —
|
|
27
|
+
* `not_a_server_error` (separate check, severity HIGH by design) owns
|
|
28
|
+
* the 5xx signal, and `ACCEPTABLE()` below treats 5xx as a non-silent
|
|
29
|
+
* accept so this check passes for those cases. Avoids double-counting.
|
|
30
|
+
*
|
|
31
|
+
* Per ARV-250's proof-cap principle (no evidence → no high severity):
|
|
32
|
+
* single-signal proof caps at LOW; concrete schema breach escalates to
|
|
33
|
+
* MEDIUM. The declared `severity: "low"` is the natural fallback /
|
|
34
|
+
* proof-cap baseline; stronger findings use `outcome.severity` to
|
|
35
|
+
* override. The agent re-severitizes from the raw evidence.
|
|
10
36
|
*/
|
|
11
|
-
import type { Check } from "../types.ts";
|
|
12
|
-
import {
|
|
37
|
+
import type { Check, CheckOutcome } from "../types.ts";
|
|
38
|
+
import type { Severity } from "../../severity/index.ts";
|
|
13
39
|
|
|
14
40
|
const ACCEPTABLE = (status: number): boolean => {
|
|
15
41
|
if (status === 400 || status === 401 || status === 403 || status === 404 || status === 422 || status === 428) return true;
|
|
@@ -17,29 +43,106 @@ const ACCEPTABLE = (status: number): boolean => {
|
|
|
17
43
|
return false;
|
|
18
44
|
};
|
|
19
45
|
|
|
46
|
+
/** Body-boundary labels we treat as LOW (often by-design vendor
|
|
47
|
+
* behaviour, single-signal, ambiguous intent). All other boundary
|
|
48
|
+
* labels emitted by `coverage-phase.ts:enumerateBoundaryCases` —
|
|
49
|
+
* maxLength+1, pattern-violation, uuid-invalid, drop-required:X, etc.
|
|
50
|
+
* — are concrete schema breaches and stay MEDIUM. */
|
|
51
|
+
const LOW_BODY_BOUNDARIES: ReadonlySet<string> = new Set([
|
|
52
|
+
"additionalProperties-violation",
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
/** Param-scenario labels (`coverage-phase.ts:enumerateParamBoundaryCases`)
|
|
56
|
+
* we treat as LOW. `wrong-type` is the classic "Stripe returns empty
|
|
57
|
+
* list for invalid id" pattern — many APIs document this; flagging
|
|
58
|
+
* HIGH/MEDIUM produces noise. `drop-required-query` stays MEDIUM (a
|
|
59
|
+
* declared-required param being silently optional is a concrete
|
|
60
|
+
* contract gap, not vendor convention). */
|
|
61
|
+
const LOW_PARAM_SCENARIOS_ON_GET: ReadonlySet<string> = new Set([
|
|
62
|
+
"wrong-type",
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
interface MutationMeta {
|
|
66
|
+
mutation?: string;
|
|
67
|
+
boundary?: string;
|
|
68
|
+
param_scenario?: string;
|
|
69
|
+
param_location?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function severityForEvidence(
|
|
73
|
+
meta: MutationMeta | undefined,
|
|
74
|
+
method: string,
|
|
75
|
+
): Severity {
|
|
76
|
+
if (!meta) return "low";
|
|
77
|
+
|
|
78
|
+
// Param-side mutations (query / path wrong-type, drop-required-query).
|
|
79
|
+
if (meta.mutation === "param-boundary") {
|
|
80
|
+
const scenario = meta.param_scenario ?? "";
|
|
81
|
+
if (
|
|
82
|
+
meta.param_location === "query"
|
|
83
|
+
&& method.toUpperCase() === "GET"
|
|
84
|
+
&& LOW_PARAM_SCENARIOS_ON_GET.has(scenario)
|
|
85
|
+
) {
|
|
86
|
+
return "low";
|
|
87
|
+
}
|
|
88
|
+
// drop-required-query, wrong-type on non-GET, wrong-type on path
|
|
89
|
+
// params: concrete contract violations server should reject.
|
|
90
|
+
return "medium";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Body-boundary mutations (additionalProperties, maxLength+1, etc).
|
|
94
|
+
if (meta.mutation === "boundary") {
|
|
95
|
+
const boundary = meta.boundary ?? "";
|
|
96
|
+
if (LOW_BODY_BOUNDARIES.has(boundary)) return "low";
|
|
97
|
+
return "medium";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Unknown mutation kind — single-signal fallback.
|
|
101
|
+
return "low";
|
|
102
|
+
}
|
|
103
|
+
|
|
20
104
|
export const negativeDataRejection: Check = {
|
|
21
105
|
id: "negative_data_rejection",
|
|
22
|
-
|
|
106
|
+
// ARV-284: declared severity is the *natural* tier (proof-cap baseline
|
|
107
|
+
// per ARV-250 — single-signal caps at LOW). Per-finding severity is
|
|
108
|
+
// dispatched via `outcome.severity` in `run()` below, so summary
|
|
109
|
+
// tables can show HIGH for 5xx and MEDIUM for concrete schema breach
|
|
110
|
+
// without globally setting the check to HIGH (which masks calibration).
|
|
111
|
+
severity: "low",
|
|
23
112
|
defaultExpected: "Server must reject invalid bodies with 400/401/403/404/422/428 (or 5xx)",
|
|
24
113
|
references: [{ id: "OWASP-API-08" }],
|
|
25
114
|
caseKinds: ["negative_data"],
|
|
26
115
|
applies: (op) => Boolean(op.requestBodySchema),
|
|
27
|
-
run({ case: c, response }) {
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
116
|
+
run({ case: c, response }): CheckOutcome {
|
|
117
|
+
const meta = c.meta as MutationMeta | undefined;
|
|
118
|
+
const method = c.operation.method.toUpperCase();
|
|
119
|
+
const bodyless = method === "GET" || method === "HEAD" || method === "DELETE";
|
|
120
|
+
// ARV-345 (scope fix, like ARV-340): don't manufacture "accepted an invalid
|
|
121
|
+
// body" findings on methods with no request-body semantics. A GET/HEAD/DELETE
|
|
122
|
+
// that 2xx's a mutated body simply ignored the body — correct, not a gap.
|
|
123
|
+
if (bodyless && meta?.mutation === "boundary") {
|
|
124
|
+
return { kind: "skip", reason: "body mutation not applicable on GET/HEAD/DELETE (no request-body semantics)" };
|
|
125
|
+
}
|
|
126
|
+
// ARV-345: an unknown / wrong-type QUERY param a GET tolerates (2xx) is
|
|
127
|
+
// documented leniency ("invalid id → empty list"), not a rejectable invalid.
|
|
128
|
+
// drop-required-query stays in scope — a declared-required param silently
|
|
129
|
+
// optional IS a concrete contract gap.
|
|
130
|
+
if (
|
|
131
|
+
bodyless
|
|
132
|
+
&& meta?.mutation === "param-boundary"
|
|
133
|
+
&& meta?.param_location === "query"
|
|
134
|
+
&& LOW_PARAM_SCENARIOS_ON_GET.has(meta?.param_scenario ?? "")
|
|
135
|
+
) {
|
|
136
|
+
return { kind: "skip", reason: "unknown/wrong-type query param on GET is documented leniency, not a validation gap" };
|
|
32
137
|
}
|
|
138
|
+
if (ACCEPTABLE(response.status)) return { kind: "pass" };
|
|
33
139
|
return {
|
|
34
140
|
kind: "fail",
|
|
35
141
|
message: `Server accepted an invalid body (status ${response.status}) — single-site mutation: ${
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
(c.meta as any)?.field_path
|
|
41
|
-
}`,
|
|
42
|
-
evidence: { status: response.status, mutation: c.meta },
|
|
142
|
+
meta?.mutation ?? "unknown"
|
|
143
|
+
} @ ${meta?.boundary ?? meta?.param_scenario ?? "?"}`,
|
|
144
|
+
evidence: { status: response.status, mutation: meta },
|
|
145
|
+
severity: severityForEvidence(meta, c.operation.method),
|
|
43
146
|
};
|
|
44
147
|
},
|
|
45
148
|
};
|
|
@@ -13,6 +13,14 @@ export const notAServerError: Check = {
|
|
|
13
13
|
references: [
|
|
14
14
|
{ id: "RFC-9110-15.6", url: "https://www.rfc-editor.org/rfc/rfc9110#name-server-error-5xx" },
|
|
15
15
|
],
|
|
16
|
+
// ARV-340: evaluate malformed-input cases too, not just the positive
|
|
17
|
+
// baseline. A negative_data / missing-header request is still a
|
|
18
|
+
// well-formed HTTP request the server must handle with a 4xx — a 5xx
|
|
19
|
+
// there is an unhandled crash (live Stripe: GET /v1/billing/alerts 500
|
|
20
|
+
// on a bad query param slipped through when this defaulted to positive
|
|
21
|
+
// only). `unsupported_method` is excluded on purpose: 501/405 to an
|
|
22
|
+
// undeclared method is legitimate, not a server error.
|
|
23
|
+
caseKinds: ["positive", "negative_data", "missing_required_header"],
|
|
16
24
|
applies: () => true,
|
|
17
25
|
run({ response }) {
|
|
18
26
|
if (response.status >= 500 && response.status < 600) {
|
|
@@ -99,30 +99,59 @@ export const openCorsOnSensitive: AuthStatefulCheck = {
|
|
|
99
99
|
const originIsStar = allowOrigin === "*";
|
|
100
100
|
const originReflects = allowOrigin === PROBE_ORIGIN;
|
|
101
101
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
102
|
+
// HIGH requires two independent pieces of evidence that authed data is
|
|
103
|
+
// actually CORS-readable by an attacker site:
|
|
104
|
+
//
|
|
105
|
+
// ARV-312: a 2xx response served real content under the reflected
|
|
106
|
+
// Origin. On a non-2xx (401/403/4xx/5xx) *this* response exposed no
|
|
107
|
+
// authenticated payload. Recording the real status also kills the
|
|
108
|
+
// phantom `response_summary.status: 0` (auth checks don't otherwise
|
|
109
|
+
// thread their response back to the runner).
|
|
110
|
+
//
|
|
111
|
+
// ARV-316: an AMBIENT credential the browser auto-attaches cross-origin
|
|
112
|
+
// — i.e. a cookie (apiKey-in-cookie scheme, or a Set-Cookie observed
|
|
113
|
+
// on the probe). `Allow-Credentials: true` is only exploitable with
|
|
114
|
+
// one: for bearer/header/oauth2 token auth the attacker's page can't
|
|
115
|
+
// set the victim's Authorization header, so a reflected Origin is a
|
|
116
|
+
// hygiene nit, not a data leak (Stripe et al are bearer-auth).
|
|
117
|
+
//
|
|
118
|
+
// Without both, cap at LOW per the m-21 "no evidence → no high" matrix.
|
|
119
|
+
const status = resp.status;
|
|
120
|
+
const isTwoXx = status >= 200 && status < 300;
|
|
121
|
+
const schemes = (h.doc?.components?.securitySchemes ?? {}) as Record<
|
|
122
|
+
string,
|
|
123
|
+
OpenAPIV3.SecuritySchemeObject | OpenAPIV3.ReferenceObject
|
|
124
|
+
>;
|
|
125
|
+
const usesCookieAuth = op.security.some((name) => {
|
|
126
|
+
const s = schemes[name];
|
|
127
|
+
return !!s && !("$ref" in s) && s.type === "apiKey" && s.in === "cookie";
|
|
128
|
+
});
|
|
129
|
+
const setCookie = getHeader(resp.headers, "set-cookie");
|
|
130
|
+
const ambientCredential = usesCookieAuth || setCookie !== undefined;
|
|
131
|
+
const authExposed = isTwoXx && ambientCredential;
|
|
132
|
+
const impactNote = authExposed
|
|
133
|
+
? "any attacker site can read authed cross-origin responses"
|
|
134
|
+
: !isTwoXx
|
|
135
|
+
? `observed on a ${status} response — cross-origin CORS is misconfigured but authed-data exposure is unproven`
|
|
136
|
+
: "no ambient (cookie) credential — bearer/token auth isn't auto-attached cross-origin, so the reflection isn't exploitable as a data leak";
|
|
137
|
+
|
|
138
|
+
if (credsTrue && (originIsStar || originReflects)) {
|
|
139
|
+
const variant = originIsStar ? "wildcard+credentials" : "reflected+credentials";
|
|
140
|
+
const shape = originIsStar
|
|
141
|
+
? "Allow-Origin: * with Allow-Credentials: true"
|
|
142
|
+
: "response reflects arbitrary Origin with Allow-Credentials: true";
|
|
117
143
|
return {
|
|
118
144
|
kind: "fail",
|
|
119
|
-
|
|
120
|
-
|
|
145
|
+
severity: authExposed ? "high" : "low",
|
|
146
|
+
responseStatus: status,
|
|
147
|
+
message: `CORS misconfiguration: ${shape} — ${impactNote}`,
|
|
121
148
|
evidence: {
|
|
122
149
|
request_origin: PROBE_ORIGIN,
|
|
150
|
+
response_status: status,
|
|
123
151
|
access_control_allow_origin: allowOrigin,
|
|
124
152
|
access_control_allow_credentials: allowCreds,
|
|
125
|
-
|
|
153
|
+
ambient_credential: ambientCredential,
|
|
154
|
+
variant,
|
|
126
155
|
},
|
|
127
156
|
};
|
|
128
157
|
}
|