OpenClaw’s Denied-Exec Redaction Fix Closes the Worst Kind of Security Bug
The most dangerous security bugs are not always the ones where enforcement fails. Sometimes the guardrail works, the action is blocked, and the system still quietly preserves the payload somewhere worse. OpenClaw’s denied-exec redaction fix is in that category: the gateway denied a dangerous command, then logged the raw command parameters, including live secrets, into a durable error file.
That is the kind of failure that makes operators distrust safety systems. Not because the policy engine said yes when it should have said no, but because the diagnostic path behaved like a data exfiltration feature with better formatting.
PR #85140, created on May 22, fixes a denied `exec` logging path where normalized failure params could include raw `raw_params` from the model-supplied tool call. The linked issue #85049 reports a real incident, not a theoretical concern: six live credentials appeared in a single denied-command log line after an agent attempted to inline environment-variable exports before a shell action. The exposed names were not subtle — `MOONSHOT_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`, `BRAVE_API_KEY`, `GUMROAD_API_KEY`, and `INSTAGRAM_ACCESS_TOKEN`. The reporter rotated the keys. That is the right response, and also the part of the story nobody wants to do at 2 a.m.
The failure mode is easy to understand if you have ever debugged tool-calling systems. A model is trying to complete a task. It assembles a shell command. It may include `export KEY=value` assignments because it has seen humans do that in scripts, or because the surrounding context contains secrets and the model is trying to be “helpful.” The runtime’s exec policy correctly denies the call. Then the failure handler formats the denied params for logs so an operator can inspect what happened. In doing so, it preserves the exact thing the policy prevented from executing.
Denied actions are still sensitive actions
PR #85140 changes five files with 269 additions and four deletions, including tests for the Pi tool-definition adapter and shared logging redaction. The patch sanitizes normalized `exec` and `bash` failure params before formatting failure logs. It omits command text and environment values while retaining safer metadata. It handles both `command` and `cmd`, supports JSON-string and malformed exec params, and adds escaped environment-assignment redaction as defense in depth. The scope is intentionally narrow: non-exec tool failure parameter logging keeps its existing behavior.
That trade is correct. Operators need to know that an exec call was denied. They need enough metadata to correlate it with a run, a tool family, a policy decision, and a user-visible failure. They do not need a full shell command containing inline credentials in `gateway.err.log`. The whole reason the command was denied is that the runtime did not trust it enough to execute. Treating it as safe enough to serialize verbatim is backwards.
This is a general lesson for agent platforms: denied tool calls deserve at least as much threat modeling as successful ones. Models often dump the most sensitive material into failed attempts because they are trying to compress intent, credentials, and execution into one step. A successful high-level API call may contain a sanitized request body. A denied shell call may contain the operator’s entire secret soup, pasted into an `export` chain that never should have existed. If your observability pipeline logs rejected payloads verbatim, your sandbox is also a credential collection service.
The macOS file-permission detail in the issue makes this less academic. The affected log file reportedly had default ACLs/mode 644, meaning anything with access to the operator’s home directory on a multi-user system could read it. That is not the same as public internet exposure, but it is absolutely enough to matter in shared developer boxes, CI runners, remote desktops, and incident-response scenarios where logs get zipped and attached to tickets. Secrets in logs have a way of traveling farther than anyone intended.
Audit trails should preserve decisions, not payloads
The fix’s behavior proof is worth noting because it checks both the absence of dangerous material and the presence of useful redaction. The after-fix evidence reported `gatewayErrContainsOmittedCommand=true`, `gatewayErrContainsOmittedEnv=true`, `fileLogContainsOmittedCommand=true`, `fileLogContainsOmittedEnv=true`, while fake token/export checks were false. In plain English: the logs still indicate that command and env material existed and were omitted, but the raw payload is gone. That is what an audit trail should do.
This pattern should become standard across agent runtimes. Log the decision: denied exec. Log the reason: approval policy, sandbox policy, malformed params, blocked destination, whatever applies. Log stable identifiers: run ID, tool name, agent, session, maybe a hashed payload identifier if dedupe matters. Do not log raw commands, env values, bearer tokens, OAuth artifacts, SSH material, cookies, API keys, or model-visible secrets just because the failure formatter is convenient.
There is also a product-design point hiding here. Developers love verbose failure logs because they make local debugging faster. Security engineers hate verbose failure logs because they preserve the blast radius. Agent systems make that tension sharper because the payload was not necessarily typed by a trusted human. It may have been generated by a model after reading a messy transcript, a web page, a README, or a poisoned instruction. Failure logging cannot assume the payload is operator-authored. It has to assume the payload is attacker-influenced.
For practitioners running OpenClaw, the immediate checklist is boring and necessary. Upgrade once the fix lands in your deployed line. Search recent gateway error logs for denied `exec` or `bash` failures containing `API_KEY`, `TOKEN`, `SECRET`, `Authorization`, `sk-`, or provider-specific prefixes. Rotate anything that appears in cleartext. Tighten log retention and access controls. Then review custom plugins and wrappers for the same anti-pattern: denied, refused, or failed tool payloads written verbatim “for debugging.”
The broader OpenClaw context is moving in the right direction. The same 5.20 release cycle includes MCP diagnostics, approval-runtime cleanup, and removal of permissive historical exec-approval compatibility around skill wrapper command shapes. That does not make this bug harmless. It does suggest the project is converging on the correct operating model: tool authority is a runtime surface, not a prompt convention. Redaction has to be part of that surface.
The editorial take is simple: a denied command should leave evidence, not evidence bags full of API keys. Security controls are not finished when they block the action. They are finished when the block itself does not create a new incident.
Sources: OpenClaw PR #85140, OpenClaw issue #85049, OpenClaw v2026.5.20 release, OpenClaw PR #84699