mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
fix(javascript): authenticate transfer bytes before decoding (#10078)
fix(javascript): authenticate raw analysis transfer bytes
This commit is contained in:
1 parent
d1895841a4
commit
a14a0c20ed
3 files changed
+242
-16
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { join } from "node:path";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { analyzeJavaScriptSemantics } from "../../../src/domain/javascript/javascriptSemanticAnalysis.js";
|
||||
@@ -136,3 +137,57 @@ it("rejects authenticated worker rows beyond the offered node or unknown capacit
|
||||
}),
|
||||
).rejects.toThrow("nodes exceed the offered capacity");
|
||||
});
|
||||
|
||||
it("authenticates physical transfer bytes and refuses malformed UTF-8 without replacing source facts", async () => {
|
||||
const root = await createTestTempDirectory("rea-js-transfer-utf8-");
|
||||
const graph = `${JSON.stringify({ kind: "projection-header", value: { roots: [], nodesTruncated: false } })}\n`;
|
||||
const moduleRecord = (limitation: string) =>
|
||||
`${JSON.stringify({
|
||||
kind: "module-header",
|
||||
value: {
|
||||
coverage: { status: "complete", omittedCount: 0 },
|
||||
limitations: [limitation],
|
||||
},
|
||||
})}\n`;
|
||||
const prefixBytes =
|
||||
Buffer.byteLength(graph) + moduleRecord("MARK").indexOf("MARK");
|
||||
const limitation = `${"a".repeat(65535 - prefixBytes)}😀�`;
|
||||
const module = moduleRecord(limitation);
|
||||
const valid = Buffer.from(graph + module);
|
||||
expect(valid.indexOf(Buffer.from("😀"))).toBe(65535);
|
||||
const descriptor = {
|
||||
bytes: valid.length,
|
||||
sha256: createHash("sha256").update(valid).digest("hex"),
|
||||
records: 2,
|
||||
module_bytes: Buffer.byteLength(module),
|
||||
module_records: 1,
|
||||
graph_bytes: Buffer.byteLength(graph),
|
||||
graph_records: 1,
|
||||
};
|
||||
const path = join(root, "records");
|
||||
const source = { path: "app.mjs", sha256: "a".repeat(64) };
|
||||
await writeFile(path, valid);
|
||||
expect(
|
||||
(await readJavaScriptAnalysisTransfer(path, descriptor, source)).module
|
||||
.limitations,
|
||||
).toEqual([limitation]);
|
||||
const malformed = Buffer.from(valid);
|
||||
// This incomplete four-byte sequence becomes one three-byte replacement
|
||||
// under permissive decoding, so a decoded-text digest can conceal the change.
|
||||
Buffer.from([0xf0, 0x90, 0x80]).copy(
|
||||
malformed,
|
||||
malformed.indexOf(Buffer.from("�")),
|
||||
);
|
||||
const physicalDigest = createHash("sha256").update(malformed).digest("hex");
|
||||
expect(physicalDigest).not.toBe(descriptor.sha256);
|
||||
await writeFile(path, malformed);
|
||||
for (const sha256 of [descriptor.sha256, physicalDigest]) {
|
||||
await expect(
|
||||
readJavaScriptAnalysisTransfer(
|
||||
path,
|
||||
{ ...descriptor, sha256 },
|
||||
source,
|
||||
).then(() => "accepted"),
|
||||
).rejects.toMatchObject({ code: "ERR_ENCODING_INVALID_ENCODED_DATA" });
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user