@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,621 +0,0 @@
1
- ---
2
- name: zond-checks
3
- description: |
4
- Schemathesis-style depth checks for an API — conformance + security
5
- probes that go beyond YAML smoke tests. Use when the user asks for:
6
- "deep audit", "find spec drift", "test edge cases", "boundary value
7
- coverage", "find security bugs", "broken auth check", "use-after-free",
8
- "SARIF for code scanning", "GitHub Code Scanning report", "stateful
9
- invariants", "cross-call drift", "idempotency replay", "pagination
10
- consistency", "lifecycle state machine". For a full generated test
11
- pipeline + scenarios + audit chain, hand off to `zond`. For triage
12
- of a failing run, use `zond-triage`.
13
- allowed-tools: [Read, Bash(zond *), Bash(bunx zond *), Bash(jq *), Bash(sqlite3 *)]
14
- ---
15
-
16
- # zond-checks — Depth checks (conformance + security)
17
-
18
- CLI-only skill. Use *after* a YAML smoke run passes — depth checks
19
- exercise contract drift, malicious input rejection, broken auth, and
20
- soft-deleted resource leaks against a live API.
21
-
22
- The catalog is fixed and self-describing — list it before running so
23
- the user sees what they'll get:
24
-
25
- ```bash
26
- zond checks list # registered checks (id, severity, expected)
27
- zond checks list --json # same, machine-readable
28
- ```
29
-
30
- ## When to use
31
-
32
- | User asks | Run |
33
- |---|---|
34
- | "deep audit", "find edge cases" | `zond checks run --api <name>` |
35
- | "boundary value coverage" | `... --phase coverage` |
36
- | "find security bugs", "broken auth" | `... --check ignored_auth,use_after_free,ensure_resource_availability` |
37
- | "is GET returning what POST accepted?", "cross-call drift" | `... --check cross_call_references` (m-20) |
38
- | "does the API honor Idempotency-Key?", "two-POST replay" | `... --check idempotency_replay` (m-20) |
39
- | "are paginated lists consistent?", "duplicates across cursor pages" | `... --check pagination_invariants` (m-20) |
40
- | "does cancel/archive land the resource in the declared state?", "state-machine" | `... --check lifecycle_transitions` (m-20) |
41
- | "do captured webhook events match spec.webhooks shape?" | `zond probe webhooks --event-log events.jsonl` (m-20) — recipe: docs/recipes/webhook-receiver.md |
42
- | "schemathesis-style strict mode" | `... --strict-405 --strict-401` (m-18) |
43
- | "SARIF for GitHub Code Scanning" | `... --report sarif --output zond.sarif` |
44
- | "stream findings to a pipeline" | `... --report ndjson \| jq -c '.'` |
45
- | "scan large API faster" | `... --workers auto` (or `--workers 8`) |
46
-
47
- ### Strict-mode флаги (m-18)
48
-
49
- - **`--strict-405`** — `unsupported_method` принимает только 405 (вместо
50
- pragmatic 401/403/404/405). Mirror schemathesis V4 default. Включай
51
- когда target — backend, где политика «undeclared method → 405»
52
- обязательна (RFC 9110 compliance).
53
- - **`--strict-401`** — `ignored_auth` no-auth/bogus_auth требует ровно 401
54
- (вместо любого 4xx). Включай для проверки точности auth-rejection policy.
55
-
56
- Pragmatic режим (default) — реалистичный для production API'ев где gateway
57
- часто возвращает 404 на undeclared method или 403 на missing auth.
58
-
59
- ## Iron rules
60
-
61
- - **Safe by default (ARV-299).** `checks run` and the mutating probes
62
- (`probe mass-assignment`, `probe security`) default to `--safe`: no
63
- destructive traffic. For `checks run` that means the stateful CRUD
64
- create-chains (`ensure_resource_availability`, `use_after_free`) self-skip
65
- — read-only checks (conformance, pagination, observation-mode lifecycle)
66
- still run. For probes it means plan-only (no live attack payloads). Pass
67
- `--live` to opt into mutating traffic — **only against a throwaway/sandbox
68
- account.** Same vocabulary as `zond audit --safe/--live`. `probe static`
69
- only generates suites, so it's always safe (`--live` is a no-op there).
70
- - **NEVER hand-roll these checks in YAML.** The catalog encodes
71
- schemathesis V4 semantics 1-to-1 — replicating them in YAML drifts
72
- silently. `zond checks run` is the single source of truth.
73
- - **`--check stateful` = state-machine set only (ARV-325):**
74
- cross_call_references, idempotency_replay, pagination_invariants,
75
- lifecycle_transitions, use_after_free, ensure_resource_availability,
76
- cursor_boundary_fuzzing. `ignored_auth` / `open_cors_on_sensitive` are
77
- NOT included — run them by explicit id when you want the security pair.
78
- - **NEVER run `--check stateful` without prior `api annotate` review.**
79
- m-20 stateful checks (cross_call_references, idempotency_replay,
80
- pagination_invariants, lifecycle_transitions) read `.api-resources.local.yaml`
81
- for per-API quirks (custom pagination param, non-standard lifecycle
82
- field, write-only ignore_fields). Defaults catch the obvious; running
83
- on defaults alone misses API-specific drift. See **Phase pre-0** below.
84
- - **NEVER ignore `--bootstrap-cleanup-failed`.** Stateful security
85
- checks (`ignored_auth`, `use_after_free`, `ensure_resource_availability`)
86
- produce false positives on stale data. If the previous run's
87
- cleanup failed, pass the flag — they skip with a warning instead.
88
- - **Auth headers are auto-derived from `--api <name>` `.env.yaml`**
89
- (`auth_token` → `Authorization: Bearer …`, `api_key` → `X-API-Key`).
90
- Add explicit ones with `--auth-header 'Name: value'` (repeatable).
91
- - **Prewarm a fresh workspace before `zond checks run`.** Right after
92
- `zond add api <name>`, `.env.yaml` only has the auth skeleton — list
93
- endpoints can return 401 until a `setup:` suite captures session
94
- vars. Running checks cold (e.g. `--check ignored_auth` or a full
95
- run) immediately surfaces dozens of
96
- spurious `ignored_auth` / `report_backend_bug` findings (cold-state
97
- race, F1/F18 in feedback loop). Fix: run one smoke pass first
98
- (`zond run apis/<name>/tests --safe --report json` or `zond doctor
99
- --api <name> --missing-only`), let `prepare-fixtures` warm
100
- `.env.yaml`, then `zond checks run`.
101
- - **Treat per-check noise as fixture / generator drift, not a backend
102
- bug.** Two checks are reliably noisy on production APIs and need a
103
- pre-filter before triage:
104
- - `positive_data_acceptance` — emits MEDIUM when a generated body is
105
- rejected with 422. On APIs with **semantic validation** (Resend
106
- domain ownership, Stripe currency vs country, …) this is almost
107
- always `fix_generator` / `fix_fixture`, not `report_backend_bug`.
108
- Inspect the 422 reason; if it mentions a business rule the
109
- generator can't satisfy, file under `regenerate_suite` or annotate
110
- `seed_body`.
111
- - `response_schema_conformance` — HIGH on format-only mismatches
112
- (`format: email` rejecting a free-form string the server returned)
113
- is contract drift; HIGH on a field the server actually doesn't
114
- persist is a backend bug. Use `cross_call_references` to
115
- disambiguate before reporting.
116
- - **No response schema declared** (Sentry-style specs where the
117
- check skips 200+ endpoints): mine schemas from a real run and
118
- patch them in — `zond schema-from-runs --api <name>` writes
119
- `patch.schema.json` from the run's 2xx bodies, then
120
- `zond refresh-api --api <name> --merge-schema patch.schema.json`
121
- folds them into `.api-schema.local.yaml` (survives refresh) and
122
- re-runs conformance with real schemas (ARV-175/176).
123
-
124
- ## In-spec `x-zond-*` extensions (ARV-189, m-21)
125
-
126
- Low-friction per-endpoint policy via OpenAPI vendor extensions —
127
- when a single operation needs an exception, avoid the round-trip
128
- through `.api-resources.local.yaml`. Implemented today:
129
-
130
- | Extension | Type | Effect |
131
- |---|---|---|
132
- | `x-zond-skip: [check_id, …]` (or single string) | string\[] | skip those checks for this op |
133
- | `x-zond-public: true` | boolean | shortcut: skip auth-class checks (`ignored_auth`, `missing_required_header`) |
134
-
135
- Place at operation level (preferred) or path-item level — operation
136
- wins on key collision.
137
-
138
- ```yaml
139
- paths:
140
- /v1/public/status:
141
- get:
142
- x-zond-public: true # public health route
143
- responses: { '200': { description: ok } }
144
- /v1/admin/raw-debug:
145
- get:
146
- x-zond-skip: [status_code_conformance] # known 500-on-bad-input
147
- responses: { '500': { description: 'debug 500' } }
148
- ```
149
-
150
- Skipped runs surface in `--report` `skipped_outcomes` as
151
- `<check>: x-zond-skip listed "<id>" at the spec level` or
152
- `<check>: x-zond-public: true (auth check suppressed …)` so the
153
- operator can tell spec-level suppression apart from runtime skips.
154
-
155
- Priority (highest first): `.api-resources.local.yaml` overlay >
156
- `x-zond-*` extensions > `.api-resources.yaml` baseline > built-in
157
- defaults. For one-shots use extensions; for resource-wide policy
158
- (`idempotency.header`, `lifecycle.field`, …) use the overlay yaml.
159
-
160
- Not yet implemented: `x-zond-resource` / `x-zond-idempotent` /
161
- `x-zond-lifecycle-field` — these need deeper overlay-config wiring
162
- and are tracked separately. Use `annotate apply` for those today.
163
-
164
- ## Phase pre-0 — Annotation (mandatory for `--check stateful`)
165
-
166
- > **Heads-up for `pagination_invariants`:** `cursor` (Stripe-style) and
167
- > `page` (GitHub/GitLab/Atlassian/Notion-style `?page=N&per_page=M`)
168
- > are implemented. `offset` / `token` annotations parse but the check
169
- > short-circuits with "type not implemented" — skip dump+apply for
170
- > those resources until later milestones.
171
-
172
- m-20 stateful checks rely on per-resource config in
173
- `.api-resources.local.yaml` (overlay that survives `refresh-api`).
174
- `zond api annotate` is the canonical authoring path: zond emits raw
175
- spec slices, **agent (you) writes the YAML**, zond validates and
176
- applies. **zond itself does NOT call any LLM** — agent is the LLM, zond
177
- is the dumb-tool.
178
-
179
- Six dump aspects (one per check class):
180
-
181
- ```bash
182
- zond api annotate dump --api <name> --seed-bodies > /tmp/seed.json
183
- zond api annotate dump --api <name> --readback > /tmp/readback.json
184
- zond api annotate dump --api <name> --idempotency > /tmp/idem.json
185
- zond api annotate dump --api <name> --pagination > /tmp/pag.json
186
- zond api annotate dump --api <name> --lifecycle > /tmp/life.json
187
- zond api annotate dump --api <name> --resources > /tmp/orphans.json # optional: new CRUD resources from orphans
188
- ```
189
-
190
- Restrict scope with `--only r1,r2,r3` on any dump.
191
-
192
- `annotate dump` emits **JSON** (a top-level array of per-resource
193
- slices). YAML is a JSON superset, so the file parses either way — when
194
- piping into a YAML-aware reader, treat it as a list of objects with the
195
- same shape documented below. The corresponding response file the agent
196
- writes back is **YAML** (more comments-friendly).
197
-
198
- Agent reads each dump and writes a YAML response file (top-level list
199
- of entries — see per-check schemas below for the block shape).
200
- Optional `rationale` and `confidence: high|medium|low` per entry help
201
- future review.
202
-
203
- Apply — dry-run first (renders diff + conflicts), then `--yes` to write:
204
-
205
- ```bash
206
- zond api annotate apply --api <name> --readback --input /tmp/readback.yaml # dry-run
207
- zond api annotate apply --api <name> --readback --input /tmp/readback.yaml --yes # write
208
- zond api annotate apply --api <name> --readback --input /tmp/readback.yaml --yes --force # overwrite conflicts
209
- ```
210
-
211
- Conflicts: when an existing field already has a value, apply keeps
212
- existing by default (renders `! field: (conflict — kept existing; pass
213
- --yes to overwrite)`). Pass `--force` to overwrite.
214
-
215
- **Recommended pre-stateful sweep on a new API:**
216
-
217
- ```bash
218
- zond api annotate dump --api <name> --seed-bodies > /tmp/seed.json # create-body overlay for stateful checks
219
- zond api annotate dump --api <name> --readback > /tmp/readback.json # for cross_call_references
220
- zond api annotate dump --api <name> --pagination > /tmp/pag.json # for pagination_invariants
221
- zond api annotate dump --api <name> --lifecycle > /tmp/life.json # for lifecycle_transitions
222
- zond api annotate dump --api <name> --idempotency > /tmp/idem.json # for idempotency_replay
223
- # … agent generates YAML files for each …
224
- zond api annotate apply --api <name> --seed-bodies --input /tmp/seed.yaml --yes
225
- zond api annotate apply --api <name> --readback --input /tmp/readback.yaml --yes
226
- zond api annotate apply --api <name> --pagination --input /tmp/pag.yaml --yes
227
- zond api annotate apply --api <name> --lifecycle --input /tmp/life.yaml --yes
228
- zond api annotate apply --api <name> --idempotency --input /tmp/idem.yaml --yes
229
- ```
230
-
231
- Each block's YAML format is documented under the per-check section below.
232
-
233
- **Who writes the annotations:** zond does not infer them. The agent reads
234
- the `dump` output (spec slice + expected YAML shape) and writes the overlay;
235
- `apply` validates it against the zod contract and merges into
236
- `.api-resources.local.yaml`. For large specs, dump per-resource with
237
- `--only <res>` and (for seed-bodies) `--with-last-attempt --history N` to
238
- pull the recent seed-POST error progression onto your screen before writing.
239
-
240
- ## Reading findings
241
-
242
- Every finding carries a closed-enum `recommended_action` so the agent
243
- can route without parsing free-form messages:
244
-
245
- | `recommended_action` | What to do |
246
- |---|---|
247
- | `report_backend_bug` | Server returned 5xx / accepted invalid auth / leaked deleted resource. File a backend ticket; do not "fix" the test. |
248
- | `fix_spec` | Server's behaviour is reasonable but spec doesn't predict it. Update `apis/<name>/spec.json` (or upstream OpenAPI) and `zond refresh-api`. |
249
- | `tighten_validation` | Server accepted a body that violates the schema. Backend should reject earlier (400/422). |
250
- | `add_required_header` | Spec marked a header `required: true`; server didn't enforce it. Either enforce it or relax the spec. |
251
- | `fix_auth_config` | Auth-related failure. Check `apis/<name>/.env.yaml` (`auth_token`, `api_key`) — never log the value. |
252
- | `fix_network_config` | Transport-level error (timeout / DNS / refused). Verify `base_url` and reachability before re-running. |
253
- | `wontfix_known_limitation` | Known accepted gap. Don't retry, don't file a bug. |
254
-
255
- **Severity is a coarse deterministic CI-gate default, not a priority signal
256
- (ARV-346).** zond stamps each check a fixed severity so the exit code
257
- (`high_or_critical`) and SARIF mapping are reproducible — it is NOT a judgment
258
- about which finding matters most on *your* API. You (the agent) prioritize
259
- from `recommended_action` + the raw evidence, not from zond's severity number:
260
- route by the closed enum first, read the response body, decide impact. The
261
- severity field only answers "does this trip the CI gate?", nothing more. (This
262
- is the deterministic per-check default — not the removed ARV-337 calibrator.)
263
-
264
- HIGH/CRITICAL gates exit-code 1; LOW/MEDIUM is informational. In an
265
- orchestrator/CI that must tell "found drift" from "command crashed", pass
266
- `--no-fail-on-findings` (alias `--advisory`, ARV-308): exit 0 even with
267
- HIGH/CRITICAL findings — the findings are still in the envelope. Mirrors
268
- `zond run --no-fail-on-failures`. Exit 2 stays reserved for real failures.
269
-
270
- ### Spec-level rollup (ARV-60)
271
-
272
- When a single spec-level gap manifests on many operations (`401 not
273
- declared in spec` × 83 sites; `no JSON Schema on this response branch` ×
274
- all skipped cases; `use_after_free` ran 0 cases because there's no
275
- DELETE+GET pair in the spec), the runner emits a single `spec_finding`
276
- instead of N per-op rows. Threshold: ≥80% of the check's applicable
277
- operations sharing one root cause.
278
-
279
- Each `spec_finding` has:
280
-
281
- | Field | Meaning |
282
- |---|---|
283
- | `kind` | `status_drift` / `missing_declaration` / `no_detector` / `broken_baseline` / `other` |
284
- | `reason` | One-line root cause statement |
285
- | `fix_hint` | Actionable next step (spec edit / tolerate flag / annotate command) |
286
- | `affected_operations` | Operations covered (empty for skip/no-detector clusters) |
287
- | `count` / `applicable` | Cluster size + the population it was measured against |
288
-
289
- `broken_baseline` (ARV-307) is special: when the positive/success probes
290
- overwhelmingly failed (>90% non-2xx — e.g. a fully auth-rejected scan),
291
- `status_code_conformance` / `content_type_conformance` findings are baseline
292
- artifacts, so they're removed from `findings[]` and rolled into this one
293
- row. Fix the baseline (auth + path-param fixtures) and re-run before trusting
294
- conformance — undeclared statuses on an all-4xx scan are not real drift.
295
-
296
- Triage spec_findings first — they collapse 1×N noise into one decision.
297
- Per-op findings still live in `data.findings`; `--verbose` brings the
298
- full unaggregated list back to stdout. SARIF + JSON envelope always
299
- carry both layers.
300
-
301
- ```bash
302
- zond checks run --api myapi --json | jq '.data.spec_findings'
303
- zond checks run --api myapi --report ndjson | jq -c 'select(.type == "spec_finding")'
304
- ```
305
-
306
- ## Output formats
307
-
308
- ```bash
309
- zond checks run --api myapi --json # one envelope: findings[] + spec_findings[] + summary
310
- zond checks run --api myapi --report sarif --output zond.sarif
311
- # SARIF v2.1.0 for github/codeql-action/upload-sarif@v3
312
- zond checks run --api myapi --report ndjson | jq -c '.' # streaming events: check_start | check_result | finding | spec_finding | summary
313
- ```
314
-
315
- NDJSON event schema is published at `docs/json-schema/ndjson-events.schema.json`
316
- — pipe through `ajv validate` if you build a downstream consumer.
317
-
318
- ## Scoping a run
319
-
320
- Long runs feel sluggish on a 200-endpoint spec. Scope down before
321
- broadening:
322
-
323
- ```bash
324
- # Filter operations (regex) — same grammar as `zond generate`
325
- zond checks run --api myapi --include 'tag:billing,users' --exclude 'method:DELETE'
326
-
327
- # Pick a vector
328
- zond checks run --api myapi --mode positive # contract verification only
329
- zond checks run --api myapi --mode negative # malicious-input probes only
330
-
331
- # Pick a phase
332
- zond checks run --api myapi --phase coverage # ~×1.75 cases per op; real bug-hunt phase
333
- zond checks run --api myapi --phase examples # default — 1 positive + 1 negative per op (smoke)
334
- ```
335
-
336
- **`--phase examples` is smoke, not depth.** It runs one positive + one
337
- negative example per operation, finishes ~3× faster, but routinely
338
- returns zero HIGH findings on well-formed APIs because boundary
339
- mutations never fire. For an actual bug-hunt sweep, pass
340
- `--phase coverage` — it expands each operation to ~×1.75 cases with
341
- deterministic boundary values (min/max ints, empty strings, length
342
- overflows, RFC-3339 edge dates, …). Use `examples` only for fast
343
- gate-keeping in CI; use `coverage` once before sign-off.
344
-
345
- `--allow-x00` adds the NUL byte (`\x00`) to string boundaries during
346
- coverage — off by default (some HTTP/JSON stacks panic on it).
347
-
348
- ## Contribution to audit-coverage (ARV-265)
349
-
350
- Every case `checks run` dispatches is now written to `runs`/`results`
351
- with `run_kind = 'check'`. This means `zond coverage` picks it up in
352
- its **audit-coverage** block — answering "did the scan touch endpoint
353
- X?" without requiring a separate `zond run`. The test-coverage block
354
- (pass/hit) is unaffected — `checks run` does not produce passing test
355
- suites, so it doesn't contribute to `pass-coverage`.
356
-
357
- ```bash
358
- zond checks run --api myapi # touches every endpoint at least once
359
- zond coverage --api myapi --scope audit # 100% audit-coverage on the GET surface
360
- zond coverage --api myapi # default dual output: test + audit blocks
361
- ```
362
-
363
- Opt-out: `ZOND_CHECKS_PERSIST=0 zond checks run …` skips the
364
- persistence step (matches pre-ARV-265 behaviour).
365
-
366
- ## Concurrency
367
-
368
- ```bash
369
- zond checks run --api myapi --workers auto # min(cpus, 8); usually right
370
- zond checks run --api myapi --workers 16 # explicit; clamped to 64
371
- zond checks run --api myapi --workers 8 --rate-limit 50
372
- # 8 workers, global 50 RPS budget
373
- ```
374
-
375
- Workers parallelize at the *operation* level — cases inside one
376
- operation always run sequentially (CRUD chain ordering must be
377
- preserved). `--rate-limit auto` adapts from `RateLimit-*` response
378
- headers (RFC 9568); use it on rate-limited APIs to avoid bursting.
379
-
380
- ## "0 findings" doesn't always mean "all green"
381
-
382
- The summary one-liner now ends with `(N check outcome(s) skipped: …)`
383
- when probes can't validate (e.g. `response_schema_conformance: no JSON
384
- Schema on this response branch ×2` when probes ran without auth and
385
- got a 4xx that the spec only declares for 2xx). Treat skipped probes
386
- as "not yet exercised", not "passed" — re-run with auth (or via
387
- `zond run --validate-schema`) to actually cover those branches.
388
-
389
- ## Exit codes
390
-
391
- | Code | Meaning |
392
- |---|---|
393
- | 0 | No HIGH/CRITICAL findings (LOW/MEDIUM may be present). |
394
- | 1 | At least one HIGH/CRITICAL finding — gate CI on this. |
395
- | 2 | CLI-input error (bad flag value, unreachable spec, etc.). |
396
-
397
- ## Common combos
398
-
399
- ```bash
400
- # Full conformance pass on staging, output SARIF for code scanning
401
- zond checks run --api staging --report sarif --output zond.sarif --workers auto
402
-
403
- # Just the security checks (stateful) with explicit auth
404
- zond checks run --api prod \
405
- --check ignored_auth,use_after_free,ensure_resource_availability \
406
- --auth-header 'Authorization: Bearer $TOKEN'
407
-
408
- # Coverage-phase boundary sweep, NDJSON pipe into a watcher
409
- zond checks run --api dev --phase coverage --report ndjson | \
410
- jq -c 'select(.type == "finding") | {check, op: .finding.operation, action: .finding.recommended_action}'
411
-
412
- # Cross-call POST→GET drift only (m-20, single CRUD-chain check per resource)
413
- zond checks run --api stripe --check cross_call_references
414
- ```
415
-
416
- ## Cross-call drift (m-20 ARV-169)
417
-
418
- `cross_call_references` — POST resource → GET resource, diff write-shape
419
- vs read-shape. Surfaces fields the server silently dropped:
420
-
421
- - **state_not_persisted** — POST 2xx echoed the field, GET dropped it.
422
- HIGH-signal: server lied about persisting state.
423
- - **write_only** — POST accepted, GET dropped. Spec-declared write-only
424
- fields (passwords, etc.) are auto-filtered.
425
-
426
- Tunable per-resource in `apis/<name>/.api-resources.yaml` (или
427
- `.api-resources.local.yaml` overlay):
428
-
429
- ```yaml
430
- resources:
431
- - resource: customer
432
- # … existing fields …
433
- readback_diff:
434
- ignore_fields: [metadata, livemode] # API-quirks, suppress
435
- write_to_read_map:
436
- tax_id_data: tax_ids # write-shape → read-shape
437
- ```
438
-
439
- Defaults already filter timestamps (`created_at`, `updated_at`), envelope
440
- fields (`object`, `_links`), and ETag. Per-API quirks need a yaml line.
441
- Authored either by hand or via the `zond api annotate dump --readback` →
442
- agent → `apply` flow (see **Phase pre-0** above). zond emits the
443
- write+read endpoint slice; agent decides `ignore_fields` /
444
- `write_to_read_map` and writes the YAML; zond validates + writes to
445
- `.api-resources.local.yaml`.
446
-
447
- ## Idempotency replay (m-20 ARV-170)
448
-
449
- `idempotency_replay` — two POSTs with the same `Idempotency-Key` header.
450
- Server must (a) return the same resource id and (b) bit-identical response
451
- bodies (modulo timestamps / request-id / etag).
452
-
453
- - **duplicate_resource** — ids differ → server ignored the key. HIGH.
454
- - **non_bit_identical** — same id but bodies drift on non-ignored fields
455
- → replay isn't truly idempotent. Surfaced in the same HIGH finding via
456
- `evidence.kind`.
457
-
458
- Two ways to opt-in per resource:
459
-
460
- 1. Spec declares `Idempotency-Key` as a header parameter on the create
461
- endpoint → auto-detected, runs with defaults.
462
- 2. `.api-resources.local.yaml` block (preferred — documents intent +
463
- lets you tune the ignore list). Author via Phase pre-0
464
- `annotate dump --idempotency` → agent → `apply`:
465
-
466
- ```yaml
467
- resources:
468
- - resource: charge
469
- # … existing fields …
470
- idempotency:
471
- header: Idempotency-Key # default; override for non-standard names
472
- scope: endpoint # informational; `endpoint` | `global`
473
- ignore_response_fields: # added on top of timestamp/request_id baseline
474
- - retry_after
475
- ```
476
-
477
- Anti-FP: 429/409 on the 2nd POST → skip with cleanup. No DELETE on the
478
- group → finding still fires, evidence carries `cleanup_warning`.
479
-
480
- ## Pagination invariants (m-20 ARV-171, m-21 ARV-220)
481
-
482
- `pagination_invariants` — fetch two consecutive pages and assert the
483
- contract holds. Two styles implemented:
484
-
485
- **Cursor style** (Stripe / Notion / Linear, default when
486
- `starting_after` / `cursor` / `after` / `page_token` query param is
487
- detected):
488
-
489
- - **duplicate_items** — an item id appears on both page A and page B
490
- (off-by-one / cursor stops one short). HIGH-signal.
491
- - **has_more_inconsistent** — page A said `has_more=true`, but page B is
492
- empty and doesn't flip to `has_more=false`. Broken end-of-list signal.
493
- - **partial_page_with_has_more** — page A returns fewer items than the
494
- requested limit *yet* advertises `has_more=true`. Cursor truncates.
495
-
496
- **Page-number style** (GitHub / GitLab / Atlassian / Notion, default
497
- when `page` / `page_number` query param is detected):
498
-
499
- - **duplicate_items** — items overlap across pages N and N+1.
500
- - **per_page_exceeded** — server returned more items than `per_page=N`
501
- (e.g. asked for 2, got 5). `has_more` is NOT enforced — most APIs in
502
- this family signal end-of-list via Link headers / `total_pages`.
503
-
504
- `offset` / `token` declarations parse but the check short-circuits with
505
- a "type not implemented" reason so the yaml block stays a stable schema.
506
-
507
- Defaults: `cursor_field=id`, `items_field=data|items|results|value`,
508
- `has_more_field=has_more`, `limit=2`. Page-style adds `page_param=page`,
509
- `start_page=1`, `limit_param=per_page`.
510
-
511
- Per-resource yaml override (author via Phase pre-0
512
- `annotate dump --pagination` → agent → `apply`):
513
-
514
- ```yaml
515
- resources:
516
- - resource: customer # Stripe-style cursor
517
- pagination:
518
- type: cursor
519
- cursor_param: starting_after # Stripe; "after" / "cursor" / "page_token" also work
520
- cursor_field: id # field on each item that feeds the next cursor
521
- has_more_field: has_more # response field that flips on end-of-list
522
- limit_param: limit
523
- default_limit: 2
524
- items_field: data # array container (falls back to items / results / value)
525
-
526
- - resource: issue # GitHub-style page
527
- pagination:
528
- type: page
529
- page_param: page # query param carrying page number (default `page`)
530
- start_page: 1 # 1-based on GitHub/GitLab; 0 for some APIs
531
- limit_param: per_page # default `per_page` for page-style
532
- default_limit: 2
533
- cursor_field: id # dedupe key across pages
534
- items_field: data # omit if response is a bare array
535
- ```
536
-
537
- ## Lifecycle transitions (m-20 ARV-172, m-21 ARV-219)
538
-
539
- `lifecycle_transitions` runs in one of two modes based on the
540
- `actions:` field of the yaml manifest:
541
-
542
- **Action-driven mode** (preferred — needs create + read endpoints):
543
- The check creates a resource and walks the named actions, asserting:
544
-
545
- - **undeclared_state** — observed state isn't in declared `states[]`.
546
- - **wrong_expected_state** — action landed the resource in a state other
547
- than its declared `expected_state`.
548
- - **forbidden_transition** — observed (from, to) isn't in declared
549
- transitions graph.
550
- - **state_regression_on_replay** — invoking the action a second time
551
- drifted state instead of staying idempotent.
552
- - **double_action_5xx** — replay 5xx'd. Idempotent actions should 4xx
553
- or 2xx, never crash.
554
- - **action_rejected** — first-call non-2xx (server-side gating). Not a
555
- contract bug per se, surfaced as INCONCLUSIVE-class info.
556
-
557
- **Observation mode** (ARV-219 — read-only state machines): when
558
- `actions: {}` (or omitted) AND the resource has a list endpoint, the
559
- check GETs the list once and asserts every observed `field` value is
560
- in `states[]`. Reports `undeclared_state` with sample item ids for
561
- each unexpected value.
562
-
563
- When to use observation mode:
564
- - The API exposes a status enum the agent enumerated from the spec,
565
- but the auth scope is read-only and cannot drive mutations (GitHub
566
- Issues with a read PAT, public-read endpoints).
567
- - You want to detect contract-doc drift (spec says `enum: [open,
568
- closed]`, prod returns `archived`).
569
-
570
- Observation mode cannot verify the `transitions` graph (no time
571
- series in a single list call) — the transition arrows stay purely
572
- documentation. If write scope is available, prefer action mode for
573
- the full guarantee surface.
574
-
575
- Skip reasons unique to observation mode: `list returned <code>` (broken
576
- baseline), `list empty — no data to observe`, `state field "<x>"
577
- missing on all N observed items — yaml mismatch or nested field`.
578
-
579
- Manifest validation runs at yaml load (catches cycles, unreachable
580
- states, missing terminal, actions referencing undeclared states)
581
- before any HTTP call goes out.
582
-
583
- Author via Phase pre-0 `annotate dump --lifecycle` → agent → `apply`.
584
- The dump emits action-endpoint candidates (POST `/{resource}/{id}/cancel`,
585
- PATCH `/{resource}/{id}/status` etc.); agent decides the state machine
586
- graph. For observation-only resources, return `actions: {}` and the
587
- check picks the observation path automatically.
588
-
589
- ```yaml
590
- resources:
591
- - resource: subscription # action-driven mode
592
- lifecycle:
593
- field: status
594
- states: [pending, active, cancelled]
595
- transitions:
596
- - from: pending
597
- to: [active, cancelled]
598
- - from: active
599
- to: [cancelled]
600
- - from: cancelled
601
- to: [] # terminal
602
- actions:
603
- cancel:
604
- endpoint: POST /v1/subscriptions/{id}/cancel
605
- expected_state: cancelled
606
-
607
- - resource: issue # observation mode
608
- lifecycle:
609
- field: state
610
- states: [open, closed]
611
- transitions:
612
- - from: open
613
- to: [closed]
614
- - from: closed
615
- to: []
616
- actions: {} # no actions → check observes via GET /issues
617
- ```
618
-
619
- Action endpoints accept the `{id}` placeholder (replaced with the
620
- captured create-id) or `{<idParam>}`. Body-less actions are the common
621
- case; provide `body:` only for actions that demand a request payload.