Skip to content

Commit 9c7666e

Browse files
committed
fix(codex): redact plugin thread config logs
1 parent caec807 commit 9c7666e

2 files changed

Lines changed: 135 additions & 16 deletions

File tree

extensions/codex/src/app-server/run-attempt.test.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ import * as approvalBridge from "./approval-bridge.js";
3838
import * as authBridge from "./auth-bridge.js";
3939
import { resolveCodexAppServerEnvApiKeyCacheKey } from "./auth-bridge.js";
4040
import type { CodexAppServerClientFactory } from "./client-factory.js";
41-
import { readCodexPluginConfig, resolveCodexAppServerRuntimeOptions } from "./config.js";
41+
import {
42+
readCodexPluginConfig,
43+
resolveCodexAppServerRuntimeOptions,
44+
resolveCodexPluginsPolicy,
45+
} from "./config.js";
4246
import {
4347
CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
4448
createCodexDynamicToolBridge,
@@ -9059,6 +9063,84 @@ describe("runCodexAppServerAttempt", () => {
90599063
expect(second).not.toContain("Bearer second");
90609064
});
90619065

9066+
it("redacts plugin thread config eligibility log data", () => {
9067+
const appServer = {
9068+
start: {
9069+
transport: "websocket" as const,
9070+
command: "codex",
9071+
commandSource: "config" as const,
9072+
args: [],
9073+
url: "ws://127.0.0.1:39175",
9074+
authToken: "token-secret",
9075+
headers: {
9076+
Authorization: "Bearer secret",
9077+
"X-Test-Token": "header-secret",
9078+
},
9079+
env: {
9080+
CODEX_HOME: "/tmp/codex-home",
9081+
OPENAI_API_KEY: "env-secret",
9082+
},
9083+
},
9084+
codeModeOnly: false,
9085+
requestTimeoutMs: 60_000,
9086+
turnCompletionIdleTimeoutMs: 60_000,
9087+
approvalPolicy: "never" as const,
9088+
approvalsReviewer: "user" as const,
9089+
sandbox: "danger-full-access" as const,
9090+
serviceTier: "priority" as const,
9091+
};
9092+
const resolvedPluginPolicy = resolveCodexPluginsPolicy({
9093+
codexPlugins: {
9094+
enabled: true,
9095+
plugins: {
9096+
"google-calendar": {
9097+
marketplaceName: "openai-curated",
9098+
pluginName: "google-calendar",
9099+
},
9100+
},
9101+
},
9102+
});
9103+
const logData = testing.buildCodexPluginThreadConfigEligibilityLogData({
9104+
sessionId: "session-1",
9105+
sessionKey: "agent:main:session-1",
9106+
pluginThreadConfigRequired: true,
9107+
resolvedPluginPolicy,
9108+
enabledPluginConfigKeys: ["google-calendar"],
9109+
pluginAppCacheKey: buildCodexPluginAppCacheKey({
9110+
appServer,
9111+
agentDir: "/tmp/agent",
9112+
authProfileId: "openai-codex:work",
9113+
accountId: "account-work",
9114+
envApiKeyFingerprint: "env-key",
9115+
}),
9116+
startupAuthProfileId: "openai-codex:work",
9117+
appServer,
9118+
});
9119+
9120+
expect(logData).toEqual(
9121+
expect.objectContaining({
9122+
sessionId: "session-1",
9123+
sessionKey: "agent:main:session-1",
9124+
enabled: true,
9125+
policyConfigured: true,
9126+
policyEnabled: true,
9127+
pluginConfigKeys: ["google-calendar"],
9128+
enabledPluginConfigKeys: ["google-calendar"],
9129+
appCacheKeyFingerprint: expect.stringMatching(/^sha256:/),
9130+
authProfileId: "openai-codex:work",
9131+
appServerTransport: "websocket",
9132+
appServerCommandSource: "config",
9133+
}),
9134+
);
9135+
expect(logData).not.toHaveProperty("appCacheKeyInput");
9136+
const serialized = JSON.stringify(logData);
9137+
expect(serialized).not.toContain("token-secret");
9138+
expect(serialized).not.toContain("Bearer secret");
9139+
expect(serialized).not.toContain("header-secret");
9140+
expect(serialized).not.toContain("env-secret");
9141+
expect(serialized).not.toContain("/tmp/codex-home");
9142+
});
9143+
90629144
it("builds resume and turn params from the currently selected OpenClaw model", () => {
90639145
const params = createParams("/tmp/session.jsonl", "/tmp/workspace");
90649146
const appServer = {

extensions/codex/src/app-server/run-attempt.ts

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,21 +1203,19 @@ export async function runCodexAppServerAttempt(
12031203
.map((plugin) => plugin.configKey)
12041204
.toSorted()
12051205
: undefined;
1206-
embeddedAgentLog.info("codex plugin thread config eligibility", {
1207-
sessionId: params.sessionId,
1208-
sessionKey: sandboxSessionKey,
1209-
enabled: pluginThreadConfigRequired,
1210-
policyConfigured: resolvedPluginPolicy?.configured === true,
1211-
policyEnabled: resolvedPluginPolicy?.enabled === true,
1212-
pluginConfigKeys: resolvedPluginPolicy?.pluginPolicies
1213-
.map((plugin) => plugin.configKey)
1214-
.toSorted(),
1215-
enabledPluginConfigKeys,
1216-
appCacheKeyInput: pluginAppCacheKeyInput,
1217-
authProfileId: startupAuthProfileId,
1218-
appServerTransport: appServer.start.transport,
1219-
appServerCommandSource: appServer.start.commandSource,
1220-
});
1206+
embeddedAgentLog.info(
1207+
"codex plugin thread config eligibility",
1208+
buildCodexPluginThreadConfigEligibilityLogData({
1209+
sessionId: params.sessionId,
1210+
sessionKey: sandboxSessionKey,
1211+
pluginThreadConfigRequired,
1212+
resolvedPluginPolicy,
1213+
enabledPluginConfigKeys,
1214+
pluginAppCacheKey,
1215+
startupAuthProfileId,
1216+
appServer,
1217+
}),
1218+
);
12211219
pluginAppServer =
12221220
resolvedPluginPolicy?.enabled === true
12231221
? {
@@ -3108,6 +3106,44 @@ function buildCodexNativeHookRelayId(params: {
31083106
return `codex-${hash.digest("hex").slice(0, 40)}`;
31093107
}
31103108

3109+
function fingerprintCodexLogValue(namespace: string, value: string): string {
3110+
const hash = createHash("sha256");
3111+
hash.update(namespace);
3112+
hash.update("\0");
3113+
hash.update(value);
3114+
return `sha256:${hash.digest("hex").slice(0, 16)}`;
3115+
}
3116+
3117+
function buildCodexPluginThreadConfigEligibilityLogData(params: {
3118+
sessionId: string;
3119+
sessionKey: string;
3120+
pluginThreadConfigRequired: boolean;
3121+
resolvedPluginPolicy: ReturnType<typeof resolveCodexPluginsPolicy> | undefined;
3122+
enabledPluginConfigKeys: string[] | undefined;
3123+
pluginAppCacheKey: string;
3124+
startupAuthProfileId: string | undefined;
3125+
appServer: CodexAppServerRuntimeOptions;
3126+
}): Record<string, unknown> {
3127+
return {
3128+
sessionId: params.sessionId,
3129+
sessionKey: params.sessionKey,
3130+
enabled: params.pluginThreadConfigRequired,
3131+
policyConfigured: params.resolvedPluginPolicy?.configured === true,
3132+
policyEnabled: params.resolvedPluginPolicy?.enabled === true,
3133+
pluginConfigKeys: params.resolvedPluginPolicy?.pluginPolicies
3134+
.map((plugin) => plugin.configKey)
3135+
.toSorted(),
3136+
enabledPluginConfigKeys: params.enabledPluginConfigKeys,
3137+
appCacheKeyFingerprint: fingerprintCodexLogValue(
3138+
"openclaw:codex:plugin-app-cache-key:v1",
3139+
params.pluginAppCacheKey,
3140+
),
3141+
authProfileId: params.startupAuthProfileId,
3142+
appServerTransport: params.appServer.start.transport,
3143+
appServerCommandSource: params.appServer.start.commandSource,
3144+
};
3145+
}
3146+
31113147
function interruptCodexTurnBestEffort(
31123148
client: CodexAppServerClient,
31133149
params: {
@@ -4605,6 +4641,7 @@ export const testing = {
46054641
resolveOpenClawCodingToolsSessionKeys,
46064642
shouldEnableCodexAppServerNativeToolSurface,
46074643
shouldForceMessageTool,
4644+
buildCodexPluginThreadConfigEligibilityLogData,
46084645
setOpenClawCodingToolsFactoryForTests(factory: OpenClawCodingToolsFactory): void {
46094646
openClawCodingToolsFactoryForTests = factory;
46104647
},

0 commit comments

Comments
 (0)