Skip to content

Commit 87de81a

Browse files
cxbAsDevsteipete
andauthored
Bound diagnostic config file read with size cap (#110591)
* Bound diagnostic config read with size cap * fix: use buffer.toString for readRegularFileSync result in diagnostic export * test: verify normal and oversized diagnostic config export behavior PR #110591 — Add focused regression coverage for the bounded diagnostic config read: - Test: normal config file (<8MB) produces parseOk: true in config/shape.json - Test: oversized config file (>8MB) produces parseOk: false with graceful error handling - Both tests verify the support bundle zip is still written with all expected contents * refactor(logging): clarify support export config limit Co-authored-by: 陈宪彪0668000387 <[email protected]> --------- Co-authored-by: Peter Steinberger <[email protected]>
1 parent 8ff1512 commit 87de81a

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

src/logging/diagnostic-support-export.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,4 +912,50 @@ describe("diagnostic support export", () => {
912912
expect(combined).toContain("config stat failed with token");
913913
expect(combined).toContain("Attach this zip to the bug report");
914914
});
915+
916+
it("finishes the support export when the config exceeds its read limit", async () => {
917+
const configPath = path.join(tempDir, "openclaw.json");
918+
const outputPath = path.join(tempDir, "support-oversized-config.zip");
919+
fs.writeFileSync(configPath, Buffer.alloc(8 * 1024 * 1024 + 1, "{"));
920+
921+
await writeDiagnosticSupportExport({
922+
env: {
923+
...process.env,
924+
HOME: tempDir,
925+
OPENCLAW_CONFIG_PATH: configPath,
926+
OPENCLAW_STATE_DIR: tempDir,
927+
},
928+
stateDir: tempDir,
929+
outputPath,
930+
now: new Date("2026-07-18T12:00:01.000Z"),
931+
readLogTail: async () => ({
932+
file: path.join(tempDir, "logs", "openclaw.log"),
933+
cursor: 0,
934+
size: 0,
935+
truncated: false,
936+
reset: false,
937+
lines: [],
938+
}),
939+
});
940+
941+
const entries = await readZipTextEntries(outputPath);
942+
const configShape = JSON.parse(entries["config/shape.json"] ?? "{}") as {
943+
parseOk?: boolean;
944+
error?: string;
945+
};
946+
expect(configShape.parseOk).toBe(false);
947+
expect(configShape.error).toContain("File exceeds 8388608 bytes");
948+
expect(entries["config/sanitized.json"]).toBe("null\n");
949+
expect(Object.keys(entries).toSorted()).toEqual([
950+
"config/sanitized.json",
951+
"config/shape.json",
952+
"diagnostics.json",
953+
"logs/openclaw-sanitized.jsonl",
954+
"manifest.json",
955+
"summary.md",
956+
]);
957+
958+
const combined = Object.values(entries).join("\n");
959+
expect(combined).toContain("Attach this zip to the bug report");
960+
});
915961
});

src/logging/diagnostic-support-export.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { resolveConfigPath, resolveStateDir } from "../config/paths.js";
88
import { redactConfigObject } from "../config/redact-snapshot.js";
99
import { buildConfigSchema } from "../config/schema.js";
1010
import { resolveHomeRelativePath } from "../infra/home-dir.js";
11+
import { readRegularFileSync } from "../infra/regular-file.js";
1112
import { VERSION } from "../version.js";
1213
import {
1314
readDiagnosticStabilityBundleFileSync,
@@ -38,6 +39,9 @@ const DIAGNOSTIC_SUPPORT_EXPORT_VERSION = 1;
3839

3940
const DEFAULT_LOG_LIMIT = 5000;
4041
const DEFAULT_LOG_MAX_BYTES = 1_000_000;
42+
// Support export must remain usable when the config is corrupt or unexpectedly
43+
// large. This defensive ceiling is not the product's general config-file limit.
44+
const SUPPORT_EXPORT_CONFIG_MAX_BYTES = 8 * 1024 * 1024;
4145
const SUPPORT_EXPORT_PREFIX = "openclaw-diagnostics-";
4246
const SUPPORT_EXPORT_SUFFIX = ".zip";
4347
type Awaitable<T> = T | Promise<T>;
@@ -343,7 +347,11 @@ function readConfigExport(options: {
343347
let stat: fs.Stats | undefined;
344348
try {
345349
stat = fs.statSync(options.configPath);
346-
const parsed = parseConfigJson5(fs.readFileSync(options.configPath, "utf8"));
350+
const { buffer } = readRegularFileSync({
351+
filePath: options.configPath,
352+
maxBytes: SUPPORT_EXPORT_CONFIG_MAX_BYTES,
353+
});
354+
const parsed = parseConfigJson5(buffer.toString("utf8"));
347355
if (!parsed.ok) {
348356
return {
349357
shape: configShapeReadFailure({

0 commit comments

Comments
 (0)