Files
rea/src/browser/JavaScriptRuntimeScope.ts
T
morluto 5f27e02fae refactor(runtime): name all silent catch causes without changing diagnostics (#637)
* refactor(domain): split error taxonomy into thematic modules

Move each error family out of domain/errors.ts into its own module
(base, core, provider selection/adapter, browser observation,
artifact, evidence, unknown registry, hopper, configuration) and
relocate AnalysisErrorProjection to analysisErrorProjection.
Migrate all 162 importers to the owning modules and delete
errors.ts. No re-export shims; behavior is unchanged.

* refactor(runtime): name all silent catch causes without changing diagnostics

Bind cause in every bare catch and unhandled rejection across src/.
Real failures preserve the cause for logging; best-effort cleanup
carries an explicit comment. Caller-visible and Evidence strings are
byte-identical to before.
2026-10-06 05:58:46 +08:00

98 lines
3.1 KiB
TypeScript

import { pathToFileURL } from "node:url";
import {
browserOriginSchema,
sanitizeBrowserUrl,
} from "../domain/browserObservation.js";
import type {
JavaScriptRuntimeLocation,
JavaScriptRuntimeTargetLocation,
} from "../domain/javascriptRuntimeObservation.js";
import { authorizedElectronFile } from "./ElectronFileScope.js";
export type RuntimeLocationDecision =
| { readonly allowed: true; readonly location: JavaScriptRuntimeLocation }
| {
readonly allowed: false;
readonly reason: "unsupported_location";
};
/** Resolve a protocol location exposed by the explicitly selected Inspector endpoint. */
export const authorizeRuntimeLocation = async (
value: string,
): Promise<RuntimeLocationDecision> => {
if (value.startsWith("node:") && value.length > 0)
return {
allowed: true,
location: { kind: "builtin", specifier: value },
};
if (value.startsWith("file:")) {
const filePath = await authorizedElectronFile(value);
return filePath === undefined
? { allowed: false, reason: "unsupported_location" }
: { allowed: true, location: { kind: "file", file_path: filePath } };
}
let url: URL;
try {
url = new URL(value);
} catch (cause: unknown) {
// Non-URL input is outside the authorized runtime scope.
void cause;
return { allowed: false, reason: "unsupported_location" };
}
if (!["http:", "https:"].includes(url.protocol))
return { allowed: false, reason: "unsupported_location" };
const parsedOrigin = browserOriginSchema.safeParse(url.origin);
if (!parsedOrigin.success)
return { allowed: false, reason: "unsupported_location" };
return {
allowed: true,
location: {
kind: "url",
origin: parsedOrigin.data,
sanitized_url: sanitizeBrowserUrl(value).url,
},
};
};
/** Interpret Node's lossy discovery metadata separately from scriptParsed URLs. */
export const authorizeRuntimeTargetLocation = async (
value: string,
context: { readonly type: string; readonly product: string },
): Promise<
| {
readonly allowed: true;
readonly location: JavaScriptRuntimeTargetLocation;
}
| { readonly allowed: false; readonly reason: "unsupported_location" }
> => {
if (
context.type !== "node" ||
!/^node\.js\/v/iu.test(context.product) ||
!value.startsWith("file://")
)
return authorizeRuntimeLocation(value);
const reportedPath = value.slice("file://".length);
if (!reportedPath.startsWith("/") && !/^[a-z]:[/\\_]/iu.test(reportedPath))
return authorizeRuntimeLocation(value);
// Node concatenates the pathname, then replaces quotes and backslashes with
// underscores. A remaining underscore cannot prove which pathname was used.
if (process.platform !== "win32" && !/[_"\\]/u.test(reportedPath)) {
if (reportedPath.startsWith("/")) {
const decision = await authorizeRuntimeLocation(
pathToFileURL(reportedPath).href,
);
if (decision.allowed) return decision;
}
}
return {
allowed: true,
location: {
kind: "unresolved",
reported_url: value,
reason: "unverifiable-file-location",
},
};
};