@kirrosh/zond 0.22.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.
- package/CHANGELOG.md +648 -0
- package/README.md +58 -6
- package/package.json +9 -6
- package/src/cli/argv.ts +122 -0
- package/src/cli/commands/add-api.ts +134 -0
- package/src/cli/commands/api/annotate/idempotency.ts +59 -0
- package/src/cli/commands/api/annotate/index.ts +525 -0
- package/src/cli/commands/api/annotate/lifecycle.ts +74 -0
- package/src/cli/commands/api/annotate/overlay.ts +206 -0
- package/src/cli/commands/api/annotate/pagination.ts +60 -0
- package/src/cli/commands/api/annotate/prompts.ts +183 -0
- package/src/cli/commands/api/annotate/readback.ts +58 -0
- package/src/cli/commands/api/annotate/resources.ts +91 -0
- package/src/cli/commands/api/annotate/seed-bodies.ts +61 -0
- package/src/cli/commands/audit.ts +480 -0
- package/src/cli/commands/bootstrap.ts +710 -0
- package/src/cli/commands/catalog.ts +35 -0
- package/src/cli/commands/check.ts +348 -0
- package/src/cli/commands/checks.ts +756 -0
- package/src/cli/commands/ci-init.ts +43 -0
- package/src/cli/commands/clean.ts +212 -0
- package/src/cli/commands/cleanup.ts +262 -0
- package/src/cli/commands/completions.ts +16 -0
- package/src/cli/commands/coverage.ts +605 -132
- package/src/cli/commands/db.ts +178 -7
- package/src/cli/commands/describe.ts +37 -2
- package/src/cli/commands/discover.ts +1236 -0
- package/src/cli/commands/doctor.ts +607 -0
- package/src/cli/commands/fixtures.ts +402 -0
- package/src/cli/commands/generate.ts +420 -46
- package/src/cli/commands/init/bootstrap.ts +30 -1
- package/src/cli/commands/{init.ts → init/index.ts} +99 -5
- package/src/cli/commands/init/skills.ts +56 -3
- package/src/cli/commands/init/templates/agents.md +65 -61
- package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
- package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
- package/src/cli/commands/init/templates/skills/zond.md +592 -125
- package/src/cli/commands/init/templates/zond-config.yml +8 -9
- package/src/cli/commands/prepare-fixtures.ts +135 -0
- package/src/cli/commands/probe/mass-assignment.ts +503 -0
- package/src/cli/commands/probe/security.ts +454 -0
- package/src/cli/commands/probe/static.ts +255 -0
- package/src/cli/commands/probe/webhooks.ts +161 -0
- package/src/cli/commands/probe.ts +459 -0
- package/src/cli/commands/reference.ts +87 -0
- package/src/cli/commands/refresh-api.ts +169 -0
- package/src/cli/commands/remove-api.ts +150 -0
- package/src/cli/commands/report-bundle.ts +318 -0
- package/src/cli/commands/report.ts +241 -0
- package/src/cli/commands/request.ts +379 -4
- package/src/cli/commands/run.ts +842 -53
- package/src/cli/commands/session.ts +244 -0
- package/src/cli/commands/use.ts +18 -1
- package/src/cli/index.ts +20 -3
- package/src/cli/json-envelope.ts +112 -3
- package/src/cli/json-schemas.ts +263 -0
- package/src/cli/program.ts +198 -635
- package/src/cli/resolve.ts +105 -0
- package/src/cli/status-filter.ts +124 -0
- package/src/cli/util/api-context.ts +85 -0
- package/src/cli/version.ts +5 -0
- package/src/core/anti-fp/bootstrap.ts +34 -0
- package/src/core/anti-fp/index.ts +33 -0
- package/src/core/anti-fp/registry.ts +44 -0
- package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
- package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
- package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
- package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
- package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
- package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
- package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
- package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
- package/src/core/anti-fp/types.ts +68 -0
- package/src/core/checks/checks/_crud-helpers.ts +133 -0
- package/src/core/checks/checks/_negative_mutator.ts +133 -0
- package/src/core/checks/checks/_readback-helpers.ts +133 -0
- package/src/core/checks/checks/content_type_conformance.ts +39 -0
- package/src/core/checks/checks/cross_call_references.ts +134 -0
- package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
- package/src/core/checks/checks/idempotency_replay.ts +246 -0
- package/src/core/checks/checks/ignored_auth.ts +211 -0
- package/src/core/checks/checks/index.ts +65 -0
- package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
- package/src/core/checks/checks/missing_required_header.ts +40 -0
- package/src/core/checks/checks/negative_data_rejection.ts +45 -0
- package/src/core/checks/checks/not_a_server_error.ts +27 -0
- package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
- package/src/core/checks/checks/pagination_invariants.ts +238 -0
- package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
- package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
- package/src/core/checks/checks/response_headers_conformance.ts +74 -0
- package/src/core/checks/checks/response_schema_conformance.ts +30 -0
- package/src/core/checks/checks/status_code_conformance.ts +61 -0
- package/src/core/checks/checks/unsupported_method.ts +63 -0
- package/src/core/checks/checks/use_after_free.ts +78 -0
- package/src/core/checks/index.ts +30 -0
- package/src/core/checks/mode.ts +79 -0
- package/src/core/checks/recommended-action.ts +64 -0
- package/src/core/checks/registry.ts +78 -0
- package/src/core/checks/runner.ts +874 -0
- package/src/core/checks/sarif.ts +230 -0
- package/src/core/checks/stateful.ts +121 -0
- package/src/core/checks/types.ts +189 -0
- package/src/core/classifier/recommended-action.ts +222 -0
- package/src/core/context/current.ts +22 -6
- package/src/core/context/session.ts +78 -0
- package/src/core/coverage/loader.ts +185 -0
- package/src/core/coverage/reasons.ts +300 -0
- package/src/core/diagnostics/db-analysis.ts +151 -11
- package/src/core/diagnostics/failure-class.ts +120 -0
- package/src/core/diagnostics/failure-hints.ts +212 -9
- package/src/core/diagnostics/spec-pointer.ts +99 -0
- package/src/core/diagnostics/suggested-fixes.ts +156 -0
- package/src/core/exporter/case-study/index.ts +270 -0
- package/src/core/exporter/curl.ts +40 -0
- package/src/core/exporter/exporter.ts +48 -0
- package/src/core/exporter/html-report/escape.ts +24 -0
- package/src/core/exporter/html-report/index.ts +479 -0
- package/src/core/exporter/html-report/script.ts +100 -0
- package/src/core/exporter/html-report/styles.ts +408 -0
- package/src/core/generator/chunker.ts +42 -16
- package/src/core/generator/coverage-phase.ts +0 -0
- package/src/core/generator/create-body.ts +89 -0
- package/src/core/generator/data-factory.ts +445 -19
- package/src/core/generator/describe.ts +1 -1
- package/src/core/generator/fixtures-builder.ts +325 -0
- package/src/core/generator/index.ts +7 -5
- package/src/core/generator/openapi-reader.ts +37 -3
- package/src/core/generator/path-param-disambig.ts +114 -0
- package/src/core/generator/resources-builder.ts +648 -0
- package/src/core/generator/schema-utils.ts +11 -3
- package/src/core/generator/serializer.ts +103 -13
- package/src/core/generator/suite-generator.ts +419 -111
- package/src/core/generator/types.ts +8 -0
- package/src/core/identity/identity-file.ts +129 -0
- package/src/core/lint/affects.ts +28 -0
- package/src/core/lint/config.ts +96 -0
- package/src/core/lint/format.ts +42 -0
- package/src/core/lint/index.ts +94 -0
- package/src/core/lint/reporter.ts +128 -0
- package/src/core/lint/rules/consistency.ts +158 -0
- package/src/core/lint/rules/heuristics.ts +97 -0
- package/src/core/lint/rules/strictness.ts +109 -0
- package/src/core/lint/types.ts +96 -0
- package/src/core/lint/walker.ts +248 -0
- package/src/core/meta/meta-store.ts +6 -73
- package/src/core/output/README.md +91 -0
- package/src/core/output/index.ts +13 -0
- package/src/core/output/run.ts +126 -0
- package/src/core/output/types.ts +129 -0
- package/src/core/parser/env-interpolation.ts +104 -0
- package/src/core/parser/filter.ts +57 -0
- package/src/core/parser/schema.ts +129 -4
- package/src/core/parser/types.ts +19 -1
- package/src/core/parser/variables.ts +0 -0
- package/src/core/parser/yaml-parser.ts +58 -12
- package/src/core/probe/bootstrap.ts +34 -0
- package/src/core/probe/dry-run-envelope.ts +57 -0
- package/src/core/probe/mass-assignment-probe-class.ts +198 -0
- package/src/core/probe/mass-assignment-probe.ts +1122 -0
- package/src/core/probe/mass-assignment-template.ts +212 -0
- package/src/core/probe/method-probe.ts +43 -76
- package/src/core/probe/method-shared.ts +69 -0
- package/src/core/probe/negative-probe.ts +183 -149
- package/src/core/probe/orphan-tracker.ts +188 -0
- package/src/core/probe/path-discovery.ts +440 -0
- package/src/core/probe/probe-harness.ts +120 -0
- package/src/core/probe/registry.ts +89 -0
- package/src/core/probe/runner.ts +136 -0
- package/src/core/probe/security-probe-class.ts +201 -0
- package/src/core/probe/security-probe.ts +1453 -0
- package/src/core/probe/shared.ts +505 -0
- package/src/core/probe/static-probe-class.ts +125 -0
- package/src/core/probe/types.ts +165 -0
- package/src/core/probe/verdict-aggregator.ts +33 -0
- package/src/core/probe/webhooks-probe.ts +284 -0
- package/src/core/reporter/console.ts +41 -2
- package/src/core/reporter/index.ts +2 -3
- package/src/core/reporter/json.ts +11 -1
- package/src/core/reporter/junit.ts +27 -12
- package/src/core/reporter/ndjson.ts +37 -0
- package/src/core/reporter/types.ts +3 -0
- package/src/core/runner/assertions.ts +58 -1
- package/src/core/runner/async-pool.ts +108 -0
- package/src/core/runner/auth-path.ts +8 -0
- package/src/core/runner/ci-context.ts +72 -0
- package/src/core/runner/executor.ts +264 -20
- package/src/core/runner/form-encode.ts +51 -0
- package/src/core/runner/http-client.ts +75 -2
- package/src/core/runner/learn-drift.ts +293 -0
- package/src/core/runner/preflight-vars.ts +149 -0
- package/src/core/runner/progress-tracker.ts +73 -0
- package/src/core/runner/rate-limiter.ts +89 -17
- package/src/core/runner/run-kind.ts +39 -0
- package/src/core/runner/schema-validator.ts +312 -0
- package/src/core/runner/send-request.ts +153 -20
- package/src/core/runner/types.ts +38 -0
- package/src/core/secrets/registry.ts +164 -0
- package/src/core/secrets/secrets-file.ts +115 -0
- package/src/core/selectors/operation-filter.ts +144 -0
- package/src/core/setup-api.ts +415 -16
- package/src/core/severity/category.ts +94 -0
- package/src/core/severity/index.ts +121 -0
- package/src/core/spec/layers.ts +154 -0
- package/src/core/util/format-eta.ts +21 -0
- package/src/core/utils.ts +5 -1
- package/src/core/workspace/config.ts +129 -0
- package/src/core/workspace/manifest.ts +283 -0
- package/src/core/workspace/output-rotation.ts +62 -0
- package/src/core/workspace/triage-path.ts +87 -0
- package/src/db/lint-runs.ts +47 -0
- package/src/db/migrate.ts +126 -0
- package/src/db/migrations/0001_run_kind.sql +25 -0
- package/src/db/migrations/sql.d.ts +4 -0
- package/src/db/queries/collections.ts +133 -0
- package/src/db/queries/coverage.ts +9 -0
- package/src/db/queries/dashboard.ts +59 -0
- package/src/db/queries/results.ts +128 -0
- package/src/db/queries/runs.ts +235 -0
- package/src/db/queries/sessions.ts +42 -0
- package/src/db/queries/settings.ts +28 -0
- package/src/db/queries/types.ts +172 -0
- package/src/db/queries.ts +72 -802
- package/src/db/schema.ts +178 -50
- package/src/cli/commands/export.ts +0 -144
- package/src/cli/commands/guide.ts +0 -127
- package/src/cli/commands/init/templates/skills/scenarios.md +0 -97
- package/src/cli/commands/probe-methods.ts +0 -108
- package/src/cli/commands/probe-validation.ts +0 -124
- package/src/cli/commands/serve.ts +0 -114
- package/src/cli/commands/sync.ts +0 -268
- package/src/cli/commands/update.ts +0 -189
- package/src/cli/commands/validate.ts +0 -34
- package/src/core/diagnostics/render-md.ts +0 -112
- package/src/core/exporter/postman.ts +0 -963
- package/src/core/generator/guide-builder.ts +0 -253
- package/src/core/meta/types.ts +0 -19
- package/src/core/parser/index.ts +0 -21
- package/src/core/runner/execute-run.ts +0 -132
- package/src/core/runner/index.ts +0 -12
- package/src/core/sync/spec-differ.ts +0 -38
- package/src/web/data/collection-state.ts +0 -362
- package/src/web/routes/api.ts +0 -314
- package/src/web/routes/dashboard.ts +0 -350
- package/src/web/routes/runs.ts +0 -64
- package/src/web/schemas.ts +0 -121
- package/src/web/server.ts +0 -134
- package/src/web/static/htmx.min.cjs +0 -1
- package/src/web/static/style.css +0 -1148
- package/src/web/views/endpoints-tab.ts +0 -174
- package/src/web/views/explorer-tab.ts +0 -402
- package/src/web/views/health-strip.ts +0 -92
- package/src/web/views/layout.ts +0 -48
- package/src/web/views/results.ts +0 -210
- package/src/web/views/runs-tab.ts +0 -126
- package/src/web/views/suites-tab.ts +0 -181
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { resolve } from "path";
|
|
2
|
+
import type { SourceMetadata } from "../parser/types.ts";
|
|
2
3
|
|
|
3
4
|
// ──────────────────────────────────────────────
|
|
4
5
|
// Utility functions (moved from skeleton.ts)
|
|
5
6
|
// ──────────────────────────────────────────────
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
function isRelativeUrl(url: string): boolean {
|
|
8
9
|
return url.startsWith("/") && !url.includes("://");
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
function resolveSpecPath(specPath: string): string {
|
|
12
13
|
if (specPath.startsWith("http://") || specPath.startsWith("https://")) {
|
|
13
14
|
return specPath;
|
|
14
15
|
}
|
|
15
16
|
return resolve(specPath);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
function sanitizeEnvName(name: string): string {
|
|
19
20
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -25,6 +26,7 @@ export function sanitizeEnvName(name: string): string {
|
|
|
25
26
|
|
|
26
27
|
export interface RawStep {
|
|
27
28
|
name: string;
|
|
29
|
+
source?: SourceMetadata;
|
|
28
30
|
[methodKey: string]: unknown;
|
|
29
31
|
expect: {
|
|
30
32
|
status?: number | number[];
|
|
@@ -37,6 +39,7 @@ export interface RawSuite {
|
|
|
37
39
|
name: string;
|
|
38
40
|
setup?: boolean;
|
|
39
41
|
tags?: string[];
|
|
42
|
+
source?: SourceMetadata;
|
|
40
43
|
folder?: string;
|
|
41
44
|
fileStem?: string;
|
|
42
45
|
base_url?: string;
|
|
@@ -66,11 +69,20 @@ export function serializeSuite(suite: RawSuite): string {
|
|
|
66
69
|
lines.push(` ${hk}: ${yamlScalar(String(hv))}`);
|
|
67
70
|
}
|
|
68
71
|
}
|
|
72
|
+
if (suite.source && Object.keys(suite.source).length > 0) {
|
|
73
|
+
lines.push("source:");
|
|
74
|
+
serializeValue(suite.source, 1, lines);
|
|
75
|
+
}
|
|
69
76
|
lines.push("tests:");
|
|
70
77
|
|
|
71
78
|
for (const test of suite.tests) {
|
|
72
79
|
lines.push(` - name: ${yamlScalar(test.name)}`);
|
|
73
80
|
|
|
81
|
+
if (test.source && Object.keys(test.source).length > 0) {
|
|
82
|
+
lines.push(" source:");
|
|
83
|
+
serializeValue(test.source, 3, lines);
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
// Write method-as-key (the shorthand)
|
|
75
87
|
for (const method of ["GET", "POST", "PUT", "PATCH", "DELETE"]) {
|
|
76
88
|
if (method in test) {
|
|
@@ -92,6 +104,27 @@ export function serializeSuite(suite: RawSuite): string {
|
|
|
92
104
|
serializeValue(test.json, 3, lines);
|
|
93
105
|
}
|
|
94
106
|
|
|
107
|
+
// ARV-149: form body (application/x-www-form-urlencoded). The runner
|
|
108
|
+
// serialises this via URLSearchParams; values are flat strings with
|
|
109
|
+
// bracket notation for nested fields (e.g. `address[line1]`).
|
|
110
|
+
//
|
|
111
|
+
// ARV-162 (round-08 F19): form values are ALWAYS strings on the wire —
|
|
112
|
+
// x-www-form-urlencoded has no native numbers/bools/nulls. YAML parsing
|
|
113
|
+
// `phone: +1234567890` or `width: 12.5` as int/float makes `zond check
|
|
114
|
+
// tests` reject the suite ("expected string, received number"), and
|
|
115
|
+
// `zond run` silently skipped 21/68 generated Stripe suites this way.
|
|
116
|
+
// Force-quote every value regardless of shape; key still uses yamlScalar
|
|
117
|
+
// because bracket keys (`address[line1]`) need quoting too.
|
|
118
|
+
if (test.form !== undefined && typeof test.form === "object" && test.form !== null) {
|
|
119
|
+
const formEntries = Object.entries(test.form as Record<string, unknown>);
|
|
120
|
+
if (formEntries.length > 0) {
|
|
121
|
+
lines.push(" form:");
|
|
122
|
+
for (const [fk, fv] of formEntries) {
|
|
123
|
+
lines.push(` ${yamlScalar(fk)}: "${escapeYamlDoubleQuoted(String(fv))}"`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
95
128
|
// query
|
|
96
129
|
if (test.query) {
|
|
97
130
|
lines.push(" query:");
|
|
@@ -151,7 +184,10 @@ export function serializeSuite(suite: RawSuite): string {
|
|
|
151
184
|
lines.push(` ${rk}:`);
|
|
152
185
|
serializeValue(rv, 6, lines);
|
|
153
186
|
} else {
|
|
154
|
-
|
|
187
|
+
// Preserve scalar types — `not_equals: true` (boolean) must not
|
|
188
|
+
// become `not_equals: "true"` (string), or assertions silently
|
|
189
|
+
// mistype against real boolean fields.
|
|
190
|
+
lines.push(` ${rk}: ${formatInlineValue(rv)}`);
|
|
155
191
|
}
|
|
156
192
|
}
|
|
157
193
|
}
|
|
@@ -182,16 +218,24 @@ function serializeValue(value: unknown, indent: number, lines: string[]): void {
|
|
|
182
218
|
if (entries.length > 0) {
|
|
183
219
|
const [firstKey, firstVal] = entries[0]!;
|
|
184
220
|
if (typeof firstVal === "object" && firstVal !== null) {
|
|
185
|
-
|
|
186
|
-
|
|
221
|
+
if (isEmptyContainer(firstVal)) {
|
|
222
|
+
lines.push(`${prefix}- ${firstKey}: ${Array.isArray(firstVal) ? "[]" : "{}"}`);
|
|
223
|
+
} else {
|
|
224
|
+
lines.push(`${prefix}- ${firstKey}:`);
|
|
225
|
+
serializeValue(firstVal, indent + 1, lines);
|
|
226
|
+
}
|
|
187
227
|
} else {
|
|
188
228
|
lines.push(`${prefix}- ${firstKey}: ${formatInlineValue(firstVal)}`);
|
|
189
229
|
}
|
|
190
230
|
for (let i = 1; i < entries.length; i++) {
|
|
191
231
|
const [k, v] = entries[i]!;
|
|
192
232
|
if (typeof v === "object" && v !== null) {
|
|
193
|
-
|
|
194
|
-
|
|
233
|
+
if (isEmptyContainer(v)) {
|
|
234
|
+
lines.push(`${prefix} ${k}: ${Array.isArray(v) ? "[]" : "{}"}`);
|
|
235
|
+
} else {
|
|
236
|
+
lines.push(`${prefix} ${k}:`);
|
|
237
|
+
serializeValue(v, indent + 1, lines);
|
|
238
|
+
}
|
|
195
239
|
} else {
|
|
196
240
|
lines.push(`${prefix} ${k}: ${formatInlineValue(v)}`);
|
|
197
241
|
}
|
|
@@ -209,8 +253,12 @@ function serializeValue(value: unknown, indent: number, lines: string[]): void {
|
|
|
209
253
|
if (typeof value === "object") {
|
|
210
254
|
for (const [key, val] of Object.entries(value as Record<string, unknown>)) {
|
|
211
255
|
if (typeof val === "object" && val !== null) {
|
|
212
|
-
|
|
213
|
-
|
|
256
|
+
if (isEmptyContainer(val)) {
|
|
257
|
+
lines.push(`${prefix}${key}: ${Array.isArray(val) ? "[]" : "{}"}`);
|
|
258
|
+
} else {
|
|
259
|
+
lines.push(`${prefix}${key}:`);
|
|
260
|
+
serializeValue(val, indent + 1, lines);
|
|
261
|
+
}
|
|
214
262
|
} else {
|
|
215
263
|
lines.push(`${prefix}${key}: ${formatInlineValue(val)}`);
|
|
216
264
|
}
|
|
@@ -218,12 +266,32 @@ function serializeValue(value: unknown, indent: number, lines: string[]): void {
|
|
|
218
266
|
}
|
|
219
267
|
}
|
|
220
268
|
|
|
269
|
+
/** Empty `{}` / `[]` written as a bare `key:` (no value, no children) is
|
|
270
|
+
* re-parsed by YAML as `null` — sending `null` for an `object`-typed field
|
|
271
|
+
* guarantees 422 against strict APIs. Emit inline flow form to preserve type. */
|
|
272
|
+
function isEmptyContainer(val: unknown): boolean {
|
|
273
|
+
if (Array.isArray(val)) return val.length === 0;
|
|
274
|
+
if (typeof val === "object" && val !== null) return Object.keys(val).length === 0;
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
221
278
|
function formatInlineValue(val: unknown): string {
|
|
222
279
|
if (val === null || val === undefined) return "null";
|
|
223
280
|
if (typeof val === "string") return yamlScalar(val);
|
|
224
281
|
return String(val);
|
|
225
282
|
}
|
|
226
283
|
|
|
284
|
+
/** ARV-62 (feedback round-01 / F3): security probes emit attack payloads
|
|
285
|
+
* with raw CRLF (`crlf:`, header-injection) and other control bytes.
|
|
286
|
+
* When written into a double-quoted YAML scalar these *must* be escaped
|
|
287
|
+
* (`\r` / `\n` / `\t` / `\xNN`) — emitting the raw byte produces YAML
|
|
288
|
+
* that the parser rejects with "bad indentation of a mapping entry"
|
|
289
|
+
* (`zond run` then fails the whole suite at load-time before sending a
|
|
290
|
+
* single request). The check also covers `\r`, `\t`, `\x00–\x1f`, and
|
|
291
|
+
* `\x7f` so any other control byte that sneaks into a payload survives
|
|
292
|
+
* the YAML round-trip. */
|
|
293
|
+
// eslint-disable-next-line no-control-regex
|
|
294
|
+
const CONTROL_BYTE_RE = /[\x00-\x1f\x7f]/;
|
|
227
295
|
function yamlScalar(value: string): string {
|
|
228
296
|
if (
|
|
229
297
|
value === "" ||
|
|
@@ -232,7 +300,6 @@ function yamlScalar(value: string): string {
|
|
|
232
300
|
value === "null" ||
|
|
233
301
|
value.includes(":") ||
|
|
234
302
|
value.includes("#") ||
|
|
235
|
-
value.includes("\n") ||
|
|
236
303
|
value.includes("'") ||
|
|
237
304
|
value.includes('"') ||
|
|
238
305
|
value.includes("{") ||
|
|
@@ -245,9 +312,32 @@ function yamlScalar(value: string): string {
|
|
|
245
312
|
value.startsWith("%") ||
|
|
246
313
|
value.startsWith("@") ||
|
|
247
314
|
value.startsWith("`") ||
|
|
248
|
-
/^\d+$/.test(value)
|
|
315
|
+
/^\d+$/.test(value) ||
|
|
316
|
+
CONTROL_BYTE_RE.test(value)
|
|
249
317
|
) {
|
|
250
|
-
return `"${value
|
|
318
|
+
return `"${escapeYamlDoubleQuoted(value)}"`;
|
|
251
319
|
}
|
|
252
320
|
return value;
|
|
253
321
|
}
|
|
322
|
+
|
|
323
|
+
function escapeYamlDoubleQuoted(value: string): string {
|
|
324
|
+
let out = "";
|
|
325
|
+
for (let i = 0; i < value.length; i++) {
|
|
326
|
+
const ch = value[i]!;
|
|
327
|
+
const code = ch.charCodeAt(0);
|
|
328
|
+
if (ch === "\\") { out += "\\\\"; continue; }
|
|
329
|
+
if (ch === '"') { out += '\\"'; continue; }
|
|
330
|
+
if (ch === "\n") { out += "\\n"; continue; }
|
|
331
|
+
if (ch === "\r") { out += "\\r"; continue; }
|
|
332
|
+
if (ch === "\t") { out += "\\t"; continue; }
|
|
333
|
+
if (code < 0x20 || code === 0x7f) {
|
|
334
|
+
// YAML 1.2 allows \xNN for any byte in (0..0xff); use this for any
|
|
335
|
+
// control character that doesn't have a dedicated short escape
|
|
336
|
+
// (covers \x00–\x1f minus the three handled above, plus DEL).
|
|
337
|
+
out += "\\x" + code.toString(16).padStart(2, "0");
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
out += ch;
|
|
341
|
+
}
|
|
342
|
+
return out;
|
|
343
|
+
}
|