Files
rea/src/wasm/WabtOutput.ts
T
morluto 6fb3a345fe fix(analysis): preserve producer identity and partial evidence
Reconcile Mach-O UUID observations without hiding malformed or conflicting sources. Require source-map identity digests to be supported by consistent observations. Keep truncated Android manifests as partial raw evidence, validate WABT section framing, and reject missing JEB inventories or error-marked success replies.

Consolidate native UUID parsing and simplify capacity and malformed-input fixtures. Focused source and public workflow checks pass, including native arm64e inspection and real WABT framing probes; a successful real JEB GUI project opening remains unverified.
2026-10-12 12:37:00 +08:00

77 lines
2.7 KiB
TypeScript

/** WABT prints names with %.*s, so an embedded NUL ends their display. */
const wabtDisplayedName = (name: string | undefined): string | undefined =>
name?.split("\0", 1)[0];
/** Frame WABT's raw custom names using the ordered names from its escaped WAT output. */
export const parseWabtOutput = (
text: string,
artifactBytes: number,
customSectionNames: readonly string[],
debugModuleNames: readonly string[] = [],
) => {
const preamble =
"\nmodule.wasm:\tfile format wasm 0x1\n" +
debugModuleNames
.map((name) => `module name: <${wabtDisplayedName(name)}>\n`)
.join("");
const prefix = `${preamble}\nSections:\n\n`;
if (!text.startsWith(prefix))
throw new Error("Unsupported WABT section header format.");
let offset = prefix.length;
let customIndex = 0;
const sections = [];
while (!text.startsWith("\nSection Details:\n", offset)) {
const match =
/^[ \t]*(\w+) start=0x([\da-f]+) end=0x([\da-f]+) \(size=0x([\da-f]+)\) /u.exec(
text.slice(offset),
);
if (match === null) throw new Error("Unrecognized WABT section row.");
offset += match[0].length;
let description: string;
if (match[1] === "Custom") {
const name = wabtDisplayedName(customSectionNames[customIndex++]);
if (name === undefined || !text.startsWith(`"${name}"\n`, offset))
throw new Error(
"WABT custom-section header does not match decoded WAT.",
);
description = `"${name}"`;
offset += description.length + 1;
} else {
const end = text.indexOf("\n", offset);
if (end < 0) throw new Error("Incomplete WABT section row.");
description = text.slice(offset, end).trim();
offset = end + 1;
}
const section = {
kind: match[1] ?? "",
start: Number.parseInt(match[2] ?? "", 16),
end: Number.parseInt(match[3] ?? "", 16),
bytes: Number.parseInt(match[4] ?? "", 16),
description,
};
const sectionHeaderBytes = section.start - (sections.at(-1)?.end ?? 8);
if (
section.end > artifactBytes ||
section.end - section.start !== section.bytes ||
sectionHeaderBytes < 2 ||
sectionHeaderBytes > 6
)
throw new Error("WABT section ranges do not match selected artifact.");
sections.push(section);
}
if (customIndex !== customSectionNames.length)
throw new Error(
"WABT custom-section inventory does not match decoded WAT.",
);
if ((sections.at(-1)?.end ?? 8) !== artifactBytes)
throw new Error(
"WABT section output does not cover the selected artifact.",
);
const separator = offset + 1;
return {
sections,
headers: text.slice(0, separator),
details: `${preamble.slice(1)}\n${text.slice(separator)}`,
};
};