mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:10:45 +00:00
fix: reject malformed stability bundles
This commit is contained in:
@@ -187,4 +187,64 @@ describe("diagnostic stability bundles", () => {
|
||||
"Unsupported stability bundle version",
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects malformed bundle snapshots before returning them", () => {
|
||||
const baseBundle = {
|
||||
version: 1,
|
||||
generatedAt: "2026-04-22T12:00:00.000Z",
|
||||
reason: "gateway.restart_startup_failed",
|
||||
process: {
|
||||
pid: 123,
|
||||
platform: "darwin",
|
||||
arch: "arm64",
|
||||
node: "24.14.1",
|
||||
uptimeMs: 1000,
|
||||
},
|
||||
host: {
|
||||
hostname: "<redacted-hostname>",
|
||||
},
|
||||
snapshot: {
|
||||
generatedAt: "2026-04-22T12:00:00.000Z",
|
||||
capacity: 1000,
|
||||
count: 1,
|
||||
dropped: 0,
|
||||
events: [{ seq: 1, ts: 1, type: "webhook.received" }],
|
||||
summary: { byType: { "webhook.received": 1 } },
|
||||
},
|
||||
};
|
||||
const cases = [
|
||||
{
|
||||
name: "malformed-event",
|
||||
bundle: {
|
||||
...baseBundle,
|
||||
snapshot: {
|
||||
...baseBundle.snapshot,
|
||||
events: [{ type: "webhook.received", ts: 1 }],
|
||||
},
|
||||
},
|
||||
error: "snapshot.events[0].seq",
|
||||
},
|
||||
{
|
||||
name: "null-summary",
|
||||
bundle: {
|
||||
...baseBundle,
|
||||
snapshot: {
|
||||
...baseBundle.snapshot,
|
||||
summary: null,
|
||||
},
|
||||
},
|
||||
error: "snapshot.summary",
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of cases) {
|
||||
const file = path.join(tempDir, `${testCase.name}.json`);
|
||||
fs.writeFileSync(file, `${JSON.stringify(testCase.bundle, null, 2)}\n`, "utf8");
|
||||
|
||||
const result = readDiagnosticStabilityBundleFileSync(file);
|
||||
|
||||
expect(result.status).toBe("failed");
|
||||
expect(result.status === "failed" ? String(result.error) : "").toContain(testCase.error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,6 +161,49 @@ function readObject(value: unknown, label: string): Record<string, unknown> {
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function readNumber(value: unknown, label: string): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||
throw new Error(`Invalid stability bundle: ${label} must be a finite number`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function readOptionalNumber(value: unknown, label: string): number | undefined {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return readNumber(value, label);
|
||||
}
|
||||
|
||||
function readString(value: unknown, label: string): string {
|
||||
if (typeof value !== "string") {
|
||||
throw new Error(`Invalid stability bundle: ${label} must be a string`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function readStabilitySnapshot(value: unknown): DiagnosticStabilitySnapshot {
|
||||
const snapshot = readObject(value, "snapshot");
|
||||
readString(snapshot.generatedAt, "snapshot.generatedAt");
|
||||
readNumber(snapshot.capacity, "snapshot.capacity");
|
||||
readNumber(snapshot.count, "snapshot.count");
|
||||
readNumber(snapshot.dropped, "snapshot.dropped");
|
||||
readOptionalNumber(snapshot.firstSeq, "snapshot.firstSeq");
|
||||
readOptionalNumber(snapshot.lastSeq, "snapshot.lastSeq");
|
||||
if (!Array.isArray(snapshot.events)) {
|
||||
throw new Error("Invalid stability bundle: snapshot.events must be an array");
|
||||
}
|
||||
for (const [index, event] of snapshot.events.entries()) {
|
||||
const record = readObject(event, `snapshot.events[${index}]`);
|
||||
readNumber(record.seq, `snapshot.events[${index}].seq`);
|
||||
readNumber(record.ts, `snapshot.events[${index}].ts`);
|
||||
readString(record.type, `snapshot.events[${index}].type`);
|
||||
}
|
||||
const summary = readObject(snapshot.summary, "snapshot.summary");
|
||||
readObject(summary.byType, "snapshot.summary.byType");
|
||||
return snapshot as DiagnosticStabilitySnapshot;
|
||||
}
|
||||
|
||||
function parseDiagnosticStabilityBundle(value: unknown): DiagnosticStabilityBundle {
|
||||
const bundle = readObject(value, "bundle");
|
||||
if (bundle.version !== DIAGNOSTIC_STABILITY_BUNDLE_VERSION) {
|
||||
@@ -171,10 +214,7 @@ function parseDiagnosticStabilityBundle(value: unknown): DiagnosticStabilityBund
|
||||
}
|
||||
readObject(bundle.process, "process");
|
||||
readObject(bundle.host, "host");
|
||||
const snapshot = readObject(bundle.snapshot, "snapshot");
|
||||
if (!Array.isArray(snapshot.events) || typeof snapshot.summary !== "object") {
|
||||
throw new Error("Invalid stability bundle: snapshot is malformed");
|
||||
}
|
||||
readStabilitySnapshot(bundle.snapshot);
|
||||
return bundle as DiagnosticStabilityBundle;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user