fix(codex): rotate shared app-server clients on auth changes · openclaw/openclaw@0bc5ccc
Lucenx9
·
2026-04-23
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
|
2 | 2 | import { describe, expect, it } from "vitest"; |
3 | 3 | import { |
4 | 4 | CODEX_APP_SERVER_CONFIG_KEYS, |
| 5 | +codexAppServerStartOptionsKey, |
5 | 6 | readCodexPluginConfig, |
6 | 7 | resolveCodexAppServerRuntimeOptions, |
7 | 8 | } from "./config.js"; |
@@ -75,6 +76,29 @@ describe("Codex app-server config", () => {
|
75 | 76 | ); |
76 | 77 | }); |
77 | 78 | |
| 79 | +it("derives distinct shared-client keys for distinct auth tokens without exposing them", () => { |
| 80 | +const first = codexAppServerStartOptionsKey({ |
| 81 | +transport: "websocket", |
| 82 | +command: "codex", |
| 83 | +args: [], |
| 84 | +url: "ws://127.0.0.1:39175", |
| 85 | +authToken: "tok_first", |
| 86 | +headers: {}, |
| 87 | +}); |
| 88 | +const second = codexAppServerStartOptionsKey({ |
| 89 | +transport: "websocket", |
| 90 | +command: "codex", |
| 91 | +args: [], |
| 92 | +url: "ws://127.0.0.1:39175", |
| 93 | +authToken: "tok_second", |
| 94 | +headers: {}, |
| 95 | +}); |
| 96 | + |
| 97 | +expect(first).not.toEqual(second); |
| 98 | +expect(first).not.toContain("tok_first"); |
| 99 | +expect(second).not.toContain("tok_second"); |
| 100 | +}); |
| 101 | + |
78 | 102 | it("keeps runtime config keys aligned with manifest schema and UI hints", async () => { |
79 | 103 | const manifest = JSON.parse( |
80 | 104 | await fs.readFile(new URL("../../openclaw.plugin.json", import.meta.url), "utf8"), |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { createHash } from "node:crypto"; |
1 | 2 | import { z } from "zod"; |
2 | 3 | |
3 | 4 | export type CodexAppServerTransportMode = "stdio" | "websocket"; |
@@ -156,7 +157,7 @@ export function codexAppServerStartOptionsKey(options: CodexAppServerStartOption
|
156 | 157 | command: options.command, |
157 | 158 | args: options.args, |
158 | 159 | url: options.url ?? null, |
159 | | -authToken: options.authToken ? "<set>" : null, |
| 160 | +authToken: hashSecretForKey(options.authToken), |
160 | 161 | headers: Object.entries(options.headers).toSorted(([left], [right]) => |
161 | 162 | left.localeCompare(right), |
162 | 163 | ), |
@@ -223,6 +224,13 @@ function readNonEmptyString(value: unknown): string | undefined {
|
223 | 224 | return trimmed || undefined; |
224 | 225 | } |
225 | 226 | |
| 227 | +function hashSecretForKey(value: string | undefined): string | null { |
| 228 | +if (!value) { |
| 229 | +return null; |
| 230 | +} |
| 231 | +return createHash("sha256").update(value).digest("hex"); |
| 232 | +} |
| 233 | + |
226 | 234 | function splitShellWords(value: string): string[] { |
227 | 235 | const words: string[] = []; |
228 | 236 | let current = ""; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -102,4 +102,46 @@ describe("shared Codex app-server client", () => {
|
102 | 102 | }), |
103 | 103 | ); |
104 | 104 | }); |
| 105 | + |
| 106 | +it("restarts the shared client when the bridged auth token changes", async () => { |
| 107 | +const first = createClientHarness(); |
| 108 | +const second = createClientHarness(); |
| 109 | +const startSpy = vi |
| 110 | +.spyOn(CodexAppServerClient, "start") |
| 111 | +.mockReturnValueOnce(first.client) |
| 112 | +.mockReturnValueOnce(second.client); |
| 113 | + |
| 114 | +const firstList = listCodexAppServerModels({ |
| 115 | +timeoutMs: 1000, |
| 116 | +startOptions: { |
| 117 | +transport: "websocket", |
| 118 | +command: "codex", |
| 119 | +args: [], |
| 120 | +url: "ws://127.0.0.1:39175", |
| 121 | +authToken: "tok-first", |
| 122 | +headers: {}, |
| 123 | +}, |
| 124 | +}); |
| 125 | +await sendInitializeResult(first, "openclaw/0.118.0 (macOS; test)"); |
| 126 | +await sendEmptyModelList(first); |
| 127 | +await expect(firstList).resolves.toEqual({ models: [] }); |
| 128 | + |
| 129 | +const secondList = listCodexAppServerModels({ |
| 130 | +timeoutMs: 1000, |
| 131 | +startOptions: { |
| 132 | +transport: "websocket", |
| 133 | +command: "codex", |
| 134 | +args: [], |
| 135 | +url: "ws://127.0.0.1:39175", |
| 136 | +authToken: "tok-second", |
| 137 | +headers: {}, |
| 138 | +}, |
| 139 | +}); |
| 140 | +await sendInitializeResult(second, "openclaw/0.118.0 (macOS; test)"); |
| 141 | +await sendEmptyModelList(second); |
| 142 | +await expect(secondList).resolves.toEqual({ models: [] }); |
| 143 | + |
| 144 | +expect(startSpy).toHaveBeenCalledTimes(2); |
| 145 | +expect(first.process.kill).toHaveBeenCalledWith("SIGTERM"); |
| 146 | +}); |
105 | 147 | }); |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。