Files
reverse-engineering-anything/src/domain/webScriptArtifacts.ts
T
DeryFerd 499ac18011 fix(web): reject NUL bytes in web tool local path inputs
Four web tools accepted caller-selected local paths as plain strings, so
a NUL byte passed contract validation and reached filesystem calls. The
services only checked isAbsolute, and isAbsolute admits a NUL, so the
failure surfaced later as a raw ERR_INVALID_ARG_VALUE instead of a named
input constraint. Every other local-input tool already refuses NUL through
the shared local-path schema, and the existing OS-invalid-local-input
harness covers those but not these web tools.

inspect_web_network_capture.capture_path, export_web_scripts.capture_path
and output_directory, trace_web_source_location.manifest_path and
source_map.path, and trace_web_module_imports.manifest_path and
import_map.path now use localPathStringSchema. Each field already required
a nonempty string, so the advertised pattern and minLength stay the same
and only the NUL refusal is added.

Validation: a new schema test rejects NUL in all seven fields while
accepting ordinary absolute paths; the four tools are added to the
OS-invalid-local-input harness and reject NUL through the real advertised
MCP contracts; typecheck, oxlint, oxfmt, and toolSchemaValidity with a
regenerated catalog. Two webScriptExport boundary cases fail identically
with and without this change on this Windows host (symlink EPERM and
private-runtime-root authority), so they are environmental.
2026-10-11 23:32:52 +07:00

50 lines
1.6 KiB
TypeScript

import { z } from "zod";
import { digestSchema } from "./digests.js";
import { localPathStringSchema } from "./localPath.js";
import { webScriptExportManifestSchema } from "./webScriptExport.js";
/** Select one source from an existing captured-script export. */
export const selectedWebScriptInputSchema = z.strictObject({
manifest_path: localPathStringSchema.describe(
"Absolute export_web_scripts manifest path",
),
script_index: z
.number()
.int()
.min(0)
.describe("Zero-based index in manifest.scripts"),
});
export type SelectedWebScriptInput = z.output<
typeof selectedWebScriptInputSchema
>;
/** Exact identity of one local file, independently read as bytes. */
export const webArtifactFileSchema = z.strictObject({
path: z.string().min(1),
sha256: digestSchema,
bytes: z.number().int().min(0),
});
/** Artifact-port report shared by capture-derived analyses. */
export const selectedWebScriptArtifactsSchema = z.strictObject({
manifest: webScriptExportManifestSchema,
manifestFile: webArtifactFileSchema,
sourceFile: webArtifactFileSchema,
source: z.string(),
});
export type SelectedWebScriptArtifacts = z.output<
typeof selectedWebScriptArtifactsSchema
>;
/** Preserve original capture authority and completeness alongside the actual manifest identity. */
export const capturedWebManifestIdentitySchema = webArtifactFileSchema.extend({
reported_output_directory: z.string(),
...webScriptExportManifestSchema.pick({
capture_path: true,
capture_sha256: true,
capture_kind: true,
capture_completeness: true,
source_evidence_id: true,
}).shape,
});