@kirrosh/zond 0.26.0 → 0.27.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.
- package/CHANGELOG.md +59 -0
- package/README.md +22 -21
- package/bin/zond.mjs +23 -0
- package/package.json +13 -16
- package/scripts/npm/postinstall.mjs +76 -0
- package/src/CLAUDE.md +0 -112
- package/src/bun-types.d.ts +0 -5
- package/src/cli/argv.ts +0 -122
- package/src/cli/commands/add-api.ts +0 -146
- package/src/cli/commands/api/annotate/idempotency.ts +0 -59
- package/src/cli/commands/api/annotate/index.ts +0 -880
- package/src/cli/commands/api/annotate/lifecycle.ts +0 -74
- package/src/cli/commands/api/annotate/overlay.ts +0 -206
- package/src/cli/commands/api/annotate/pagination.ts +0 -64
- package/src/cli/commands/api/annotate/prompts.ts +0 -220
- package/src/cli/commands/api/annotate/readback.ts +0 -58
- package/src/cli/commands/api/annotate/resources.ts +0 -91
- package/src/cli/commands/api/annotate/seed-bodies.ts +0 -61
- package/src/cli/commands/audit.ts +0 -786
- package/src/cli/commands/catalog.ts +0 -97
- package/src/cli/commands/check.ts +0 -361
- package/src/cli/commands/checks.ts +0 -1072
- package/src/cli/commands/ci-init.ts +0 -223
- package/src/cli/commands/clean.ts +0 -212
- package/src/cli/commands/cleanup.ts +0 -236
- package/src/cli/commands/completions.ts +0 -192
- package/src/cli/commands/coverage.ts +0 -872
- package/src/cli/commands/db.ts +0 -611
- package/src/cli/commands/describe.ts +0 -120
- package/src/cli/commands/discover.ts +0 -1356
- package/src/cli/commands/doctor.ts +0 -661
- package/src/cli/commands/fixtures.ts +0 -402
- package/src/cli/commands/generate.ts +0 -577
- package/src/cli/commands/init/agents-md.ts +0 -61
- package/src/cli/commands/init/bootstrap.ts +0 -111
- package/src/cli/commands/init/index.ts +0 -244
- package/src/cli/commands/init/skills.ts +0 -141
- package/src/cli/commands/init/templates/agents.md +0 -86
- package/src/cli/commands/init/templates/markdown.d.ts +0 -4
- package/src/cli/commands/init/templates/skills/warm-up-target.md +0 -122
- package/src/cli/commands/init/templates/skills/zond-checks.md +0 -621
- package/src/cli/commands/init/templates/skills/zond-seed.md +0 -114
- package/src/cli/commands/init/templates/skills/zond-triage.md +0 -272
- package/src/cli/commands/init/templates/skills/zond.md +0 -861
- package/src/cli/commands/init/templates/zond-config.yml +0 -14
- package/src/cli/commands/prepare-fixtures.ts +0 -97
- package/src/cli/commands/probe/_seed-bodies.ts +0 -52
- package/src/cli/commands/probe/mass-assignment.ts +0 -594
- package/src/cli/commands/probe/security.ts +0 -537
- package/src/cli/commands/probe/static.ts +0 -255
- package/src/cli/commands/probe/webhooks.ts +0 -163
- package/src/cli/commands/probe.ts +0 -535
- package/src/cli/commands/reference.ts +0 -87
- package/src/cli/commands/refresh-api.ts +0 -227
- package/src/cli/commands/remove-api.ts +0 -150
- package/src/cli/commands/report-bundle.ts +0 -310
- package/src/cli/commands/report.ts +0 -241
- package/src/cli/commands/request.ts +0 -548
- package/src/cli/commands/run.ts +0 -1142
- package/src/cli/commands/schema-from-runs.ts +0 -128
- package/src/cli/commands/secrets.ts +0 -133
- package/src/cli/commands/session.ts +0 -244
- package/src/cli/commands/use.ts +0 -74
- package/src/cli/index.ts +0 -55
- package/src/cli/json-envelope.ts +0 -108
- package/src/cli/json-schemas.ts +0 -314
- package/src/cli/output.ts +0 -40
- package/src/cli/program.ts +0 -219
- package/src/cli/resolve.ts +0 -105
- package/src/cli/runtime.ts +0 -7
- package/src/cli/safe-live.ts +0 -24
- package/src/cli/status-filter.ts +0 -114
- package/src/cli/util/api-context.ts +0 -85
- package/src/cli/version.ts +0 -8
- package/src/core/audit/persist.ts +0 -183
- package/src/core/checks/budget.ts +0 -59
- package/src/core/checks/checks/_crud-helpers.ts +0 -133
- package/src/core/checks/checks/_negative_mutator.ts +0 -133
- package/src/core/checks/checks/_readback-helpers.ts +0 -133
- package/src/core/checks/checks/content_type_conformance.ts +0 -39
- package/src/core/checks/checks/cross_call_references.ts +0 -147
- package/src/core/checks/checks/cursor_boundary_fuzzing.ts +0 -219
- package/src/core/checks/checks/ensure_resource_availability.ts +0 -62
- package/src/core/checks/checks/idempotency_replay.ts +0 -242
- package/src/core/checks/checks/ignored_auth.ts +0 -254
- package/src/core/checks/checks/index.ts +0 -68
- package/src/core/checks/checks/lifecycle_transitions.ts +0 -416
- package/src/core/checks/checks/missing_required_header.ts +0 -40
- package/src/core/checks/checks/negative_data_rejection.ts +0 -148
- package/src/core/checks/checks/not_a_server_error.ts +0 -35
- package/src/core/checks/checks/open_cors_on_sensitive.ts +0 -160
- package/src/core/checks/checks/pagination_invariants.ts +0 -419
- package/src/core/checks/checks/positive_data_acceptance.ts +0 -33
- package/src/core/checks/checks/rate_limit_headers_absent.ts +0 -77
- package/src/core/checks/checks/response_headers_conformance.ts +0 -74
- package/src/core/checks/checks/response_schema_conformance.ts +0 -30
- package/src/core/checks/checks/status_code_conformance.ts +0 -132
- package/src/core/checks/checks/unsupported_method.ts +0 -63
- package/src/core/checks/checks/use_after_free.ts +0 -78
- package/src/core/checks/index.ts +0 -30
- package/src/core/checks/mode.ts +0 -82
- package/src/core/checks/recommended-action.ts +0 -68
- package/src/core/checks/registry.ts +0 -78
- package/src/core/checks/runner.ts +0 -1461
- package/src/core/checks/sarif.ts +0 -230
- package/src/core/checks/spec-findings.ts +0 -308
- package/src/core/checks/stateful.ts +0 -121
- package/src/core/checks/types.ts +0 -305
- package/src/core/checks/zond-extensions.ts +0 -73
- package/src/core/classifier/recommended-action.ts +0 -251
- package/src/core/context/current.ts +0 -51
- package/src/core/context/session.ts +0 -78
- package/src/core/coverage/loader.ts +0 -216
- package/src/core/coverage/reasons.ts +0 -300
- package/src/core/diagnostics/db-analysis.ts +0 -666
- package/src/core/diagnostics/failure-class.ts +0 -140
- package/src/core/diagnostics/failure-hints.ts +0 -112
- package/src/core/diagnostics/spec-pointer.ts +0 -99
- package/src/core/diagnostics/suggested-fixes.ts +0 -155
- package/src/core/exporter/case-study/index.ts +0 -270
- package/src/core/exporter/curl.ts +0 -40
- package/src/core/exporter/exporter.ts +0 -48
- package/src/core/exporter/html-report/escape.ts +0 -24
- package/src/core/exporter/html-report/index.ts +0 -479
- package/src/core/exporter/html-report/script.ts +0 -100
- package/src/core/exporter/html-report/styles.ts +0 -408
- package/src/core/generator/catalog-builder.ts +0 -179
- package/src/core/generator/chunker.ts +0 -78
- package/src/core/generator/coverage-phase.ts +0 -0
- package/src/core/generator/coverage-scanner.ts +0 -87
- package/src/core/generator/data-factory.ts +0 -759
- package/src/core/generator/describe.ts +0 -302
- package/src/core/generator/endpoint-warnings.ts +0 -52
- package/src/core/generator/fixtures-builder.ts +0 -332
- package/src/core/generator/index.ts +0 -14
- package/src/core/generator/openapi-reader.ts +0 -297
- package/src/core/generator/path-param-disambig.ts +0 -140
- package/src/core/generator/resources-builder.ts +0 -898
- package/src/core/generator/schema-utils.ts +0 -112
- package/src/core/generator/serializer.ts +0 -343
- package/src/core/generator/suite-generator.ts +0 -1301
- package/src/core/generator/types.ts +0 -63
- package/src/core/identity/identity-file.ts +0 -0
- package/src/core/lint/affects.ts +0 -28
- package/src/core/lint/config.ts +0 -96
- package/src/core/lint/format.ts +0 -42
- package/src/core/lint/index.ts +0 -94
- package/src/core/lint/reporter.ts +0 -128
- package/src/core/lint/rules/consistency.ts +0 -158
- package/src/core/lint/rules/heuristics.ts +0 -97
- package/src/core/lint/rules/strictness.ts +0 -109
- package/src/core/lint/types.ts +0 -96
- package/src/core/lint/walker.ts +0 -248
- package/src/core/meta/meta-store.ts +0 -11
- package/src/core/output/README.md +0 -73
- package/src/core/output/index.ts +0 -13
- package/src/core/output/run.ts +0 -91
- package/src/core/output/types.ts +0 -122
- package/src/core/parser/dynamic-values.ts +0 -160
- package/src/core/parser/env-interpolation.ts +0 -104
- package/src/core/parser/filter.ts +0 -97
- package/src/core/parser/schema.ts +0 -359
- package/src/core/parser/types.ts +0 -117
- package/src/core/parser/variables.ts +0 -0
- package/src/core/parser/yaml-parser.ts +0 -181
- package/src/core/probe/bootstrap.ts +0 -34
- package/src/core/probe/dry-run-envelope.ts +0 -61
- package/src/core/probe/mass-assignment/classify.ts +0 -175
- package/src/core/probe/mass-assignment/cleanup.ts +0 -52
- package/src/core/probe/mass-assignment/digest.ts +0 -114
- package/src/core/probe/mass-assignment/orchestrator.ts +0 -459
- package/src/core/probe/mass-assignment/regression.ts +0 -141
- package/src/core/probe/mass-assignment/suspects.ts +0 -92
- package/src/core/probe/mass-assignment/types.ts +0 -135
- package/src/core/probe/mass-assignment-probe-class.ts +0 -198
- package/src/core/probe/mass-assignment-probe.ts +0 -27
- package/src/core/probe/mass-assignment-template.ts +0 -240
- package/src/core/probe/method-probe.ts +0 -164
- package/src/core/probe/method-shared.ts +0 -69
- package/src/core/probe/negative-probe.ts +0 -691
- package/src/core/probe/orphan-tracker.ts +0 -188
- package/src/core/probe/path-discovery.ts +0 -439
- package/src/core/probe/probe-harness.ts +0 -119
- package/src/core/probe/registry.ts +0 -89
- package/src/core/probe/runner.ts +0 -136
- package/src/core/probe/security/baseline.ts +0 -174
- package/src/core/probe/security/classify.ts +0 -341
- package/src/core/probe/security/cleanup.ts +0 -125
- package/src/core/probe/security/detectors.ts +0 -71
- package/src/core/probe/security/digest.ts +0 -104
- package/src/core/probe/security/orchestrator.ts +0 -398
- package/src/core/probe/security/regression.ts +0 -103
- package/src/core/probe/security/types.ts +0 -151
- package/src/core/probe/security-probe-class.ts +0 -207
- package/src/core/probe/security-probe.ts +0 -32
- package/src/core/probe/shared.ts +0 -531
- package/src/core/probe/static-probe-class.ts +0 -125
- package/src/core/probe/types.ts +0 -165
- package/src/core/probe/verdict-aggregator.ts +0 -33
- package/src/core/probe/webhooks-probe.ts +0 -282
- package/src/core/reporter/console.ts +0 -240
- package/src/core/reporter/index.ts +0 -22
- package/src/core/reporter/json.ts +0 -22
- package/src/core/reporter/junit.ts +0 -93
- package/src/core/reporter/ndjson.ts +0 -37
- package/src/core/reporter/types.ts +0 -15
- package/src/core/runner/assertions.ts +0 -383
- package/src/core/runner/async-pool.ts +0 -108
- package/src/core/runner/auth-path.ts +0 -8
- package/src/core/runner/ci-context.ts +0 -72
- package/src/core/runner/executor.ts +0 -652
- package/src/core/runner/expr-eval.ts +0 -41
- package/src/core/runner/form-encode.ts +0 -41
- package/src/core/runner/http-client.ts +0 -224
- package/src/core/runner/learn-drift.ts +0 -293
- package/src/core/runner/preflight-vars.ts +0 -153
- package/src/core/runner/progress-tracker.ts +0 -73
- package/src/core/runner/rate-limiter.ts +0 -185
- package/src/core/runner/run-kind.ts +0 -45
- package/src/core/runner/schema-validator.ts +0 -308
- package/src/core/runner/send-request.ts +0 -232
- package/src/core/runner/transforms.ts +0 -65
- package/src/core/runner/types.ts +0 -94
- package/src/core/secrets/registry.ts +0 -164
- package/src/core/secrets/secrets-file.ts +0 -115
- package/src/core/selectors/operation-filter.ts +0 -144
- package/src/core/setup-api.ts +0 -590
- package/src/core/severity/category.ts +0 -94
- package/src/core/severity/index.ts +0 -58
- package/src/core/spec/infer-schema.ts +0 -102
- package/src/core/spec/layers.ts +0 -154
- package/src/core/spec/merge-specs.ts +0 -156
- package/src/core/spec/schema-from-runs.ts +0 -117
- package/src/core/spec/schema-overlay.ts +0 -130
- package/src/core/util/ajv.ts +0 -13
- package/src/core/util/format-eta.ts +0 -21
- package/src/core/util/headers.ts +0 -9
- package/src/core/util/url.ts +0 -24
- package/src/core/utils.ts +0 -13
- package/src/core/workspace/config.ts +0 -129
- package/src/core/workspace/fixture-gap-report.ts +0 -84
- package/src/core/workspace/fixture-gaps.ts +0 -71
- package/src/core/workspace/manifest.ts +0 -283
- package/src/core/workspace/output-rotation.ts +0 -62
- package/src/core/workspace/root.ts +0 -96
- package/src/core/workspace/triage-path.ts +0 -87
- package/src/db/lint-runs.ts +0 -47
- package/src/db/migrate.ts +0 -128
- package/src/db/migrations/0001_run_kind.sql +0 -25
- package/src/db/migrations/0002_run_kind_request.sql +0 -59
- package/src/db/migrations/sql.d.ts +0 -4
- package/src/db/queries/collections.ts +0 -133
- package/src/db/queries/coverage.ts +0 -9
- package/src/db/queries/dashboard.ts +0 -59
- package/src/db/queries/results.ts +0 -216
- package/src/db/queries/runs.ts +0 -289
- package/src/db/queries/sessions.ts +0 -42
- package/src/db/queries/settings.ts +0 -28
- package/src/db/queries/types.ts +0 -172
- package/src/db/queries.ts +0 -75
- package/src/db/schema.ts +0 -298
package/src/core/checks/types.ts
DELETED
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type contracts for the `zond checks` framework (m-15 ARV-1).
|
|
3
|
-
*
|
|
4
|
-
* A `Check` is a single named conformance/security probe applied to one
|
|
5
|
-
* operation × one HTTP response. The `runner` resolves the spec into
|
|
6
|
-
* operations, generates a request via `core/generator/data-factory`,
|
|
7
|
-
* sends it via `core/runner/send-request`, and feeds each (case,
|
|
8
|
-
* response) pair to every active check.
|
|
9
|
-
*
|
|
10
|
-
* Names mirror schemathesis V4 1-to-1 so benchmarks and findings carry
|
|
11
|
-
* across (see `backlog/milestones/m-15`). New checks register themselves
|
|
12
|
-
* in `checks/index.ts` via `registerCheck`.
|
|
13
|
-
*/
|
|
14
|
-
import type { OpenAPIV3 } from "openapi-types";
|
|
15
|
-
import type { EndpointInfo } from "../generator/types.ts";
|
|
16
|
-
import type { SchemaValidator } from "../runner/schema-validator.ts";
|
|
17
|
-
import type { RecommendedAction } from "../diagnostics/failure-hints.ts";
|
|
18
|
-
|
|
19
|
-
// Severity unified in src/core/severity (ARV-250). Re-exported here for
|
|
20
|
-
// backwards-compatible imports across the checks subsystem; the canonical
|
|
21
|
-
// definition is in core/severity. Adds 'info' tier (previously absent in
|
|
22
|
-
// checks, now needed for hygiene-class findings per m-21 pivot).
|
|
23
|
-
import type { Severity } from "../severity/index.ts";
|
|
24
|
-
import { emptySeverityBuckets } from "../severity/index.ts";
|
|
25
|
-
import type { Category } from "../severity/category.ts";
|
|
26
|
-
import { emptyCategoryBuckets } from "../severity/category.ts";
|
|
27
|
-
export type { Severity, Category };
|
|
28
|
-
|
|
29
|
-
export type Phase = "examples" | "coverage" | "all";
|
|
30
|
-
|
|
31
|
-
/** Probe shapes a check may need. ARV-1 shipped only `positive`; ARV-2
|
|
32
|
-
* adds two more for header/method-rejection checks; ARV-4 will add
|
|
33
|
-
* `negative_data` for the data-rejection pair. The runner generates a
|
|
34
|
-
* case for each kind that at least one active check declares. */
|
|
35
|
-
export type CaseKind =
|
|
36
|
-
| "positive"
|
|
37
|
-
| "missing_required_header"
|
|
38
|
-
| "unsupported_method"
|
|
39
|
-
| "negative_data";
|
|
40
|
-
|
|
41
|
-
export interface CheckReference {
|
|
42
|
-
/** CWE / OWASP / RFC identifier — free form, agent-readable. */
|
|
43
|
-
id: string;
|
|
44
|
-
/** Optional URL for the human reader. */
|
|
45
|
-
url?: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface CheckResponse {
|
|
49
|
-
status: number;
|
|
50
|
-
headers: Record<string, string>;
|
|
51
|
-
body: unknown;
|
|
52
|
-
duration_ms: number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface CheckCase {
|
|
56
|
-
/** The operation under test (path + method + parameters). */
|
|
57
|
-
operation: EndpointInfo;
|
|
58
|
-
/** Resolved request that produced the response below. */
|
|
59
|
-
request: {
|
|
60
|
-
method: string;
|
|
61
|
-
url: string;
|
|
62
|
-
headers: Record<string, string>;
|
|
63
|
-
body?: string;
|
|
64
|
-
};
|
|
65
|
-
/** Mode the case was generated under — positive (valid) or negative
|
|
66
|
-
* (intentionally invalid). Drives the ARV-7 `--mode` filter. */
|
|
67
|
-
mode: "positive" | "negative";
|
|
68
|
-
/** Probe shape — what the case is *built* to exercise. A check only
|
|
69
|
-
* runs against a case whose `kind` it declared (or all if it
|
|
70
|
-
* declared none — defaults to `positive`). */
|
|
71
|
-
kind: CaseKind;
|
|
72
|
-
/** For probe-style cases, an opaque hint the check itself produced
|
|
73
|
-
* (e.g. the header name that was dropped, the method that was
|
|
74
|
-
* swapped in). Lets a check emit a precise finding without parsing
|
|
75
|
-
* the request back out. */
|
|
76
|
-
meta?: Record<string, unknown>;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** ARV-179: per-run knobs surfaced from `RunCheckOptions` into the
|
|
80
|
-
* check itself. Add new fields here when a check needs an opt-in
|
|
81
|
-
* behaviour that doesn't deserve its own check id. Keep this lean —
|
|
82
|
-
* most knobs belong upstream in the case-generator, not in the
|
|
83
|
-
* check's response-side logic. */
|
|
84
|
-
export interface CheckRuntimeOptions {
|
|
85
|
-
/** ARV-179: require strict 405 for `unsupported_method` (matches
|
|
86
|
-
* schemathesis V4 default). Off by default — zond's pragmatic policy
|
|
87
|
-
* accepts 401/403/404 as legitimate rejections of an undeclared
|
|
88
|
-
* method (common nginx/gateway behaviour). */
|
|
89
|
-
strict405?: boolean;
|
|
90
|
-
/** ARV-181: require strict 401 for `ignored_auth` no-auth / bogus-auth
|
|
91
|
-
* variants (matches schemathesis V4 default). Off by default — zond's
|
|
92
|
-
* pragmatic policy accepts any 4xx as a legitimate auth-reject (403,
|
|
93
|
-
* 404, 422 are common). With this on, only 401 passes — a 403 on
|
|
94
|
-
* no_auth becomes a finding "expected 401, got 403". */
|
|
95
|
-
strict401?: boolean;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface CheckContext {
|
|
99
|
-
case: CheckCase;
|
|
100
|
-
response: CheckResponse;
|
|
101
|
-
/** Pre-built once per run so checks don't pay AJV compile cost
|
|
102
|
-
* per-case. Optional so unit tests can stub a context without one. */
|
|
103
|
-
schemaValidator?: SchemaValidator;
|
|
104
|
-
/** Original spec doc — checks that need declared headers /
|
|
105
|
-
* content-types / status codes look them up here. */
|
|
106
|
-
doc?: OpenAPIV3.Document;
|
|
107
|
-
/** ARV-179: per-run knobs (see CheckRuntimeOptions). */
|
|
108
|
-
options?: CheckRuntimeOptions;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export type CheckOutcome =
|
|
112
|
-
| { kind: "pass" }
|
|
113
|
-
| { kind: "skip"; reason: string }
|
|
114
|
-
| {
|
|
115
|
-
kind: "fail";
|
|
116
|
-
message: string;
|
|
117
|
-
evidence?: Record<string, unknown>;
|
|
118
|
-
/** ARV-284: per-finding severity override. When set, runner uses
|
|
119
|
-
* this in place of `Check.severity` — lets a check emit different
|
|
120
|
-
* severities based on context (e.g. `negative_data_rejection`
|
|
121
|
-
* with `additionalProperties-violation` evidence → LOW, with
|
|
122
|
-
* `pattern-violation` → MEDIUM, with 5xx response → HIGH). The
|
|
123
|
-
* declared `Check.severity` stays as the natural fallback /
|
|
124
|
-
* documentation tier. The agent re-severitizes from the raw
|
|
125
|
-
* evidence downstream. */
|
|
126
|
-
severity?: Severity;
|
|
127
|
-
/** ARV-310: attribute the finding to a specific operation instead of
|
|
128
|
-
* the CRUD group's canonical create/read op. cursor_boundary_fuzzing
|
|
129
|
-
* probes the GET list endpoint — without this the finding lands on the
|
|
130
|
-
* POST create and reads as "a create endpoint that doesn't paginate". */
|
|
131
|
-
operation?: { path: string; method: string; operationId?: string };
|
|
132
|
-
/** ARV-312: observed HTTP status of the response the check acted on.
|
|
133
|
-
* Auth/stateful checks send their own requests, so the runner has no
|
|
134
|
-
* response to summarize and otherwise records `status: 0` — a phantom
|
|
135
|
-
* that reads as "no response captured". Set this so the finding carries
|
|
136
|
-
* the real status (and severity gating can key off it). */
|
|
137
|
-
responseStatus?: number;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
export interface Check {
|
|
141
|
-
/** Stable identifier — must match schemathesis name where possible. */
|
|
142
|
-
id: string;
|
|
143
|
-
severity: Severity;
|
|
144
|
-
/** Default expected outcome ("server should NOT 5xx", etc) — surfaced
|
|
145
|
-
* by `zond checks list` so an agent can read the contract. */
|
|
146
|
-
defaultExpected: string;
|
|
147
|
-
references: CheckReference[];
|
|
148
|
-
/** Probe shapes this check consumes. Default `["positive"]` — most
|
|
149
|
-
* conformance checks just inspect the standard response. ARV-2's
|
|
150
|
-
* `missing_required_header` / `unsupported_method` declare their
|
|
151
|
-
* own kinds so the runner generates the matching probe case. */
|
|
152
|
-
caseKinds?: CaseKind[];
|
|
153
|
-
/** Whether this check is meaningful for the given operation. Used by
|
|
154
|
-
* the runner to skip checks that don't apply (e.g. auth-related
|
|
155
|
-
* checks on operations with no security requirement). */
|
|
156
|
-
applies(operation: EndpointInfo): boolean;
|
|
157
|
-
run(ctx: CheckContext): CheckOutcome;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export interface CheckFinding {
|
|
161
|
-
check: string;
|
|
162
|
-
severity: Severity;
|
|
163
|
-
/** ARV-251: finding category drives per-section roll-up in reports.
|
|
164
|
-
* Optional for backwards compat — when absent, downstream code derives
|
|
165
|
-
* it via `categoryFor(check)`. Storing it on the finding keeps probe
|
|
166
|
-
* emitters and check emitters using the same shape. */
|
|
167
|
-
category?: Category;
|
|
168
|
-
operation: { path: string; method: string; operationId?: string };
|
|
169
|
-
request_signature: string;
|
|
170
|
-
response_summary: { status: number; content_type?: string };
|
|
171
|
-
message: string;
|
|
172
|
-
evidence?: Record<string, unknown>;
|
|
173
|
-
/** ARV-11 — closed enum so agents can route on it without parsing
|
|
174
|
-
* the message. Resolved by `recommendForCheck()` keyed on the
|
|
175
|
-
* check id (and response status for `network_error`). Optional
|
|
176
|
-
* because synthetic findings (e.g. unit-test fakes) may skip it. */
|
|
177
|
-
recommended_action?: RecommendedAction;
|
|
178
|
-
/** Suppression trace — present when a finding was removed from the
|
|
179
|
-
* gate counts (today only the deterministic broken-baseline guard,
|
|
180
|
-
* ARV-307, marks findings this way). CI summary excludes such findings
|
|
181
|
-
* from gate counts via this field's presence; presence of
|
|
182
|
-
* `suppressed_by` is the canonical "suppressed" signal. */
|
|
183
|
-
suppressed_by?: {
|
|
184
|
-
source: string;
|
|
185
|
-
rule_index: number;
|
|
186
|
-
reason: string;
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export interface CheckRunSummary {
|
|
191
|
-
operations: number;
|
|
192
|
-
cases: number;
|
|
193
|
-
checks_run: number;
|
|
194
|
-
findings: number;
|
|
195
|
-
by_severity: Record<Severity, number>;
|
|
196
|
-
/** ARV-251: per-category roll-up — small teams use this to triage
|
|
197
|
-
* "0 security, 12 reliability, 40 contract, 200 hygiene" instead of
|
|
198
|
-
* reading one flat severity pile. */
|
|
199
|
-
by_category: Record<Category, number>;
|
|
200
|
-
/** ARV-26: count of `kind: "skip"` outcomes returned by checks, keyed by
|
|
201
|
-
* `"<check_id>: <reason>"`. Surfaces the gap between probe and runtime
|
|
202
|
-
* validators — e.g. `response_schema_conformance: no JSON Schema on this
|
|
203
|
-
* response branch ×2` tells the user why "0 findings" doesn't mean "all
|
|
204
|
-
* green" (probe got 4xx, response schema only declared on 2xx). */
|
|
205
|
-
skipped_outcomes: Record<string, number>;
|
|
206
|
-
/** ARV-83: same data as `skipped_outcomes`, but split into `{check, reason,
|
|
207
|
-
* count}` so consumers don't have to colon-tokenise a key whose reason
|
|
208
|
-
* text may itself contain colons. Sorted by count descending. The legacy
|
|
209
|
-
* `skipped_outcomes` field is kept for back-compat with existing parsers
|
|
210
|
-
* / NDJSON readers. */
|
|
211
|
-
skipped_outcomes_grouped: Array<{ check: string; reason: string; count: number }>;
|
|
212
|
-
/** Count of findings suppressed by the deterministic broken-baseline
|
|
213
|
-
* guard (ARV-307). Excluded from `findings`/`by_severity` so CI gates
|
|
214
|
-
* ignore them, but surfaced here for audit-trail reconciliation. */
|
|
215
|
-
suppressed?: number;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** ARV-60: spec-level rollup of a systemic gap that manifests on N
|
|
219
|
-
* operations. When ≥80% of a check's applicable operations share the same
|
|
220
|
-
* root cause (same response status undeclared, same missing-schema skip
|
|
221
|
-
* reason, or zero cases when a detector finds no pair), the runner emits
|
|
222
|
-
* a single `SpecFinding` instead of (or in addition to) the N per-op rows.
|
|
223
|
-
*
|
|
224
|
-
* Consumed by the CLI to print one summary line; surfaced verbatim in the
|
|
225
|
-
* JSON envelope and as a dedicated `spec_finding` NDJSON event. Per-op
|
|
226
|
-
* findings are NOT removed from `findings[]` — agents that prefer per-op
|
|
227
|
-
* triage keep their existing surface; agents that triage by spec hit just
|
|
228
|
-
* one row per drift. */
|
|
229
|
-
export interface SpecFinding {
|
|
230
|
-
check: string;
|
|
231
|
-
/** Classifier so consumers can branch:
|
|
232
|
-
* - `status_drift`: response status code clustered across operations
|
|
233
|
-
* (status_code_conformance / negative_data_rejection / ignored_auth).
|
|
234
|
-
* - `missing_declaration`: every applicable case skipped for the same
|
|
235
|
-
* "spec didn't declare X" reason (response_schema_conformance,
|
|
236
|
-
* response_headers_conformance).
|
|
237
|
-
* - `no_detector`: check is applicable to ≥5 operations but ran 0 cases
|
|
238
|
-
* (use_after_free without DELETE+GET pair, cross_call_references
|
|
239
|
-
* without followups in scope).
|
|
240
|
-
* - `broken_baseline`: ARV-307 — the positive/success baseline was
|
|
241
|
-
* degenerate (>90% of positive probes returned non-2xx, e.g. a
|
|
242
|
-
* fully auth-rejected scan). The conformance checks' per-op findings
|
|
243
|
-
* are baseline artifacts, so they're rolled up into this one row and
|
|
244
|
-
* removed from `findings[]` (mirrors the stateful broken-baseline skip).
|
|
245
|
-
* - `other`: skip-cluster that doesn't fit the above. */
|
|
246
|
-
kind: "status_drift" | "missing_declaration" | "no_detector" | "broken_baseline" | "other";
|
|
247
|
-
/** Severity inherited from the underlying findings (status_drift) or
|
|
248
|
-
* fixed to "info" for missing_declaration / no_detector — those signal
|
|
249
|
-
* "spec gap, not server bug" so the team knows where to act. */
|
|
250
|
-
severity: Severity;
|
|
251
|
-
category?: Category;
|
|
252
|
-
/** One-line root cause statement — surfaces what zond observed. */
|
|
253
|
-
reason: string;
|
|
254
|
-
/** Actionable next step. References a spec edit, a tolerate flag, or
|
|
255
|
-
* another zond command. Empty string when no automatic suggestion. */
|
|
256
|
-
fix_hint: string;
|
|
257
|
-
/** Operations the rollup covers (path + method). count = length. */
|
|
258
|
-
affected_operations: Array<{ path: string; method: string; operationId?: string }>;
|
|
259
|
-
count: number;
|
|
260
|
-
/** Applicable population the cluster was measured against. ratio =
|
|
261
|
-
* count / applicable. Lets consumers re-threshold without re-running. */
|
|
262
|
-
applicable: number;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export interface CheckRunData {
|
|
266
|
-
findings: CheckFinding[];
|
|
267
|
-
summary: CheckRunSummary;
|
|
268
|
-
/** ARV-60: spec-level rollup, see SpecFinding. Always present (empty
|
|
269
|
-
* array when no clusters cross the 80% threshold). */
|
|
270
|
-
spec_findings: SpecFinding[];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
export function emptySummary(): CheckRunSummary {
|
|
274
|
-
return {
|
|
275
|
-
operations: 0,
|
|
276
|
-
cases: 0,
|
|
277
|
-
checks_run: 0,
|
|
278
|
-
findings: 0,
|
|
279
|
-
by_severity: emptySeverityBuckets(),
|
|
280
|
-
by_category: emptyCategoryBuckets(),
|
|
281
|
-
skipped_outcomes: {},
|
|
282
|
-
skipped_outcomes_grouped: [],
|
|
283
|
-
suppressed: 0,
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/** ARV-83: turn the legacy `<check>: <reason>` keys into a structured
|
|
288
|
-
* array. The split parses the colon-separator at the first occurrence; if
|
|
289
|
-
* the reason itself contains colons, only the LEADING `check_id:` is
|
|
290
|
-
* stripped, preserving the rest verbatim. */
|
|
291
|
-
export function groupSkippedOutcomes(
|
|
292
|
-
outcomes: Record<string, number>,
|
|
293
|
-
): Array<{ check: string; reason: string; count: number }> {
|
|
294
|
-
const out: Array<{ check: string; reason: string; count: number }> = [];
|
|
295
|
-
for (const [key, count] of Object.entries(outcomes)) {
|
|
296
|
-
const idx = key.indexOf(": ");
|
|
297
|
-
if (idx > 0) {
|
|
298
|
-
out.push({ check: key.slice(0, idx), reason: key.slice(idx + 2), count });
|
|
299
|
-
} else {
|
|
300
|
-
out.push({ check: key, reason: "", count });
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
out.sort((a, b) => b.count - a.count);
|
|
304
|
-
return out;
|
|
305
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `x-zond-*` OpenAPI vendor-extension policy (ARV-189, m-21).
|
|
3
|
-
*
|
|
4
|
-
* Lets spec authors declare per-operation rules directly in the spec
|
|
5
|
-
* without a sidecar yaml file. This is the low-friction alternative
|
|
6
|
-
* to `.api-resources.local.yaml` for one-off endpoints — typical
|
|
7
|
-
* cases:
|
|
8
|
-
* - public route that shouldn't be flagged for missing auth headers
|
|
9
|
-
* - debugging endpoint whose 500s aren't real bugs
|
|
10
|
-
* - one-off skip during incident triage
|
|
11
|
-
*
|
|
12
|
-
* Resolution priority (highest first):
|
|
13
|
-
* 1. `.api-resources.local.yaml` overlay (explicit operator override)
|
|
14
|
-
* 2. `x-zond-*` extensions (in-spec policy — this module)
|
|
15
|
-
* 3. `.api-resources.yaml` (auto-generated baseline)
|
|
16
|
-
* 4. built-in defaults
|
|
17
|
-
*
|
|
18
|
-
* Supported extensions in this milestone:
|
|
19
|
-
* x-zond-skip: string | string[] — check ids to skip for this op
|
|
20
|
-
* x-zond-public: boolean — shortcut: skip auth-class checks
|
|
21
|
-
*
|
|
22
|
-
* Deliberately NOT implemented yet (deferred to a follow-up task):
|
|
23
|
-
* x-zond-resource / x-zond-idempotent / x-zond-lifecycle-field —
|
|
24
|
-
* these require deeper m-20 overlay wiring (proper integration with
|
|
25
|
-
* resource-config maps), tracked separately to keep this MVP focused
|
|
26
|
-
* on the universally-useful skip rules.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import type { EndpointInfo } from "../generator/types.ts";
|
|
30
|
-
|
|
31
|
-
/** Auth-related check ids that `x-zond-public: true` should suppress.
|
|
32
|
-
* Kept in sync with the auth-class check registry in mode.ts. */
|
|
33
|
-
const AUTH_CHECK_IDS = new Set<string>([
|
|
34
|
-
"ignored_auth",
|
|
35
|
-
"missing_required_header",
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
/** True when the endpoint declares the check id should be skipped via
|
|
39
|
-
* any `x-zond-*` extension. The check id is matched case-sensitively
|
|
40
|
-
* against `x-zond-skip` entries; `x-zond-public: true` is expanded to
|
|
41
|
-
* the auth-id set before comparison. */
|
|
42
|
-
export function endpointSkipsCheck(op: EndpointInfo, checkId: string): boolean {
|
|
43
|
-
const ext = op.extensions;
|
|
44
|
-
if (!ext) return false;
|
|
45
|
-
|
|
46
|
-
// x-zond-public: true → implicit skip for every auth-class check.
|
|
47
|
-
if (ext["x-zond-public"] === true && AUTH_CHECK_IDS.has(checkId)) {
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// x-zond-skip: <string> | <string[]>
|
|
52
|
-
const skip = ext["x-zond-skip"];
|
|
53
|
-
if (typeof skip === "string") {
|
|
54
|
-
return skip === checkId;
|
|
55
|
-
}
|
|
56
|
-
if (Array.isArray(skip)) {
|
|
57
|
-
for (const entry of skip) {
|
|
58
|
-
if (typeof entry === "string" && entry === checkId) return true;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Reason string surfaced in the skipped-outcomes summary. Mirrors the
|
|
65
|
-
* `<check>: <reason>` convention used elsewhere in the runner. */
|
|
66
|
-
export function reasonForSkip(op: EndpointInfo, checkId: string): string {
|
|
67
|
-
const ext = op.extensions;
|
|
68
|
-
if (!ext) return "x-zond extension"; // shouldn't happen — caller gates on endpointSkipsCheck
|
|
69
|
-
if (ext["x-zond-public"] === true && AUTH_CHECK_IDS.has(checkId)) {
|
|
70
|
-
return "x-zond-public: true (auth check suppressed at the spec level)";
|
|
71
|
-
}
|
|
72
|
-
return `x-zond-skip listed "${checkId}" at the spec level`;
|
|
73
|
-
}
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ARV-56: single producer of `recommended_action` across every source
|
|
3
|
-
* that emits findings — run results (`db diagnose`), spec lint
|
|
4
|
-
* (`lint-spec.Issue`), probe-security findings, probe-mass-assignment
|
|
5
|
-
* verdicts, and conformance checks (`zond checks run`).
|
|
6
|
-
*
|
|
7
|
-
* Before this module, "what action does an agent take?" was answered in
|
|
8
|
-
* five different places:
|
|
9
|
-
* - `recommendedAction` / `recommendedActionForGenerated` (db-analysis)
|
|
10
|
-
* - `recommendForCheck` (checks runner)
|
|
11
|
-
* - `stampRecommendedAction` (mass-assignment) + per-severity policy
|
|
12
|
-
* - `stampAction` (security) + per-severity policy
|
|
13
|
-
* - inline `"fix_spec"` literal (lint)
|
|
14
|
-
*
|
|
15
|
-
* Each accumulated branches independently — ARV-11 added per-check
|
|
16
|
-
* actions, ARV-42 added a generator-aware override, TASK-294 layered
|
|
17
|
-
* probe severity mappings, the env-issue detector then overrode the
|
|
18
|
-
* result of all of them. By the time ARV-56 landed, the same logical
|
|
19
|
-
* question — "given this finding, what should the agent do?" — had four
|
|
20
|
-
* different entry points with subtle drift.
|
|
21
|
-
*
|
|
22
|
-
* This file owns that question. Every producer hands the classifier a
|
|
23
|
-
* `ClassifierContext` (a frozen description of the finding) and gets
|
|
24
|
-
* back a `RecommendedAction`. The classifier is **pure** — no I/O, no
|
|
25
|
-
* config reads, no side effects — so it can be table-tested.
|
|
26
|
-
*
|
|
27
|
-
* The thin wrappers (`recommendedAction`, `recommendedActionForGenerated`,
|
|
28
|
-
* `recommendForCheck`, `stampRecommendedAction`, `stampAction`) now
|
|
29
|
-
* delegate here instead of carrying their own switches. Removing a
|
|
30
|
-
* branch means editing one switch in this file.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import type { RecommendedAction } from "../diagnostics/failure-hints.ts";
|
|
34
|
-
import type { RunKind } from "../runner/run-kind.ts";
|
|
35
|
-
|
|
36
|
-
export type FindingClass =
|
|
37
|
-
// db-analysis run-result rows ────────────────────────────────────
|
|
38
|
-
| "test:network_error"
|
|
39
|
-
| "test:api_error"
|
|
40
|
-
| "test:assertion_failed"
|
|
41
|
-
|
|
42
|
-
// checks/<id> ────────────────────────────────────────────────────
|
|
43
|
-
| "check:status_code_conformance"
|
|
44
|
-
| "check:content_type_conformance"
|
|
45
|
-
| "check:response_headers_conformance"
|
|
46
|
-
| "check:response_schema_conformance"
|
|
47
|
-
| "check:not_a_server_error"
|
|
48
|
-
| "check:unsupported_method"
|
|
49
|
-
| "check:positive_data_acceptance"
|
|
50
|
-
| "check:use_after_free"
|
|
51
|
-
| "check:ensure_resource_availability"
|
|
52
|
-
| "check:negative_data_rejection"
|
|
53
|
-
| "check:missing_required_header"
|
|
54
|
-
| "check:ignored_auth"
|
|
55
|
-
| "check:cross_call_references"
|
|
56
|
-
| "check:idempotency_replay"
|
|
57
|
-
| "check:pagination_invariants"
|
|
58
|
-
| "check:lifecycle_transitions"
|
|
59
|
-
| "check:open_cors_on_sensitive"
|
|
60
|
-
| "check:cursor_boundary_fuzzing"
|
|
61
|
-
| "check:rate_limit_headers_absent"
|
|
62
|
-
| "check:network_error"
|
|
63
|
-
|
|
64
|
-
// probe verdicts (severity already classified upstream) ───────────
|
|
65
|
-
| "probe:mass_assignment"
|
|
66
|
-
| "probe:security"
|
|
67
|
-
|
|
68
|
-
// lint-spec ───────────────────────────────────────────────────────
|
|
69
|
-
| "lint:issue";
|
|
70
|
-
|
|
71
|
-
/** Optional severity hint — probe families surface a 5-level enum;
|
|
72
|
-
* here we only care about the buckets the action mapping branches on. */
|
|
73
|
-
export type FindingSeverity =
|
|
74
|
-
| "high"
|
|
75
|
-
| "medium"
|
|
76
|
-
| "inconclusive-5xx"
|
|
77
|
-
| "inconclusive-baseline"
|
|
78
|
-
| "low"
|
|
79
|
-
| "ok"
|
|
80
|
-
| "skipped";
|
|
81
|
-
|
|
82
|
-
export interface ClassifierContext {
|
|
83
|
-
finding_class: FindingClass;
|
|
84
|
-
/** HTTP status code observed for the finding, when applicable.
|
|
85
|
-
* null/undefined means "unknown / not relevant". */
|
|
86
|
-
status?: number | null;
|
|
87
|
-
/** Severity already assigned by the probe layer (mass-assignment /
|
|
88
|
-
* security). The classifier reads it instead of re-deriving from
|
|
89
|
-
* status — severity captures multi-step reasoning (baseline + attack
|
|
90
|
-
* + follow-up GET) that status alone can't recover. */
|
|
91
|
-
severity?: FindingSeverity;
|
|
92
|
-
/** Run kind for the finding's parent run — currently informational; the
|
|
93
|
-
* classifier may consult it in the future to e.g. downgrade probe-run
|
|
94
|
-
* signal. Captured now so the contract is forward-compatible. */
|
|
95
|
-
run_kind?: RunKind;
|
|
96
|
-
/** Provenance of the failing test (only relevant for test:* classes). */
|
|
97
|
-
provenance?: { type?: string; generator?: string } | null;
|
|
98
|
-
/** Suite path used to detect generator-emitted tests. */
|
|
99
|
-
suite_path?: string | null;
|
|
100
|
-
/** When the env-issue detector flagged the suite, it overrides the
|
|
101
|
-
* classifier's default. Producers set this *after* clustering. */
|
|
102
|
-
baseline_status?: number | null;
|
|
103
|
-
/** ARV-103 (F8): true when at least one assertion on the failing step
|
|
104
|
-
* has `kind: "schema"`. Schema violations are real contract bugs — per
|
|
105
|
-
* zond/SKILL.md L376-377 they should route to report_backend_bug, not
|
|
106
|
-
* fix_test_logic. Producers (db-analysis) set this after walking the
|
|
107
|
-
* step's assertions array. */
|
|
108
|
-
schema_violation?: boolean;
|
|
109
|
-
/** ARV-324: true when `prepare-fixtures`/`discover` already confirmed
|
|
110
|
-
* this exact operation returns a client error / empty list while
|
|
111
|
-
* hunting for fixture values (`.fixture-gaps.yaml`) — e.g. an empty
|
|
112
|
-
* test account. Same shape of problem `probe:mass_assignment`'s
|
|
113
|
-
* `inconclusive-baseline` branch already solves below: a finding
|
|
114
|
-
* caused by our own missing test data isn't a backend bug. */
|
|
115
|
-
unresolved_fixture?: boolean;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Decide the action for a finding. Returns `undefined` only for finding
|
|
120
|
-
* classes that intentionally don't carry an action (e.g. severity:low
|
|
121
|
-
* security findings — the producer should leave the field unset rather
|
|
122
|
-
* than coerce a value).
|
|
123
|
-
*/
|
|
124
|
-
export function classify(ctx: ClassifierContext): RecommendedAction | undefined {
|
|
125
|
-
switch (ctx.finding_class) {
|
|
126
|
-
// ── Run-result rows (db diagnose) ───────────────────────────────
|
|
127
|
-
case "test:api_error":
|
|
128
|
-
return "report_backend_bug";
|
|
129
|
-
|
|
130
|
-
case "test:network_error":
|
|
131
|
-
if (ctx.status === 401 || ctx.status === 403) return "fix_auth_config";
|
|
132
|
-
return "fix_network_config";
|
|
133
|
-
|
|
134
|
-
case "test:assertion_failed": {
|
|
135
|
-
// 401/403 → auth always wins.
|
|
136
|
-
if (ctx.status === 401 || ctx.status === 403) return "fix_auth_config";
|
|
137
|
-
// ARV-103 (F8): schema-kind assertions are real contract bugs (the
|
|
138
|
-
// server returned a body that violates its own spec). Route to
|
|
139
|
-
// report_backend_bug — same bucket as 5xx. Skill (zond/SKILL.md
|
|
140
|
-
// L376-377) explicitly says "treat them like 5xx, do not edit the
|
|
141
|
-
// expectation away". Wins over the generator-aware override below
|
|
142
|
-
// because regenerate_suite would silently re-emit the same broken
|
|
143
|
-
// assertion against the same broken response.
|
|
144
|
-
if (ctx.schema_violation) return "report_backend_bug";
|
|
145
|
-
// ARV-42: generator-emitted suites get a different default — editing
|
|
146
|
-
// the YAML gets clobbered on the next `zond audit`.
|
|
147
|
-
const generated = isGeneratedSource(ctx.provenance, ctx.suite_path);
|
|
148
|
-
if (generated) {
|
|
149
|
-
if (ctx.status === 404) return "fix_fixture";
|
|
150
|
-
if (ctx.status === 400 || ctx.status === 422) return "regenerate_suite";
|
|
151
|
-
}
|
|
152
|
-
return "fix_test_logic";
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ── checks/<id> ────────────────────────────────────────────────
|
|
156
|
-
case "check:status_code_conformance":
|
|
157
|
-
case "check:content_type_conformance":
|
|
158
|
-
case "check:response_headers_conformance":
|
|
159
|
-
case "check:response_schema_conformance":
|
|
160
|
-
return "fix_spec";
|
|
161
|
-
|
|
162
|
-
case "check:not_a_server_error":
|
|
163
|
-
// A 5xx on this operation is worth flagging regardless of fixture
|
|
164
|
-
// state — even a synthetic/garbage id shouldn't crash the server.
|
|
165
|
-
return "report_backend_bug";
|
|
166
|
-
|
|
167
|
-
case "check:positive_data_acceptance":
|
|
168
|
-
// ARV-324: a known unresolved fixture gap (garbage path-id → 400)
|
|
169
|
-
// is our own missing test data, not a backend or spec defect.
|
|
170
|
-
if (ctx.unresolved_fixture) return "fix_fixture";
|
|
171
|
-
// ARV-341: otherwise this check fires ONLY when the server rejects a
|
|
172
|
-
// body *we* generated as valid (400/422). The server is
|
|
173
|
-
// authoritative — our generated body or the spec was wrong, not the
|
|
174
|
-
// backend. Route to fix_spec, not report_backend_bug, so 100s of
|
|
175
|
-
// generator/spec-drift cases don't spam the API owner with false
|
|
176
|
-
// bug reports.
|
|
177
|
-
return "fix_spec";
|
|
178
|
-
|
|
179
|
-
case "check:unsupported_method":
|
|
180
|
-
case "check:use_after_free":
|
|
181
|
-
case "check:ensure_resource_availability":
|
|
182
|
-
case "check:cross_call_references":
|
|
183
|
-
case "check:idempotency_replay":
|
|
184
|
-
case "check:pagination_invariants":
|
|
185
|
-
case "check:lifecycle_transitions":
|
|
186
|
-
case "check:cursor_boundary_fuzzing":
|
|
187
|
-
// ARV-324: same treatment as probe:mass_assignment's
|
|
188
|
-
// inconclusive-baseline branch below — a finding on an operation
|
|
189
|
-
// we already know is an unresolved fixture gap isn't new evidence
|
|
190
|
-
// of a backend defect.
|
|
191
|
-
if (ctx.unresolved_fixture) return "fix_fixture";
|
|
192
|
-
return "report_backend_bug";
|
|
193
|
-
|
|
194
|
-
case "check:negative_data_rejection":
|
|
195
|
-
return "tighten_validation";
|
|
196
|
-
|
|
197
|
-
case "check:missing_required_header":
|
|
198
|
-
return "add_required_header";
|
|
199
|
-
|
|
200
|
-
case "check:ignored_auth":
|
|
201
|
-
case "check:open_cors_on_sensitive":
|
|
202
|
-
return "fix_auth_config";
|
|
203
|
-
|
|
204
|
-
case "check:rate_limit_headers_absent":
|
|
205
|
-
// ARV-304: this is a server-side hygiene gap (RFC-9239 /
|
|
206
|
-
// OWASP-API-04 expect rate-limit metadata on writes), not a
|
|
207
|
-
// caller-side auth problem. Agents triaging on `fix_auth_config`
|
|
208
|
-
// would chase the wrong fix; surface it as a backend-team task.
|
|
209
|
-
return "report_backend_bug";
|
|
210
|
-
|
|
211
|
-
case "check:network_error":
|
|
212
|
-
if (ctx.status === 401 || ctx.status === 403) return "fix_auth_config";
|
|
213
|
-
return "fix_network_config";
|
|
214
|
-
|
|
215
|
-
// ── Probe verdicts ─────────────────────────────────────────────
|
|
216
|
-
case "probe:mass_assignment":
|
|
217
|
-
switch (ctx.severity) {
|
|
218
|
-
case "high":
|
|
219
|
-
case "medium":
|
|
220
|
-
case "inconclusive-5xx":
|
|
221
|
-
return "report_backend_bug";
|
|
222
|
-
case "inconclusive-baseline":
|
|
223
|
-
return "fix_fixture";
|
|
224
|
-
default:
|
|
225
|
-
return undefined; // low / ok / skipped: no action
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
case "probe:security":
|
|
229
|
-
// TASK-294 policy: high/low both routed to backend-bug. Low is
|
|
230
|
-
// "server returned 2xx without echoing the payload" — still a
|
|
231
|
-
// surprising acceptance the backend should review.
|
|
232
|
-
if (ctx.severity === "high" || ctx.severity === "low") {
|
|
233
|
-
return "report_backend_bug";
|
|
234
|
-
}
|
|
235
|
-
return undefined;
|
|
236
|
-
|
|
237
|
-
// ── Lint findings ─────────────────────────────────────────────
|
|
238
|
-
case "lint:issue":
|
|
239
|
-
return "fix_spec";
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function isGeneratedSource(
|
|
244
|
-
provenance: ClassifierContext["provenance"],
|
|
245
|
-
suite_path: ClassifierContext["suite_path"],
|
|
246
|
-
): boolean {
|
|
247
|
-
if (provenance?.type === "openapi-generated") return true;
|
|
248
|
-
if (provenance?.generator && provenance.generator.toLowerCase().includes("zond")) return true;
|
|
249
|
-
if (typeof suite_path === "string" && /(^|\/)apis\/[^/]+\/tests\//.test(suite_path)) return true;
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { findWorkspaceRoot } from "../workspace/root.ts";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* TASK-290: current-API resolution chain.
|
|
7
|
-
* - Global --api flag is mirrored into ZOND_API_GLOBAL by program.ts preAction.
|
|
8
|
-
* - Users can also export ZOND_API in their shell.
|
|
9
|
-
* - Persisted choice lives in `.zond/current-api` (was `.zond-current`).
|
|
10
|
-
*/
|
|
11
|
-
const FILENAME = ".zond/current-api";
|
|
12
|
-
|
|
13
|
-
export function currentApiPath(cwd?: string): string {
|
|
14
|
-
const base = cwd ?? findWorkspaceRoot().root;
|
|
15
|
-
return join(base, FILENAME);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Returns the active API name. Resolution order:
|
|
20
|
-
* 1. ZOND_API_GLOBAL (mirrored from `zond --api <name> ...`)
|
|
21
|
-
* 2. ZOND_API (user env)
|
|
22
|
-
* 3. `.zond/current-api` file (set by `zond use <name>`)
|
|
23
|
-
*/
|
|
24
|
-
export function readCurrentApi(cwd?: string): string | null {
|
|
25
|
-
const fromGlobalFlag = process.env.ZOND_API_GLOBAL?.trim();
|
|
26
|
-
if (fromGlobalFlag) return fromGlobalFlag;
|
|
27
|
-
const fromEnv = process.env.ZOND_API?.trim();
|
|
28
|
-
if (fromEnv) return fromEnv;
|
|
29
|
-
const path = currentApiPath(cwd);
|
|
30
|
-
if (!existsSync(path)) return null;
|
|
31
|
-
const raw = readFileSync(path, "utf-8").trim();
|
|
32
|
-
return raw.length > 0 ? raw : null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Writes the API collection name to `.zond/current-api`. Creates the dir if needed. */
|
|
36
|
-
export function writeCurrentApi(name: string, cwd?: string): string {
|
|
37
|
-
const trimmed = name.trim();
|
|
38
|
-
if (!trimmed) throw new Error("API name cannot be empty");
|
|
39
|
-
const path = currentApiPath(cwd);
|
|
40
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
41
|
-
writeFileSync(path, trimmed + "\n", "utf-8");
|
|
42
|
-
return path;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Deletes `.zond/current-api`. Returns true when a file was removed, false when it did not exist. */
|
|
46
|
-
export function clearCurrentApi(cwd?: string): boolean {
|
|
47
|
-
const path = currentApiPath(cwd);
|
|
48
|
-
if (!existsSync(path)) return false;
|
|
49
|
-
unlinkSync(path);
|
|
50
|
-
return true;
|
|
51
|
-
}
|