


























@@ -28,6 +28,10 @@ function resolveRuntimeForTest(params: RuntimeOptionsParams = {}) {
2828return resolveCodexAppServerRuntimeOptions({ env: {}, requirementsToml: null, ...params });
2929}
303031+function envRef(id: string) {
32+return { source: "env" as const, provider: "default", id };
33+}
34+3135function requireRecord(value: unknown, label: string): Record<string, unknown> {
3236if (!value || typeof value !== "object" || Array.isArray(value)) {
3337throw new Error(`Expected ${label}`);
@@ -413,6 +417,65 @@ describe("Codex app-server config", () => {
413417});
414418});
415419420+it("passes resolved app-server SecretInput strings through to auth token and headers", () => {
421+const runtime = resolveRuntimeForTest({
422+pluginConfig: {
423+appServer: {
424+transport: "websocket",
425+url: "wss://codex-app-server.example.internal/ws",
426+authToken: " resolved-capability-token ",
427+headers: {
428+" x-codex-client-session-token ": " resolved-session-token ",
429+Authorization: " Bearer explicit-token ",
430+},
431+},
432+},
433+});
434+435+expectFields(runtime.start, "runtime start", {
436+authToken: "resolved-capability-token",
437+headers: {
438+"x-codex-client-session-token": "resolved-session-token",
439+Authorization: "Bearer explicit-token",
440+},
441+});
442+});
443+444+it("rejects unresolved app-server auth token SecretRefs at runtime option resolution", () => {
445+expect(() =>
446+resolveRuntimeForTest({
447+pluginConfig: {
448+appServer: {
449+transport: "websocket",
450+url: "wss://codex-app-server.example.internal/ws",
451+authToken: envRef("CODEX_APP_SERVER_TOKEN"),
452+},
453+},
454+}),
455+).toThrow(
456+'plugins.entries.codex.config.appServer.authToken: unresolved SecretRef "env:default:CODEX_APP_SERVER_TOKEN"',
457+);
458+});
459+460+it("rejects unresolved app-server header SecretRefs at runtime option resolution", () => {
461+expect(() =>
462+resolveRuntimeForTest({
463+pluginConfig: {
464+appServer: {
465+transport: "websocket",
466+url: "wss://codex-app-server.example.internal/ws",
467+authToken: "capability-token",
468+headers: {
469+"x-codex-client-session-token": envRef("CODEX_CLIENT_SESSION_TOKEN"),
470+},
471+},
472+},
473+}),
474+).toThrow(
475+'plugins.entries.codex.config.appServer.headers.x-codex-client-session-token: unresolved SecretRef "env:default:CODEX_CLIENT_SESSION_TOKEN"',
476+);
477+});
478+416479it("treats IPv6 loopback websocket app-servers as local loopback", () => {
417480const runtime = resolveRuntimeForTest({
418481pluginConfig: {
@@ -2314,6 +2377,47 @@ allowed_sandbox_modes = ["read-only", "workspace-write"]
23142377expect(second).not.toContain("sk-second");
23152378});
231623792380+it("derives distinct shared-client keys for distinct headers without exposing them", () => {
2381+const first = codexAppServerStartOptionsKey({
2382+transport: "websocket",
2383+command: "codex",
2384+args: [],
2385+url: "ws://127.0.0.1:39175",
2386+headers: {
2387+Authorization: "Bearer first",
2388+"x-codex-client-session-token": "session-first",
2389+},
2390+});
2391+const second = codexAppServerStartOptionsKey({
2392+transport: "websocket",
2393+command: "codex",
2394+args: [],
2395+url: "ws://127.0.0.1:39175",
2396+headers: {
2397+Authorization: "Bearer second",
2398+"x-codex-client-session-token": "session-second",
2399+},
2400+});
2401+2402+expect(first).not.toEqual(second);
2403+expect(
2404+codexAppServerStartOptionsKey({
2405+transport: "websocket",
2406+command: "codex",
2407+args: [],
2408+url: "ws://127.0.0.1:39175",
2409+headers: {
2410+Authorization: "Bearer first",
2411+"x-codex-client-session-token": "session-first",
2412+},
2413+}),
2414+).toEqual(first);
2415+expect(first).not.toContain("Bearer first");
2416+expect(first).not.toContain("session-first");
2417+expect(second).not.toContain("Bearer second");
2418+expect(second).not.toContain("session-second");
2419+});
2420+23172421it("keeps secret-derived shared-client keys stable across module reloads", async () => {
23182422const startOptions = {
23192423transport: "websocket" as const,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。