fix(acp): strip provider API keys from ACP child process env#41615
fix(acp): strip provider API keys from ACP child process env#41615rodrigouroz wants to merge 15 commits intoopenclaw:mainfrom
Conversation
Provider API keys (e.g. OPENAI_API_KEY) injected by the auth system were leaking into ACP child processes. This caused Codex CLI to detect the env var and overwrite ~/.codex/auth.json, replacing OAuth credentials with apikey mode. The existing stripKeys mechanism only removed skill-injected env vars (getActiveSkillEnvKeys). This change also strips all known provider API key env vars (listKnownProviderEnvApiKeyNames) so ACP agents use their own configured authentication instead of inheriting leaked credentials. Fixes the same class of issue described in openclaw#36280.
The acpx runtime plugin passes process.env directly to spawned ACP child processes. Provider API keys (e.g. OPENAI_API_KEY) loaded by the OpenClaw auth system leak into ACP agents, causing Codex CLI to detect the env var and overwrite ~/.codex/auth.json, replacing OAuth with apikey mode. Strip all env vars ending in _API_KEY (except OPENCLAW_API_KEY) from the child process environment. This is applied in the acpx plugin's spawnWithResolvedCommand, which is the actual spawn path used for gateway-initiated ACP sessions (sessions_spawn). The companion fix in src/acp/client.ts (previous commit) covers the CLI path (openclaw acp). Together they ensure no API key leakage regardless of how ACP sessions are created.
🔒 Aisle Security AnalysisWe found 2 potential security issue(s) in this PR:
1. 🔵 Incomplete secret env var enumeration leaves new provider auth tokens unsanitized in .env audit/scrub flows
Description
Impact (grounded call sites):
Vulnerable code (root cause): export function listKnownSecretEnvVarNames(): string[] {
return [...new Set(Object.values(PROVIDER_ENV_VARS).flatMap((keys) => keys))];
}This divergence between “provider auth env vars” and “secret env vars” is a concrete gap introduced by the new enumeration: some provider auth secrets are now recognized for spawn-time stripping, but still omitted from secret audit/scrub mechanisms. RecommendationUnify the two lists so all known provider auth secrets are treated as “known secret env vars” for auditing/scrubbing. Option A (simple): make export function listKnownSecretEnvVarNames(): string[] {
return listKnownProviderAuthEnvVarNames();
}Option B (explicit): include Also update any secret-audit/scrub call sites ( 2. 🔵 Provider API keys not stripped when default ACP server command is provided explicitly
DescriptionThe new env-stripping logic decides whether to remove provider auth environment variables based solely on whether As a result:
This can lead to unexpected secret exposure, especially because Vulnerable code: export function shouldStripProviderAuthEnvVarsForAcpServer(serverCommand?: string): boolean {
return !serverCommand;
}
// ...
const stripKeys = buildAcpClientStripKeys({
serverCommand: opts.serverCommand,
activeSkillEnvKeys: getActiveSkillEnvKeys(),
});RecommendationTreat the default OpenClaw bridge as the boundary to strip provider auth env vars, regardless of whether the caller specified the default command explicitly. Suggested approach:
Example fix (conceptual): function isDefaultBridgeCommand(resolvedServerCommand: string, entryPath: string | null): boolean {
const cmd = resolvedServerCommand.trim();
if (!cmd) return true;
if (cmd === "openclaw") return true;
// When using the bundled entrypoint, default bridge uses node + entry.js
if (entryPath && cmd === process.execPath) return true;
return false;
}
// in createAcpClient
const isDefaultBridge = isDefaultBridgeCommand(serverCommand, entryPath);
const stripKeys = buildAcpClientStripKeys({
// pass a boolean instead of raw user input, or pass the resolved command
serverCommand: isDefaultBridge ? undefined : serverCommand,
activeSkillEnvKeys: getActiveSkillEnvKeys(),
});This prevents accidental leaks when callers pass Analyzed PR: #41615 at commit Last updated on: 2026-03-10T15:46:53Z |
Greptile SummaryThis PR strips provider API key env vars from ACP child process environments on both CLI and gateway/acpx spawn paths, which correctly addresses the root cause of tools like Codex CLI switching auth modes when inheriting parent process credentials. The core env-var stripping logic in However, the PR introduces two significant data-loss regressions in
Additionally, the env-var stripping intent is obscured by reusing Not safe to merge as-is without either restoring these features or removing the now-unused input fields from the API contracts. Confidence Score: 2/5
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d4781d699e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Use a shared provider auth env-var list for ACP child process scrubbing so both the CLI ACP path and the acpx runtime strip the same credentials, including non-_API_KEY tokens like GITHUB_TOKEN and HF_TOKEN. Only apply provider auth env stripping on the default OpenClaw bridge path. Explicit custom ACP servers keep their env-based auth contract.
|
Addressed the remaining bot comments in d24f695. What changed:
Verification:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c4d98275b0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Restore the existing resolveAcpClientSpawnEnv assertions, including ELEVENLABS_API_KEY coverage, and keep the helper behavior that always forces OPENCLAW_SHELL after stripping requested env keys.
Restore active skill env tracking so skill-injected secrets remain stripped from ACP child process environments, alongside the shared provider auth env filtering.
Restore the skills env override test that verifies tracked env keys stay active until all overlapping overrides are released.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 097a99b54a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Remove the unnecessary skills.js barrel export for getActiveSkillEnvKeys and keep the test importing the helper from its canonical env-overrides module.
Only strip provider auth env vars on the bundled acpx path. Custom acpx commands should keep inherited provider credentials, matching the CLI ACP behavior.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c98c35879
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Apply oxfmt import ordering in src/acp/client.ts and mark test-only provider credential literals in src/acp/client.test.ts as allowlisted false positives for detect-secrets.
Add the required mcpServers field to ResolvedAcpxPluginConfig test fixtures and mark the remaining test-only provider credential literals as allowlisted false positives for detect-secrets.
|
@greptileai please review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 992a770a90
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Restore the ACPX runtime behaviors regressed during the env-strip follow-up: - preserve resumeSessionId when ensuring sessions - serialize image attachments into ACP prompt blocks - route agent verbs through the MCP proxy when mcpServers are configured - make provider-auth stripping use an explicit config flag
Add detect-secrets pragmas to the new ACPX runtime regression tests so the CI secrets job does not flag the intentional fixture values.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3b85c4edf6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (params.stripProviderAuthEnvVars !== false) { | ||
| for (const key of listKnownProviderAuthEnvVarNames()) { | ||
| if (key !== "OPENCLAW_API_KEY") { | ||
| delete childEnv[key]; | ||
| } |
There was a problem hiding this comment.
Preserve custom-command env during acpx preflight probes
spawnWithResolvedCommand now strips provider auth variables by default unless callers explicitly pass stripProviderAuthEnvVars: false. I traced the startup path in extensions/acpx/src/service.ts (ensureAcpx → checkAcpxVersion) and those probes still call spawnAndCollect without that override, so custom config.command wrappers can lose required env-based credentials during --help/--version preflight and be marked unavailable before any turn runs. This only manifests for custom acpx commands that enforce auth env even on probe commands, but in that setup it is a functional regression from previous behavior.
Useful? React with 👍 / 👎.
Remove the extra ACPX config flag and duplicated runtime test fixture, keep the runtime-level custom-command auth-env regression test on the existing fixture path, and restore the preserved plugin-sdk contract coverage.
|
Closing this in favor of #42250. That replacement PR rewrites the fix as a minimal patch on top of current |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9131150479
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ...process.env, | ||
| OPENCLAW_SHELL: "acp", | ||
| }; | ||
| if (params.stripProviderAuthEnvVars !== false) { |
There was a problem hiding this comment.
Scope default provider-env stripping to bundled acpx only
This default branch (params.stripProviderAuthEnvVars !== false) now strips provider auth env vars for every spawnAndCollect call unless each caller opts out, and the startup preflight path still does not opt out (extensions/acpx/src/service.ts calls ensureAcpx, which calls checkAcpxVersion, which calls spawnAndCollect without stripProviderAuthEnvVars). In deployments using a custom config.command wrapper that requires inherited auth env even for --help/--version, this causes the probe to fail and marks ACPX unavailable before any session starts; please make stripping opt-in only on the bundled runtime spawn paths rather than the global default.
Useful? React with 👍 / 👎.
Summary
Describe the problem and fix in 2-5 bullets:
inherited *_API_KEY vars before spawning gateway-initiated ACP child processes.
explicitly on their own side.
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
ACP child processes no longer inherit provider API key env vars from the parent OpenClaw process on the CLI ACP path, and gateway-initiated ACP sessions also strip inherited
*_API_KEY vars before spawning child processes.
This is primarily observable for ACP integrations/tools that manage their own auth (for example Codex CLI): they should now keep using their own configured credentials instead
of switching to inherited API-key auth unexpectedly.
Security Impact (required)
inheriting parent credentials.
Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Attach at least one:
Human Verification (required)
Review Conversations
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
Failure Recovery (if this breaks)
Risks and Mitigations
parent secrets.
straightforward.](fix: strip skill-injected env vars from ACP harness spawn env (#36280) #36316)