mirror of
https://github.com/morluto/rea
synced 2026-10-28 15:07:26 -04:00
623339a7ab
Own syntax validation and source-located DOM projection together. Preserve explicit consumer policies and remove duplicated warning-message filters from Apktool and SAZ. Validation: 92 focused checks, 3 selected fake Apktool boundary checks, check:fast and docs:check. No real Apktool or Ghidra execution. Refs #4694
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
import {
|
|
DOMParser,
|
|
normalizeLineEndings,
|
|
onErrorStopParsing,
|
|
type Document,
|
|
} from "@xmldom/xmldom";
|
|
import { SaxesParser } from "saxes";
|
|
import { err, ok, type Result } from "./result.js";
|
|
|
|
export class XmlDocumentParseError extends Error {
|
|
readonly _tag = "XmlDocumentParseError";
|
|
constructor(
|
|
readonly reason: "malformed" | "doctype",
|
|
cause?: unknown,
|
|
) {
|
|
super(
|
|
reason === "doctype"
|
|
? "XML document types are unsupported"
|
|
: `XML is not well-formed${cause instanceof Error ? `: ${cause.message}` : ""}`,
|
|
{ cause },
|
|
);
|
|
}
|
|
}
|
|
|
|
/** One boundary owns strict XML syntax and the source-located DOM projection. */
|
|
export const parseXmlDocument = (
|
|
source: string,
|
|
policy: {
|
|
readonly doctype: "allow" | "reject";
|
|
readonly lineEndings: "preserve" | "normalize";
|
|
},
|
|
): Result<Document, XmlDocumentParseError> => {
|
|
try {
|
|
const syntax = new SaxesParser({
|
|
defaultXMLVersion: "1.0",
|
|
forceXMLVersion: true,
|
|
});
|
|
syntax.on("doctype", () => {
|
|
if (policy.doctype === "reject")
|
|
throw new XmlDocumentParseError("doctype");
|
|
});
|
|
syntax.write(source).close();
|
|
// Saxes owns well-formedness outside DTDs. The DOM parser additionally
|
|
// checks DTD syntax and builds source-located nodes; its advisory warnings
|
|
// (including literal U+FFFD) are not byte-decoding or syntax failures.
|
|
return ok(
|
|
new DOMParser({
|
|
normalizeLineEndings:
|
|
policy.lineEndings === "preserve"
|
|
? (text) => text
|
|
: normalizeLineEndings,
|
|
onError: onErrorStopParsing,
|
|
}).parseFromString(source, "application/xml"),
|
|
);
|
|
} catch (cause: unknown) {
|
|
return err(
|
|
cause instanceof XmlDocumentParseError
|
|
? cause
|
|
: new XmlDocumentParseError("malformed", cause),
|
|
);
|
|
}
|
|
};
|