

























@@ -32,6 +32,47 @@ function createBackupManifest(assetArchivePath: string, archiveRoot = TEST_ARCHI
3232};
3333}
343435+async function createArchiveWithManifestContent(
36+options: {
37+tempPrefix: string;
38+manifestContent: string;
39+payloadArchivePath?: string;
40+},
41+run: (archivePath: string) => Promise<void>,
42+) {
43+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), options.tempPrefix));
44+const archivePath = path.join(tempDir, "broken.tar.gz");
45+const manifestPath = path.join(tempDir, "manifest.json");
46+const payloadPath = path.join(tempDir, "payload.txt");
47+const payloadArchivePath =
48+options.payloadArchivePath ?? `${TEST_ARCHIVE_ROOT}/payload/posix/tmp/.openclaw/payload.txt`;
49+try {
50+await fs.writeFile(manifestPath, options.manifestContent, "utf8");
51+await fs.writeFile(payloadPath, "payload\n", "utf8");
52+await tar.c(
53+{
54+file: archivePath,
55+gzip: true,
56+portable: true,
57+preservePaths: true,
58+onWriteEntry: (entry) => {
59+if (entry.path === manifestPath) {
60+entry.path = `${TEST_ARCHIVE_ROOT}/manifest.json`;
61+return;
62+}
63+if (entry.path === payloadPath) {
64+entry.path = payloadArchivePath;
65+}
66+},
67+},
68+[manifestPath, payloadPath],
69+);
70+await run(archivePath);
71+} finally {
72+await fs.rm(tempDir, { recursive: true, force: true });
73+}
74+}
75+3576async function withBrokenArchiveFixture(
3677options: {
3778tempPrefix: string;
@@ -196,6 +237,24 @@ describe("backupVerifyCommand", () => {
196237}
197238});
198239240+it("reports malformed manifest JSON without leaking parser internals", async () => {
241+await createArchiveWithManifestContent(
242+{
243+tempPrefix: "openclaw-backup-bad-manifest-json-",
244+manifestContent: '{"schemaVersion":1,',
245+},
246+async (archivePath) => {
247+const runtime = createBackupVerifyRuntime();
248+await expect(backupVerifyCommand(runtime, { archive: archivePath })).rejects.toThrow(
249+/^Backup manifest is not valid JSON\.$/u,
250+);
251+await expect(backupVerifyCommand(runtime, { archive: archivePath })).rejects.not.toThrow(
252+/position|Unexpected|Expected|SyntaxError/u,
253+);
254+},
255+);
256+});
257+199258it("rejects unsafe archive paths", async () => {
200259for (const { tempPrefix, archivePath, error } of [
201260{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。