Files
reverse-engineering-anything/tests/boundary/filesystem/ghidraLanguageCatalogEncoding.test.ts
T
Kaoru0822andClaude Opus 5.5 292fd532c9 fix(ghidra): retry retained language catalog closes before admission (#6210)
The language catalog kept failed .ldefs closes in a module-global set that
only a later catalog read retried, and the provider returned a cleanup error
bound to no owner. Registry admission retries provider close before profile
resolution, so the retained handle was never retried and the original error
stayed incomplete forever.

Read definitions through the provider's caller-owned inspection scope with the
shared regular-file owner used by the seed and extension readers. A failed
close is retained as an exact lease in that scope, provider close and the next
catalog admission retry it, and the translated error is bound to the lease so
confirmed release reopens the same registry.

Fixes #6133

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:10:25 -07:00

72 lines
2.3 KiB
TypeScript

import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { ArtifactResourceScope } from "../../../src/artifacts/ArtifactResourceScope.js";
import { readGhidraLanguageCatalog } from "../../../src/ghidra/GhidraLanguageCatalog.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const xml =
'<language_definitions><language id="toy:LE:32:default" description="�"><compiler id="default"/></language></language_definitions>';
it.each([
{ name: "literal U+FFFD", bytes: Buffer.from(xml), expectedError: null },
{
name: "UTF-8 BOM",
bytes: Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), Buffer.from(xml)]),
expectedError: null,
},
{
name: "UTF-16 BOM/declaration",
bytes: Buffer.concat([
Buffer.from([0xff, 0xfe]),
Buffer.from('<?xml version="1.0" encoding="UTF-16"?>' + xml, "utf16le"),
]),
expectedError: null,
},
{
name: "malformed UTF-8",
bytes: Buffer.concat([
Buffer.from('<language_definitions description="'),
Buffer.from([0xff]),
Buffer.from(
'"><language id="toy:LE:32:default"/></language_definitions>',
),
]),
expectedError: "invalid-bytes",
},
{
name: "encoding mismatch",
bytes: Buffer.from('<?xml version="1.0" encoding="UTF-16"?>' + xml),
expectedError: "encoding-mismatch",
},
{
name: "malformed XML alongside U+FFFD",
bytes: Buffer.from(xml.replace('id="toy:LE:32:default"', "id=unquoted")),
expectedError: "malformed",
},
])(
"reads $name at the language-file byte boundary",
async ({ bytes, expectedError }) => {
const root = await createTestTempDirectory("rea-ghidra-xml-encoding-");
const directory = join(
root,
"Ghidra",
"Processors",
"toy",
"data",
"languages",
);
await mkdir(directory, { recursive: true });
await writeFile(join(directory, "toy.ldefs"), bytes);
if (expectedError === null)
expect(
await readGhidraLanguageCatalog(root, new ArtifactResourceScope()),
).toEqual([{ id: "toy:LE:32:default", compilerSpecIds: ["default"] }]);
else
await expect(
readGhidraLanguageCatalog(root, new ArtifactResourceScope()),
).rejects.toMatchObject({
reason: expectedError,
});
},
);