mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
fix(cli): guard secrets plan JSON.parse against malformed input (#109721)
* fix(cli): guard secrets plan JSON.parse against malformed input readPlanFile reads a user-specified file and parses it with JSON.parse without a try-catch. A malformed or corrupted plan file produces a raw SyntaxError that is not user-actionable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: tighten malformed secrets plan proof Co-authored-by: 丁宇婷0668001435 <ding.yuting@xydigit.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -125,12 +125,15 @@ function createSecretsApplyResult(options?: {
|
||||
};
|
||||
}
|
||||
|
||||
async function withPlanFile(run: (planPath: string) => Promise<void>) {
|
||||
async function withPlanFile(
|
||||
run: (planPath: string) => Promise<void>,
|
||||
contents = `${JSON.stringify(createManualSecretsPlan())}\n`,
|
||||
) {
|
||||
const planPath = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-secrets-cli-test-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,
|
||||
);
|
||||
await fs.writeFile(planPath, `${JSON.stringify(createManualSecretsPlan())}\n`, "utf8");
|
||||
await fs.writeFile(planPath, contents, "utf8");
|
||||
try {
|
||||
await run(planPath);
|
||||
} finally {
|
||||
@@ -385,6 +388,17 @@ describe("secrets CLI", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shows a user-friendly error when the secrets plan file is malformed JSON", async () => {
|
||||
await withPlanFile(async (planPath) => {
|
||||
await expect(
|
||||
createProgram().parseAsync(["secrets", "apply", "--from", planPath], { from: "user" }),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(runtimeErrors.at(-1)).toContain(`Malformed JSON in secrets plan file: ${planPath}`);
|
||||
expect(runSecretsApply).not.toHaveBeenCalled();
|
||||
}, "{invalid json");
|
||||
});
|
||||
|
||||
it("does not print skipped-exec note when apply dry-run skippedExecRefs is zero", async () => {
|
||||
await withPlanFile(async (planPath) => {
|
||||
runSecretsApply.mockResolvedValue(createSecretsApplyResult({ resolvabilityComplete: false }));
|
||||
|
||||
@@ -53,7 +53,12 @@ async function readPlanFile(pathname: string): Promise<SecretsApplyPlan> {
|
||||
import("../secrets/plan.js"),
|
||||
]);
|
||||
const raw = readFileSync(pathname, "utf8");
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch (err) {
|
||||
throw new Error(`Malformed JSON in secrets plan file: ${pathname}`, { cause: err });
|
||||
}
|
||||
if (!isSecretsApplyPlan(parsed)) {
|
||||
throw new Error(
|
||||
`Invalid secrets plan file: ${pathname}. Generate a fresh plan with ${formatCliCommand("openclaw secrets configure --plan-out <path>")}.`,
|
||||
|
||||
Reference in New Issue
Block a user