@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.
Files changed (261) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +22 -21
  3. package/bin/zond.mjs +23 -0
  4. package/package.json +13 -16
  5. package/scripts/npm/postinstall.mjs +76 -0
  6. package/src/CLAUDE.md +0 -112
  7. package/src/bun-types.d.ts +0 -5
  8. package/src/cli/argv.ts +0 -122
  9. package/src/cli/commands/add-api.ts +0 -146
  10. package/src/cli/commands/api/annotate/idempotency.ts +0 -59
  11. package/src/cli/commands/api/annotate/index.ts +0 -880
  12. package/src/cli/commands/api/annotate/lifecycle.ts +0 -74
  13. package/src/cli/commands/api/annotate/overlay.ts +0 -206
  14. package/src/cli/commands/api/annotate/pagination.ts +0 -64
  15. package/src/cli/commands/api/annotate/prompts.ts +0 -220
  16. package/src/cli/commands/api/annotate/readback.ts +0 -58
  17. package/src/cli/commands/api/annotate/resources.ts +0 -91
  18. package/src/cli/commands/api/annotate/seed-bodies.ts +0 -61
  19. package/src/cli/commands/audit.ts +0 -786
  20. package/src/cli/commands/catalog.ts +0 -97
  21. package/src/cli/commands/check.ts +0 -361
  22. package/src/cli/commands/checks.ts +0 -1072
  23. package/src/cli/commands/ci-init.ts +0 -223
  24. package/src/cli/commands/clean.ts +0 -212
  25. package/src/cli/commands/cleanup.ts +0 -236
  26. package/src/cli/commands/completions.ts +0 -192
  27. package/src/cli/commands/coverage.ts +0 -872
  28. package/src/cli/commands/db.ts +0 -611
  29. package/src/cli/commands/describe.ts +0 -120
  30. package/src/cli/commands/discover.ts +0 -1356
  31. package/src/cli/commands/doctor.ts +0 -661
  32. package/src/cli/commands/fixtures.ts +0 -402
  33. package/src/cli/commands/generate.ts +0 -577
  34. package/src/cli/commands/init/agents-md.ts +0 -61
  35. package/src/cli/commands/init/bootstrap.ts +0 -111
  36. package/src/cli/commands/init/index.ts +0 -244
  37. package/src/cli/commands/init/skills.ts +0 -141
  38. package/src/cli/commands/init/templates/agents.md +0 -86
  39. package/src/cli/commands/init/templates/markdown.d.ts +0 -4
  40. package/src/cli/commands/init/templates/skills/warm-up-target.md +0 -122
  41. package/src/cli/commands/init/templates/skills/zond-checks.md +0 -621
  42. package/src/cli/commands/init/templates/skills/zond-seed.md +0 -114
  43. package/src/cli/commands/init/templates/skills/zond-triage.md +0 -272
  44. package/src/cli/commands/init/templates/skills/zond.md +0 -861
  45. package/src/cli/commands/init/templates/zond-config.yml +0 -14
  46. package/src/cli/commands/prepare-fixtures.ts +0 -97
  47. package/src/cli/commands/probe/_seed-bodies.ts +0 -52
  48. package/src/cli/commands/probe/mass-assignment.ts +0 -594
  49. package/src/cli/commands/probe/security.ts +0 -537
  50. package/src/cli/commands/probe/static.ts +0 -255
  51. package/src/cli/commands/probe/webhooks.ts +0 -163
  52. package/src/cli/commands/probe.ts +0 -535
  53. package/src/cli/commands/reference.ts +0 -87
  54. package/src/cli/commands/refresh-api.ts +0 -227
  55. package/src/cli/commands/remove-api.ts +0 -150
  56. package/src/cli/commands/report-bundle.ts +0 -310
  57. package/src/cli/commands/report.ts +0 -241
  58. package/src/cli/commands/request.ts +0 -548
  59. package/src/cli/commands/run.ts +0 -1142
  60. package/src/cli/commands/schema-from-runs.ts +0 -128
  61. package/src/cli/commands/secrets.ts +0 -133
  62. package/src/cli/commands/session.ts +0 -244
  63. package/src/cli/commands/use.ts +0 -74
  64. package/src/cli/index.ts +0 -55
  65. package/src/cli/json-envelope.ts +0 -108
  66. package/src/cli/json-schemas.ts +0 -314
  67. package/src/cli/output.ts +0 -40
  68. package/src/cli/program.ts +0 -219
  69. package/src/cli/resolve.ts +0 -105
  70. package/src/cli/runtime.ts +0 -7
  71. package/src/cli/safe-live.ts +0 -24
  72. package/src/cli/status-filter.ts +0 -114
  73. package/src/cli/util/api-context.ts +0 -85
  74. package/src/cli/version.ts +0 -8
  75. package/src/core/audit/persist.ts +0 -183
  76. package/src/core/checks/budget.ts +0 -59
  77. package/src/core/checks/checks/_crud-helpers.ts +0 -133
  78. package/src/core/checks/checks/_negative_mutator.ts +0 -133
  79. package/src/core/checks/checks/_readback-helpers.ts +0 -133
  80. package/src/core/checks/checks/content_type_conformance.ts +0 -39
  81. package/src/core/checks/checks/cross_call_references.ts +0 -147
  82. package/src/core/checks/checks/cursor_boundary_fuzzing.ts +0 -219
  83. package/src/core/checks/checks/ensure_resource_availability.ts +0 -62
  84. package/src/core/checks/checks/idempotency_replay.ts +0 -242
  85. package/src/core/checks/checks/ignored_auth.ts +0 -254
  86. package/src/core/checks/checks/index.ts +0 -68
  87. package/src/core/checks/checks/lifecycle_transitions.ts +0 -416
  88. package/src/core/checks/checks/missing_required_header.ts +0 -40
  89. package/src/core/checks/checks/negative_data_rejection.ts +0 -148
  90. package/src/core/checks/checks/not_a_server_error.ts +0 -35
  91. package/src/core/checks/checks/open_cors_on_sensitive.ts +0 -160
  92. package/src/core/checks/checks/pagination_invariants.ts +0 -419
  93. package/src/core/checks/checks/positive_data_acceptance.ts +0 -33
  94. package/src/core/checks/checks/rate_limit_headers_absent.ts +0 -77
  95. package/src/core/checks/checks/response_headers_conformance.ts +0 -74
  96. package/src/core/checks/checks/response_schema_conformance.ts +0 -30
  97. package/src/core/checks/checks/status_code_conformance.ts +0 -132
  98. package/src/core/checks/checks/unsupported_method.ts +0 -63
  99. package/src/core/checks/checks/use_after_free.ts +0 -78
  100. package/src/core/checks/index.ts +0 -30
  101. package/src/core/checks/mode.ts +0 -82
  102. package/src/core/checks/recommended-action.ts +0 -68
  103. package/src/core/checks/registry.ts +0 -78
  104. package/src/core/checks/runner.ts +0 -1461
  105. package/src/core/checks/sarif.ts +0 -230
  106. package/src/core/checks/spec-findings.ts +0 -308
  107. package/src/core/checks/stateful.ts +0 -121
  108. package/src/core/checks/types.ts +0 -305
  109. package/src/core/checks/zond-extensions.ts +0 -73
  110. package/src/core/classifier/recommended-action.ts +0 -251
  111. package/src/core/context/current.ts +0 -51
  112. package/src/core/context/session.ts +0 -78
  113. package/src/core/coverage/loader.ts +0 -216
  114. package/src/core/coverage/reasons.ts +0 -300
  115. package/src/core/diagnostics/db-analysis.ts +0 -666
  116. package/src/core/diagnostics/failure-class.ts +0 -140
  117. package/src/core/diagnostics/failure-hints.ts +0 -112
  118. package/src/core/diagnostics/spec-pointer.ts +0 -99
  119. package/src/core/diagnostics/suggested-fixes.ts +0 -155
  120. package/src/core/exporter/case-study/index.ts +0 -270
  121. package/src/core/exporter/curl.ts +0 -40
  122. package/src/core/exporter/exporter.ts +0 -48
  123. package/src/core/exporter/html-report/escape.ts +0 -24
  124. package/src/core/exporter/html-report/index.ts +0 -479
  125. package/src/core/exporter/html-report/script.ts +0 -100
  126. package/src/core/exporter/html-report/styles.ts +0 -408
  127. package/src/core/generator/catalog-builder.ts +0 -179
  128. package/src/core/generator/chunker.ts +0 -78
  129. package/src/core/generator/coverage-phase.ts +0 -0
  130. package/src/core/generator/coverage-scanner.ts +0 -87
  131. package/src/core/generator/data-factory.ts +0 -759
  132. package/src/core/generator/describe.ts +0 -302
  133. package/src/core/generator/endpoint-warnings.ts +0 -52
  134. package/src/core/generator/fixtures-builder.ts +0 -332
  135. package/src/core/generator/index.ts +0 -14
  136. package/src/core/generator/openapi-reader.ts +0 -297
  137. package/src/core/generator/path-param-disambig.ts +0 -140
  138. package/src/core/generator/resources-builder.ts +0 -898
  139. package/src/core/generator/schema-utils.ts +0 -112
  140. package/src/core/generator/serializer.ts +0 -343
  141. package/src/core/generator/suite-generator.ts +0 -1301
  142. package/src/core/generator/types.ts +0 -63
  143. package/src/core/identity/identity-file.ts +0 -0
  144. package/src/core/lint/affects.ts +0 -28
  145. package/src/core/lint/config.ts +0 -96
  146. package/src/core/lint/format.ts +0 -42
  147. package/src/core/lint/index.ts +0 -94
  148. package/src/core/lint/reporter.ts +0 -128
  149. package/src/core/lint/rules/consistency.ts +0 -158
  150. package/src/core/lint/rules/heuristics.ts +0 -97
  151. package/src/core/lint/rules/strictness.ts +0 -109
  152. package/src/core/lint/types.ts +0 -96
  153. package/src/core/lint/walker.ts +0 -248
  154. package/src/core/meta/meta-store.ts +0 -11
  155. package/src/core/output/README.md +0 -73
  156. package/src/core/output/index.ts +0 -13
  157. package/src/core/output/run.ts +0 -91
  158. package/src/core/output/types.ts +0 -122
  159. package/src/core/parser/dynamic-values.ts +0 -160
  160. package/src/core/parser/env-interpolation.ts +0 -104
  161. package/src/core/parser/filter.ts +0 -97
  162. package/src/core/parser/schema.ts +0 -359
  163. package/src/core/parser/types.ts +0 -117
  164. package/src/core/parser/variables.ts +0 -0
  165. package/src/core/parser/yaml-parser.ts +0 -181
  166. package/src/core/probe/bootstrap.ts +0 -34
  167. package/src/core/probe/dry-run-envelope.ts +0 -61
  168. package/src/core/probe/mass-assignment/classify.ts +0 -175
  169. package/src/core/probe/mass-assignment/cleanup.ts +0 -52
  170. package/src/core/probe/mass-assignment/digest.ts +0 -114
  171. package/src/core/probe/mass-assignment/orchestrator.ts +0 -459
  172. package/src/core/probe/mass-assignment/regression.ts +0 -141
  173. package/src/core/probe/mass-assignment/suspects.ts +0 -92
  174. package/src/core/probe/mass-assignment/types.ts +0 -135
  175. package/src/core/probe/mass-assignment-probe-class.ts +0 -198
  176. package/src/core/probe/mass-assignment-probe.ts +0 -27
  177. package/src/core/probe/mass-assignment-template.ts +0 -240
  178. package/src/core/probe/method-probe.ts +0 -164
  179. package/src/core/probe/method-shared.ts +0 -69
  180. package/src/core/probe/negative-probe.ts +0 -691
  181. package/src/core/probe/orphan-tracker.ts +0 -188
  182. package/src/core/probe/path-discovery.ts +0 -439
  183. package/src/core/probe/probe-harness.ts +0 -119
  184. package/src/core/probe/registry.ts +0 -89
  185. package/src/core/probe/runner.ts +0 -136
  186. package/src/core/probe/security/baseline.ts +0 -174
  187. package/src/core/probe/security/classify.ts +0 -341
  188. package/src/core/probe/security/cleanup.ts +0 -125
  189. package/src/core/probe/security/detectors.ts +0 -71
  190. package/src/core/probe/security/digest.ts +0 -104
  191. package/src/core/probe/security/orchestrator.ts +0 -398
  192. package/src/core/probe/security/regression.ts +0 -103
  193. package/src/core/probe/security/types.ts +0 -151
  194. package/src/core/probe/security-probe-class.ts +0 -207
  195. package/src/core/probe/security-probe.ts +0 -32
  196. package/src/core/probe/shared.ts +0 -531
  197. package/src/core/probe/static-probe-class.ts +0 -125
  198. package/src/core/probe/types.ts +0 -165
  199. package/src/core/probe/verdict-aggregator.ts +0 -33
  200. package/src/core/probe/webhooks-probe.ts +0 -282
  201. package/src/core/reporter/console.ts +0 -240
  202. package/src/core/reporter/index.ts +0 -22
  203. package/src/core/reporter/json.ts +0 -22
  204. package/src/core/reporter/junit.ts +0 -93
  205. package/src/core/reporter/ndjson.ts +0 -37
  206. package/src/core/reporter/types.ts +0 -15
  207. package/src/core/runner/assertions.ts +0 -383
  208. package/src/core/runner/async-pool.ts +0 -108
  209. package/src/core/runner/auth-path.ts +0 -8
  210. package/src/core/runner/ci-context.ts +0 -72
  211. package/src/core/runner/executor.ts +0 -652
  212. package/src/core/runner/expr-eval.ts +0 -41
  213. package/src/core/runner/form-encode.ts +0 -41
  214. package/src/core/runner/http-client.ts +0 -224
  215. package/src/core/runner/learn-drift.ts +0 -293
  216. package/src/core/runner/preflight-vars.ts +0 -153
  217. package/src/core/runner/progress-tracker.ts +0 -73
  218. package/src/core/runner/rate-limiter.ts +0 -185
  219. package/src/core/runner/run-kind.ts +0 -45
  220. package/src/core/runner/schema-validator.ts +0 -308
  221. package/src/core/runner/send-request.ts +0 -232
  222. package/src/core/runner/transforms.ts +0 -65
  223. package/src/core/runner/types.ts +0 -94
  224. package/src/core/secrets/registry.ts +0 -164
  225. package/src/core/secrets/secrets-file.ts +0 -115
  226. package/src/core/selectors/operation-filter.ts +0 -144
  227. package/src/core/setup-api.ts +0 -590
  228. package/src/core/severity/category.ts +0 -94
  229. package/src/core/severity/index.ts +0 -58
  230. package/src/core/spec/infer-schema.ts +0 -102
  231. package/src/core/spec/layers.ts +0 -154
  232. package/src/core/spec/merge-specs.ts +0 -156
  233. package/src/core/spec/schema-from-runs.ts +0 -117
  234. package/src/core/spec/schema-overlay.ts +0 -130
  235. package/src/core/util/ajv.ts +0 -13
  236. package/src/core/util/format-eta.ts +0 -21
  237. package/src/core/util/headers.ts +0 -9
  238. package/src/core/util/url.ts +0 -24
  239. package/src/core/utils.ts +0 -13
  240. package/src/core/workspace/config.ts +0 -129
  241. package/src/core/workspace/fixture-gap-report.ts +0 -84
  242. package/src/core/workspace/fixture-gaps.ts +0 -71
  243. package/src/core/workspace/manifest.ts +0 -283
  244. package/src/core/workspace/output-rotation.ts +0 -62
  245. package/src/core/workspace/root.ts +0 -96
  246. package/src/core/workspace/triage-path.ts +0 -87
  247. package/src/db/lint-runs.ts +0 -47
  248. package/src/db/migrate.ts +0 -128
  249. package/src/db/migrations/0001_run_kind.sql +0 -25
  250. package/src/db/migrations/0002_run_kind_request.sql +0 -59
  251. package/src/db/migrations/sql.d.ts +0 -4
  252. package/src/db/queries/collections.ts +0 -133
  253. package/src/db/queries/coverage.ts +0 -9
  254. package/src/db/queries/dashboard.ts +0 -59
  255. package/src/db/queries/results.ts +0 -216
  256. package/src/db/queries/runs.ts +0 -289
  257. package/src/db/queries/sessions.ts +0 -42
  258. package/src/db/queries/settings.ts +0 -28
  259. package/src/db/queries/types.ts +0 -172
  260. package/src/db/queries.ts +0 -75
  261. package/src/db/schema.ts +0 -298
@@ -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
- }