mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:50:43 +00:00
fix(codex): rotate shared app-server clients on auth changes
This commit is contained in:
committed by
Peter Steinberger
parent
f4c4e940a6
commit
0bc5ccc706
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
CODEX_APP_SERVER_CONFIG_KEYS,
|
||||
codexAppServerStartOptionsKey,
|
||||
readCodexPluginConfig,
|
||||
resolveCodexAppServerRuntimeOptions,
|
||||
} from "./config.js";
|
||||
@@ -75,6 +76,29 @@ describe("Codex app-server config", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("derives distinct shared-client keys for distinct auth tokens without exposing them", () => {
|
||||
const first = codexAppServerStartOptionsKey({
|
||||
transport: "websocket",
|
||||
command: "codex",
|
||||
args: [],
|
||||
url: "ws://127.0.0.1:39175",
|
||||
authToken: "tok_first",
|
||||
headers: {},
|
||||
});
|
||||
const second = codexAppServerStartOptionsKey({
|
||||
transport: "websocket",
|
||||
command: "codex",
|
||||
args: [],
|
||||
url: "ws://127.0.0.1:39175",
|
||||
authToken: "tok_second",
|
||||
headers: {},
|
||||
});
|
||||
|
||||
expect(first).not.toEqual(second);
|
||||
expect(first).not.toContain("tok_first");
|
||||
expect(second).not.toContain("tok_second");
|
||||
});
|
||||
|
||||
it("keeps runtime config keys aligned with manifest schema and UI hints", async () => {
|
||||
const manifest = JSON.parse(
|
||||
await fs.readFile(new URL("../../openclaw.plugin.json", import.meta.url), "utf8"),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { z } from "zod";
|
||||
|
||||
export type CodexAppServerTransportMode = "stdio" | "websocket";
|
||||
@@ -156,7 +157,7 @@ export function codexAppServerStartOptionsKey(options: CodexAppServerStartOption
|
||||
command: options.command,
|
||||
args: options.args,
|
||||
url: options.url ?? null,
|
||||
authToken: options.authToken ? "<set>" : null,
|
||||
authToken: hashSecretForKey(options.authToken),
|
||||
headers: Object.entries(options.headers).toSorted(([left], [right]) =>
|
||||
left.localeCompare(right),
|
||||
),
|
||||
@@ -223,6 +224,13 @@ function readNonEmptyString(value: unknown): string | undefined {
|
||||
return trimmed || undefined;
|
||||
}
|
||||
|
||||
function hashSecretForKey(value: string | undefined): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
return createHash("sha256").update(value).digest("hex");
|
||||
}
|
||||
|
||||
function splitShellWords(value: string): string[] {
|
||||
const words: string[] = [];
|
||||
let current = "";
|
||||
|
||||
@@ -102,4 +102,46 @@ describe("shared Codex app-server client", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("restarts the shared client when the bridged auth token changes", async () => {
|
||||
const first = createClientHarness();
|
||||
const second = createClientHarness();
|
||||
const startSpy = vi
|
||||
.spyOn(CodexAppServerClient, "start")
|
||||
.mockReturnValueOnce(first.client)
|
||||
.mockReturnValueOnce(second.client);
|
||||
|
||||
const firstList = listCodexAppServerModels({
|
||||
timeoutMs: 1000,
|
||||
startOptions: {
|
||||
transport: "websocket",
|
||||
command: "codex",
|
||||
args: [],
|
||||
url: "ws://127.0.0.1:39175",
|
||||
authToken: "tok-first",
|
||||
headers: {},
|
||||
},
|
||||
});
|
||||
await sendInitializeResult(first, "openclaw/0.118.0 (macOS; test)");
|
||||
await sendEmptyModelList(first);
|
||||
await expect(firstList).resolves.toEqual({ models: [] });
|
||||
|
||||
const secondList = listCodexAppServerModels({
|
||||
timeoutMs: 1000,
|
||||
startOptions: {
|
||||
transport: "websocket",
|
||||
command: "codex",
|
||||
args: [],
|
||||
url: "ws://127.0.0.1:39175",
|
||||
authToken: "tok-second",
|
||||
headers: {},
|
||||
},
|
||||
});
|
||||
await sendInitializeResult(second, "openclaw/0.118.0 (macOS; test)");
|
||||
await sendEmptyModelList(second);
|
||||
await expect(secondList).resolves.toEqual({ models: [] });
|
||||
|
||||
expect(startSpy).toHaveBeenCalledTimes(2);
|
||||
expect(first.process.kill).toHaveBeenCalledWith("SIGTERM");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user