feat(browser): trace captured website source-map locations (#897)

* refactor(browser): share captured-script acquisition boundaries

Reuse stable capture/source acquisition and identity validation for module
and source-map analysis. Preserve host I/O, malformed input, unavailable
source and digest mismatch diagnostics.

Refs #851

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* feat(browser): trace captured website source-map locations

Refs #851. Preserve selected capture/map byte identity, duplicate mappings and embedded source evidence through the pinned upstream codec.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* test(browser): verify source-map artifacts and synchronize generated contracts

Cover byte-preserving map reads and actionable filesystem constraints. Refresh generated public schemas and metadata on current main.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* fix(browser): preserve inert source-map URL context

The selected URL is local codec context and grants no transport authority. Preserve its reported text and verify normal/userinfo contexts through real public interfaces, alongside independent manifest byte identity.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
This commit is contained in:
ysopwnable authored and GitHub committed 2026-10-07 16:21:18 +08:00
1 parent 4801a24089
commit f430c416c7
58 files changed
+3420 -282

No files matched your search

+10 -33
View File
@@ -1,5 +1,4 @@
import { isAbsolute } from "node:path";
import { createHash } from "node:crypto";
import { z } from "zod";
import type { AnalysisError } from "../domain/analysisErrorBase.js";
import {
@@ -24,7 +23,11 @@ import {
type WebModuleResolution,
type WebModuleTraceResult,
} from "../domain/webModuleTrace.js";
import { webScriptExportManifestSchema } from "../domain/webScriptExport.js";
import { selectedWebScriptArtifactsSchema } from "../domain/webScriptArtifacts.js";
import {
capturedWebManifestIdentity,
validateSelectedWebScript,
} from "./ValidateSelectedWebScript.js";
import type { ExecutionOptions } from "./AnalysisProvider.js";
import type {
WebModuleArtifactPort,
@@ -33,11 +36,7 @@ import type {
} from "./WebModulePorts.js";
const OPERATION = "trace_web_module_imports";
const artifactsSchema = z.strictObject({
manifest: webScriptExportManifestSchema,
manifestFile: webModuleFileSchema,
sourceFile: webModuleFileSchema,
source: z.string(),
const artifactsSchema = selectedWebScriptArtifactsSchema.extend({
importMap: z
.strictObject({
file: webModuleFileSchema,
@@ -125,23 +124,9 @@ export class WebModuleTraceService {
"Artifact port changed the selected import-map identity/context.",
),
);
const selected = data.manifest.scripts[input.script_index];
if (
selected === undefined ||
selected.content.state !== "exported" ||
selected.content.sha256 !== data.sourceFile.sha256 ||
selected.content.bytes !== data.sourceFile.bytes ||
Buffer.byteLength(data.source) !== data.sourceFile.bytes ||
createHash("sha256").update(data.source, "utf8").digest("hex") !==
data.sourceFile.sha256 ||
data.manifestFile.path !== input.manifest_path
)
return err(
new AnalysisOutputError(
OPERATION,
"Artifact port did not bind the selected source and manifest identities.",
),
);
const bound = validateSelectedWebScript(input, data, OPERATION);
if (!bound.ok) return bound;
const selected = bound.value;
const importerUrl = input.importer_url ?? selected.url;
const importerAdmission = admitContext(importerUrl, ["importer_url"]);
if (!importerAdmission.ok) return importerAdmission;
@@ -215,15 +200,7 @@ export class WebModuleTraceService {
});
}
const normalized = webModuleTraceResultSchema.safeParse({
manifest: {
...data.manifestFile,
reported_output_directory: data.manifest.output_directory,
capture_sha256: data.manifest.capture_sha256,
capture_path: data.manifest.capture_path,
capture_kind: data.manifest.capture_kind,
capture_completeness: data.manifest.capture_completeness,
source_evidence_id: data.manifest.source_evidence_id,
},
manifest: capturedWebManifestIdentity(data),
source: {
...data.sourceFile,
script_index: input.script_index,