@kirrosh/zond 0.21.0 → 0.23.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 (256) hide show
  1. package/CHANGELOG.md +758 -3
  2. package/README.md +78 -15
  3. package/package.json +17 -10
  4. package/src/cli/argv.ts +122 -0
  5. package/src/cli/commands/add-api.ts +134 -0
  6. package/src/cli/commands/api/annotate/idempotency.ts +59 -0
  7. package/src/cli/commands/api/annotate/index.ts +525 -0
  8. package/src/cli/commands/api/annotate/lifecycle.ts +74 -0
  9. package/src/cli/commands/api/annotate/overlay.ts +206 -0
  10. package/src/cli/commands/api/annotate/pagination.ts +60 -0
  11. package/src/cli/commands/api/annotate/prompts.ts +183 -0
  12. package/src/cli/commands/api/annotate/readback.ts +58 -0
  13. package/src/cli/commands/api/annotate/resources.ts +91 -0
  14. package/src/cli/commands/api/annotate/seed-bodies.ts +61 -0
  15. package/src/cli/commands/audit.ts +480 -0
  16. package/src/cli/commands/bootstrap.ts +710 -0
  17. package/src/cli/commands/catalog.ts +35 -0
  18. package/src/cli/commands/check.ts +348 -0
  19. package/src/cli/commands/checks.ts +756 -0
  20. package/src/cli/commands/ci-init.ts +55 -6
  21. package/src/cli/commands/clean.ts +212 -0
  22. package/src/cli/commands/cleanup.ts +262 -0
  23. package/src/cli/commands/completions.ts +192 -0
  24. package/src/cli/commands/coverage.ts +605 -132
  25. package/src/cli/commands/db.ts +180 -8
  26. package/src/cli/commands/describe.ts +37 -2
  27. package/src/cli/commands/discover.ts +1236 -0
  28. package/src/cli/commands/doctor.ts +607 -0
  29. package/src/cli/commands/fixtures.ts +402 -0
  30. package/src/cli/commands/generate.ts +420 -47
  31. package/src/cli/commands/init/agents-md.ts +61 -0
  32. package/src/cli/commands/init/bootstrap.ts +108 -0
  33. package/src/cli/commands/init/index.ts +244 -0
  34. package/src/cli/commands/init/skills.ts +98 -0
  35. package/src/cli/commands/init/templates/agents.md +77 -0
  36. package/src/cli/commands/init/templates/markdown.d.ts +4 -0
  37. package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
  38. package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
  39. package/src/cli/commands/init/templates/skills/zond.md +651 -0
  40. package/src/cli/commands/init/templates/zond-config.yml +14 -0
  41. package/src/cli/commands/prepare-fixtures.ts +135 -0
  42. package/src/cli/commands/probe/mass-assignment.ts +503 -0
  43. package/src/cli/commands/probe/security.ts +454 -0
  44. package/src/cli/commands/probe/static.ts +255 -0
  45. package/src/cli/commands/probe/webhooks.ts +161 -0
  46. package/src/cli/commands/probe.ts +459 -0
  47. package/src/cli/commands/reference.ts +87 -0
  48. package/src/cli/commands/refresh-api.ts +169 -0
  49. package/src/cli/commands/remove-api.ts +150 -0
  50. package/src/cli/commands/report-bundle.ts +318 -0
  51. package/src/cli/commands/report.ts +241 -0
  52. package/src/cli/commands/request.ts +379 -4
  53. package/src/cli/commands/run.ts +911 -33
  54. package/src/cli/commands/session.ts +244 -0
  55. package/src/cli/commands/use.ts +74 -0
  56. package/src/cli/index.ts +36 -607
  57. package/src/cli/json-envelope.ts +112 -3
  58. package/src/cli/json-schemas.ts +263 -0
  59. package/src/cli/program.ts +218 -0
  60. package/src/cli/resolve.ts +105 -0
  61. package/src/cli/status-filter.ts +124 -0
  62. package/src/cli/util/api-context.ts +85 -0
  63. package/src/cli/version.ts +8 -0
  64. package/src/core/anti-fp/bootstrap.ts +34 -0
  65. package/src/core/anti-fp/index.ts +33 -0
  66. package/src/core/anti-fp/registry.ts +44 -0
  67. package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
  68. package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
  69. package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
  70. package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
  71. package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
  72. package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
  73. package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
  74. package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
  75. package/src/core/anti-fp/types.ts +68 -0
  76. package/src/core/checks/checks/_crud-helpers.ts +133 -0
  77. package/src/core/checks/checks/_negative_mutator.ts +133 -0
  78. package/src/core/checks/checks/_readback-helpers.ts +133 -0
  79. package/src/core/checks/checks/content_type_conformance.ts +39 -0
  80. package/src/core/checks/checks/cross_call_references.ts +134 -0
  81. package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
  82. package/src/core/checks/checks/idempotency_replay.ts +246 -0
  83. package/src/core/checks/checks/ignored_auth.ts +211 -0
  84. package/src/core/checks/checks/index.ts +65 -0
  85. package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
  86. package/src/core/checks/checks/missing_required_header.ts +40 -0
  87. package/src/core/checks/checks/negative_data_rejection.ts +45 -0
  88. package/src/core/checks/checks/not_a_server_error.ts +27 -0
  89. package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
  90. package/src/core/checks/checks/pagination_invariants.ts +238 -0
  91. package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
  92. package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
  93. package/src/core/checks/checks/response_headers_conformance.ts +74 -0
  94. package/src/core/checks/checks/response_schema_conformance.ts +30 -0
  95. package/src/core/checks/checks/status_code_conformance.ts +61 -0
  96. package/src/core/checks/checks/unsupported_method.ts +63 -0
  97. package/src/core/checks/checks/use_after_free.ts +78 -0
  98. package/src/core/checks/index.ts +30 -0
  99. package/src/core/checks/mode.ts +79 -0
  100. package/src/core/checks/recommended-action.ts +64 -0
  101. package/src/core/checks/registry.ts +78 -0
  102. package/src/core/checks/runner.ts +874 -0
  103. package/src/core/checks/sarif.ts +230 -0
  104. package/src/core/checks/stateful.ts +121 -0
  105. package/src/core/checks/types.ts +189 -0
  106. package/src/core/classifier/recommended-action.ts +222 -0
  107. package/src/core/context/current.ts +51 -0
  108. package/src/core/context/session.ts +78 -0
  109. package/src/core/coverage/loader.ts +185 -0
  110. package/src/core/coverage/reasons.ts +300 -0
  111. package/src/core/diagnostics/db-analysis.ts +161 -12
  112. package/src/core/diagnostics/failure-class.ts +120 -0
  113. package/src/core/diagnostics/failure-hints.ts +212 -9
  114. package/src/core/diagnostics/spec-pointer.ts +99 -0
  115. package/src/core/diagnostics/suggested-fixes.ts +156 -0
  116. package/src/core/exporter/case-study/index.ts +270 -0
  117. package/src/core/exporter/curl.ts +40 -0
  118. package/src/core/exporter/exporter.ts +48 -0
  119. package/src/core/exporter/html-report/escape.ts +24 -0
  120. package/src/core/exporter/html-report/index.ts +479 -0
  121. package/src/core/exporter/html-report/script.ts +100 -0
  122. package/src/core/exporter/html-report/styles.ts +408 -0
  123. package/src/core/generator/chunker.ts +53 -15
  124. package/src/core/generator/coverage-phase.ts +0 -0
  125. package/src/core/generator/create-body.ts +89 -0
  126. package/src/core/generator/data-factory.ts +490 -33
  127. package/src/core/generator/describe.ts +1 -1
  128. package/src/core/generator/fixtures-builder.ts +325 -0
  129. package/src/core/generator/index.ts +7 -5
  130. package/src/core/generator/openapi-reader.ts +55 -3
  131. package/src/core/generator/path-param-disambig.ts +114 -0
  132. package/src/core/generator/resources-builder.ts +648 -0
  133. package/src/core/generator/schema-utils.ts +11 -3
  134. package/src/core/generator/serializer.ts +114 -15
  135. package/src/core/generator/suite-generator.ts +484 -77
  136. package/src/core/generator/types.ts +8 -0
  137. package/src/core/identity/identity-file.ts +129 -0
  138. package/src/core/lint/affects.ts +28 -0
  139. package/src/core/lint/config.ts +96 -0
  140. package/src/core/lint/format.ts +42 -0
  141. package/src/core/lint/index.ts +94 -0
  142. package/src/core/lint/reporter.ts +128 -0
  143. package/src/core/lint/rules/consistency.ts +158 -0
  144. package/src/core/lint/rules/heuristics.ts +97 -0
  145. package/src/core/lint/rules/strictness.ts +109 -0
  146. package/src/core/lint/types.ts +96 -0
  147. package/src/core/lint/walker.ts +248 -0
  148. package/src/core/meta/meta-store.ts +6 -73
  149. package/src/core/output/README.md +91 -0
  150. package/src/core/output/index.ts +13 -0
  151. package/src/core/output/run.ts +126 -0
  152. package/src/core/output/types.ts +129 -0
  153. package/src/core/parser/env-interpolation.ts +104 -0
  154. package/src/core/parser/filter.ts +57 -0
  155. package/src/core/parser/schema.ts +132 -5
  156. package/src/core/parser/types.ts +29 -2
  157. package/src/core/parser/variables.ts +0 -0
  158. package/src/core/parser/yaml-parser.ts +108 -13
  159. package/src/core/probe/bootstrap.ts +34 -0
  160. package/src/core/probe/dry-run-envelope.ts +57 -0
  161. package/src/core/probe/mass-assignment-probe-class.ts +198 -0
  162. package/src/core/probe/mass-assignment-probe.ts +1122 -0
  163. package/src/core/probe/mass-assignment-template.ts +212 -0
  164. package/src/core/probe/method-probe.ts +164 -0
  165. package/src/core/probe/method-shared.ts +69 -0
  166. package/src/core/probe/negative-probe.ts +691 -0
  167. package/src/core/probe/orphan-tracker.ts +188 -0
  168. package/src/core/probe/path-discovery.ts +440 -0
  169. package/src/core/probe/probe-harness.ts +120 -0
  170. package/src/core/probe/registry.ts +89 -0
  171. package/src/core/probe/runner.ts +136 -0
  172. package/src/core/probe/security-probe-class.ts +201 -0
  173. package/src/core/probe/security-probe.ts +1453 -0
  174. package/src/core/probe/shared.ts +505 -0
  175. package/src/core/probe/static-probe-class.ts +125 -0
  176. package/src/core/probe/types.ts +165 -0
  177. package/src/core/probe/verdict-aggregator.ts +33 -0
  178. package/src/core/probe/webhooks-probe.ts +284 -0
  179. package/src/core/reporter/console.ts +69 -4
  180. package/src/core/reporter/index.ts +2 -3
  181. package/src/core/reporter/json.ts +15 -2
  182. package/src/core/reporter/junit.ts +27 -12
  183. package/src/core/reporter/ndjson.ts +37 -0
  184. package/src/core/reporter/types.ts +3 -0
  185. package/src/core/runner/assertions.ts +62 -2
  186. package/src/core/runner/async-pool.ts +108 -0
  187. package/src/core/runner/auth-path.ts +8 -0
  188. package/src/core/runner/ci-context.ts +72 -0
  189. package/src/core/runner/executor.ts +391 -52
  190. package/src/core/runner/form-encode.ts +51 -0
  191. package/src/core/runner/http-client.ts +115 -7
  192. package/src/core/runner/learn-drift.ts +293 -0
  193. package/src/core/runner/preflight-vars.ts +149 -0
  194. package/src/core/runner/progress-tracker.ts +73 -0
  195. package/src/core/runner/rate-limiter.ts +203 -0
  196. package/src/core/runner/run-kind.ts +39 -0
  197. package/src/core/runner/schema-validator.ts +312 -0
  198. package/src/core/runner/send-request.ts +153 -20
  199. package/src/core/runner/types.ts +38 -0
  200. package/src/core/secrets/registry.ts +164 -0
  201. package/src/core/secrets/secrets-file.ts +115 -0
  202. package/src/core/selectors/operation-filter.ts +144 -0
  203. package/src/core/setup-api.ts +419 -17
  204. package/src/core/severity/category.ts +94 -0
  205. package/src/core/severity/index.ts +121 -0
  206. package/src/core/spec/layers.ts +154 -0
  207. package/src/core/util/format-eta.ts +21 -0
  208. package/src/core/utils.ts +5 -1
  209. package/src/core/workspace/config.ts +129 -0
  210. package/src/core/workspace/manifest.ts +283 -0
  211. package/src/core/workspace/output-rotation.ts +62 -0
  212. package/src/core/workspace/root.ts +94 -0
  213. package/src/core/workspace/triage-path.ts +87 -0
  214. package/src/db/lint-runs.ts +47 -0
  215. package/src/db/migrate.ts +126 -0
  216. package/src/db/migrations/0001_run_kind.sql +25 -0
  217. package/src/db/migrations/sql.d.ts +4 -0
  218. package/src/db/queries/collections.ts +133 -0
  219. package/src/db/queries/coverage.ts +9 -0
  220. package/src/db/queries/dashboard.ts +59 -0
  221. package/src/db/queries/results.ts +128 -0
  222. package/src/db/queries/runs.ts +235 -0
  223. package/src/db/queries/sessions.ts +42 -0
  224. package/src/db/queries/settings.ts +28 -0
  225. package/src/db/queries/types.ts +172 -0
  226. package/src/db/queries.ts +72 -802
  227. package/src/db/schema.ts +179 -48
  228. package/src/cli/commands/export.ts +0 -144
  229. package/src/cli/commands/guide.ts +0 -127
  230. package/src/cli/commands/init.ts +0 -57
  231. package/src/cli/commands/serve.ts +0 -81
  232. package/src/cli/commands/sync.ts +0 -269
  233. package/src/cli/commands/update.ts +0 -189
  234. package/src/cli/commands/validate.ts +0 -34
  235. package/src/core/exporter/postman.ts +0 -963
  236. package/src/core/generator/guide-builder.ts +0 -253
  237. package/src/core/meta/types.ts +0 -21
  238. package/src/core/parser/index.ts +0 -21
  239. package/src/core/runner/execute-run.ts +0 -132
  240. package/src/core/runner/index.ts +0 -12
  241. package/src/core/sync/spec-differ.ts +0 -38
  242. package/src/web/data/collection-state.ts +0 -362
  243. package/src/web/routes/api.ts +0 -314
  244. package/src/web/routes/dashboard.ts +0 -350
  245. package/src/web/routes/runs.ts +0 -64
  246. package/src/web/schemas.ts +0 -121
  247. package/src/web/server.ts +0 -134
  248. package/src/web/static/htmx.min.cjs +0 -1
  249. package/src/web/static/style.css +0 -1148
  250. package/src/web/views/endpoints-tab.ts +0 -174
  251. package/src/web/views/explorer-tab.ts +0 -402
  252. package/src/web/views/health-strip.ts +0 -92
  253. package/src/web/views/layout.ts +0 -48
  254. package/src/web/views/results.ts +0 -210
  255. package/src/web/views/runs-tab.ts +0 -126
  256. package/src/web/views/suites-tab.ts +0 -181
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Dry-run envelope helpers (m-17 / ARV-50).
3
+ *
4
+ * `--dry-run` answers "what would I attack" — severity is undefined
5
+ * because nothing was classified. Both probe-security and
6
+ * probe-mass-assignment write this shape into `data` instead of the
7
+ * legacy severity-bucket structure that conflated planned attacks
8
+ * with skipped endpoints (F1-15).
9
+ */
10
+ import type { EndpointPlan } from "./types.ts";
11
+
12
+ export interface DryRunSummary {
13
+ totalEndpoints: number;
14
+ planned: number;
15
+ skipped: number;
16
+ }
17
+
18
+ export interface DryRunEnvelopeData {
19
+ endpoints: EndpointPlan[];
20
+ summary: DryRunSummary;
21
+ }
22
+
23
+ export function summarizeDryRun(plans: EndpointPlan[]): DryRunEnvelopeData {
24
+ let planned = 0;
25
+ let skipped = 0;
26
+ for (const p of plans) {
27
+ if (p.planned) planned++;
28
+ else skipped++;
29
+ }
30
+ return {
31
+ endpoints: plans,
32
+ summary: {
33
+ totalEndpoints: plans.length,
34
+ planned,
35
+ skipped,
36
+ },
37
+ };
38
+ }
39
+
40
+ /** Render a short human digest for the dry-run plan (used by non-json
41
+ * output). One line per endpoint, planned/skipped counts at the end. */
42
+ export function formatDryRunDigest(plans: EndpointPlan[]): string {
43
+ const lines: string[] = [];
44
+ for (const p of plans) {
45
+ if (p.planned) {
46
+ const fields = p.fields_planned.length > 0 ? ` fields=${p.fields_planned.join(",")}` : "";
47
+ const cls = p.classes_planned.length > 0 ? ` classes=${p.classes_planned.join(",")}` : "";
48
+ lines.push(` + ${p.method} ${p.path}${cls}${fields}`);
49
+ } else {
50
+ lines.push(` - ${p.method} ${p.path} (skipped: ${p.skip_reason ?? "unknown"})`);
51
+ }
52
+ }
53
+ const summary = summarizeDryRun(plans).summary;
54
+ lines.push("");
55
+ lines.push(`Plan: ${summary.planned} planned · ${summary.skipped} skipped · ${summary.totalEndpoints} total`);
56
+ return lines.join("\n");
57
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * `MassAssignmentProbe` — Probe-contract wrapper around the existing
3
+ * `runMassAssignmentProbes` engine (m-17 / ARV-49).
4
+ *
5
+ * dryRun() lists POST/PATCH/PUT endpoints with the suspect fields the
6
+ * probe would inject (suspectedExtras + serverAssignedExtras). Skip
7
+ * reasons mirror the live runner (no-body, isolated-protected). This
8
+ * fills the F2-15 gap (mass-assignment had no --dry-run); ARV-52 wires
9
+ * the corresponding CLI flag.
10
+ *
11
+ * run() delegates to runMassAssignmentProbes; report() converts to
12
+ * either the legacy markdown digest or the structured per-endpoint
13
+ * shape (ARV-51).
14
+ */
15
+ import type { OpenAPIV3 } from "openapi-types";
16
+ import type { Probe, ProbeContext, ProbeFlags, EndpointPlan, ProbeResult, ProbeReportFormat, ProbeEndpointResult, ProbeEndpointStatus } from "./types.ts";
17
+ import {
18
+ runMassAssignmentProbes,
19
+ formatDigestMarkdown,
20
+ SUSPECTED_FIELDS,
21
+ type MassAssignmentResult,
22
+ type EndpointVerdict,
23
+ type Severity,
24
+ } from "./mass-assignment-probe.ts";
25
+ import { pathTouchesSeededVar } from "./shared.ts";
26
+ import { hasProbeBody } from "./probe-harness.ts";
27
+
28
+ const FLAGS: ProbeFlags = {
29
+ api: true,
30
+ tag: true,
31
+ include: true,
32
+ exclude: true,
33
+ dryRun: true,
34
+ listTags: true,
35
+ json: true,
36
+ output: true,
37
+ report: true,
38
+ };
39
+
40
+ function requestPropertyNames(schema?: OpenAPIV3.SchemaObject): Set<string> {
41
+ const out = new Set<string>();
42
+ if (!schema) return out;
43
+ if (schema.properties) for (const k of Object.keys(schema.properties)) out.add(k);
44
+ for (const composite of [schema.allOf, schema.oneOf, schema.anyOf]) {
45
+ if (Array.isArray(composite)) {
46
+ for (const sub of composite) {
47
+ const s = sub as OpenAPIV3.SchemaObject;
48
+ if (s.properties) for (const k of Object.keys(s.properties)) out.add(k);
49
+ }
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+
55
+ function suspectFieldsFor(ep: { requestBodySchema?: OpenAPIV3.SchemaObject }): string[] {
56
+ const reqProps = requestPropertyNames(ep.requestBodySchema);
57
+ const out: string[] = [];
58
+ for (const name of Object.keys(SUSPECTED_FIELDS)) {
59
+ if (!reqProps.has(name)) out.push(name);
60
+ }
61
+ return out;
62
+ }
63
+
64
+ function statusFromSeverity(s: Severity): ProbeEndpointStatus {
65
+ switch (s) {
66
+ case "high": return "high";
67
+ case "low": return "low";
68
+ case "medium": return "low";
69
+ case "info": return "low";
70
+ case "ok": return "ok";
71
+ case "skipped": return "skipped";
72
+ case "inconclusive-baseline":
73
+ case "inconclusive-5xx":
74
+ return "inconclusive";
75
+ }
76
+ }
77
+
78
+ function evidenceFromVerdict(v: EndpointVerdict): Record<string, unknown> {
79
+ return {
80
+ summary: v.summary,
81
+ request: { url: v.request.url, injectedFields: v.request.injectedFields },
82
+ response: v.response ? { status: v.response.status } : undefined,
83
+ fields: v.fields,
84
+ };
85
+ }
86
+
87
+ function toProbeResult(ma: MassAssignmentResult): ProbeResult {
88
+ const endpoints: ProbeEndpointResult[] = ma.verdicts.map((v) => ({
89
+ path: v.path,
90
+ method: v.method,
91
+ classes_run: ["mass-assignment"],
92
+ findings:
93
+ v.severity === "skipped" || v.severity === "ok"
94
+ ? []
95
+ : [{
96
+ class: "mass-assignment",
97
+ severity:
98
+ v.severity === "high" ? "high" :
99
+ v.severity === "low" || v.severity === "medium" ? "low" :
100
+ "inconclusive",
101
+ evidence: evidenceFromVerdict(v),
102
+ }],
103
+ status: statusFromSeverity(v.severity),
104
+ ...(v.severity === "skipped" ? { skip_reason: v.skipReason ?? v.summary } : {}),
105
+ }));
106
+ const by_status: Record<ProbeEndpointStatus, number> = {
107
+ ok: 0, high: 0, low: 0, inconclusive: 0, skipped: 0,
108
+ };
109
+ for (const ep of endpoints) by_status[ep.status]++;
110
+ return {
111
+ endpoints,
112
+ summary: {
113
+ totalEndpoints: ma.totalEndpoints,
114
+ probed: ma.specProbed,
115
+ by_status,
116
+ },
117
+ warnings: ma.warnings,
118
+ };
119
+ }
120
+
121
+ export class MassAssignmentProbe implements Probe {
122
+ readonly name = "mass-assignment";
123
+ readonly description =
124
+ "Live probe for mass-assignment / privilege-escalation: classifies POST/PATCH/PUT against suspected extra fields (is_admin, role, account_id, …).";
125
+ readonly commonFlags = FLAGS;
126
+
127
+ async dryRun(ctx: ProbeContext): Promise<EndpointPlan[]> {
128
+ const isolated = ctx.options["isolated"] === true;
129
+ const out: EndpointPlan[] = [];
130
+ for (const ep of ctx.endpoints) {
131
+ if (ep.deprecated) continue;
132
+ const m = ep.method.toUpperCase();
133
+ if (m !== "POST" && m !== "PUT" && m !== "PATCH") continue;
134
+
135
+ if (isolated && (m === "PUT" || m === "PATCH") && pathTouchesSeededVar(ep.path, ctx.vars)) {
136
+ out.push({
137
+ path: ep.path,
138
+ method: m,
139
+ planned: false,
140
+ classes_planned: [],
141
+ fields_planned: [],
142
+ skip_reason: "isolated-protected",
143
+ });
144
+ continue;
145
+ }
146
+
147
+ // ARV-150: parity with the live runner — form-urlencoded counts as a body.
148
+ if (!hasProbeBody(ep)) {
149
+ out.push({
150
+ path: ep.path,
151
+ method: m,
152
+ planned: false,
153
+ classes_planned: [],
154
+ fields_planned: [],
155
+ skip_reason: "no-body",
156
+ });
157
+ continue;
158
+ }
159
+
160
+ const fields = suspectFieldsFor(ep);
161
+ out.push({
162
+ path: ep.path,
163
+ method: m,
164
+ planned: true,
165
+ classes_planned: ["mass-assignment"],
166
+ fields_planned: fields,
167
+ skip_reason: null,
168
+ });
169
+ }
170
+ return out;
171
+ }
172
+
173
+ async run(ctx: ProbeContext): Promise<ProbeResult> {
174
+ const ma = await runMassAssignmentProbes({
175
+ endpoints: ctx.endpoints,
176
+ securitySchemes: ctx.securitySchemes,
177
+ vars: ctx.vars,
178
+ noCleanup: ctx.options["noCleanup"] === true,
179
+ timeoutMs: typeof ctx.options["timeoutMs"] === "number" ? (ctx.options["timeoutMs"] as number) : undefined,
180
+ discover: ctx.options["noDiscover"] !== true,
181
+ });
182
+ const result = toProbeResult(ma);
183
+ result.extras = { raw: ma };
184
+ return result;
185
+ }
186
+
187
+ report(format: ProbeReportFormat, result: ProbeResult): string | object {
188
+ if (format === "markdown") {
189
+ const raw = result.extras?.["raw"] as MassAssignmentResult | undefined;
190
+ if (raw) return formatDigestMarkdown(raw, "");
191
+ return "(no markdown digest available)";
192
+ }
193
+ return {
194
+ endpoints: result.endpoints,
195
+ summary: result.summary,
196
+ };
197
+ }
198
+ }