Skip to content

[Bug]: /config show chat command returns unredacted config containing plaintext secrets #65623

@coygeek

Description

@coygeek

Severity Assessment

CVSS Assessment

Metric v3.1 v4.0
Score 7.7 / 10.0 8.3 / 10.0
Severity High High
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N
Calculator CVSS v3.1 Calculator CVSS v4.0 Calculator

Threat Model Alignment

Classification: security-specific

This is security-specific because /config show is documented as an operator inspection surface, not a plaintext secret export primitive, while surrounding config and logging guidance treats secrets as data that should be redacted on shared or operator-visible surfaces. OpenClaw also supports channel reply modes where owner-triggered chat responses remain visible in shared channels or groups, so owner-only invocation does not erase the confidentiality boundary. Returning raw config secrets in the chat reply therefore violates the documented secrecy model rather than exercising an intentionally trusted admin capability.

Impact

The /config show chat command returns the full parsed config object as a JSON-formatted chat message with no secret redaction applied, exposing plaintext passwords, API keys, and bot tokens stored in the config file to the channel message history.

Affected Component

File: src/auto-reply/reply/commands-config.ts:101-135

const snapshot = await readConfigFileSnapshot();
// ...
const parsedBase = structuredClone(snapshot.parsed as Record<string, unknown>);

if (configCommand.action === "show") {
  const pathRaw = configCommand.path?.trim();
  if (pathRaw) {
    // ...
    const value = getConfigValueAtPath(parsedBase, parsedPath.path);
    const rendered = JSON.stringify(value ?? null, null, 2);
    return {
      shouldContinue: false,
      reply: {
        text: `⚙️ Config ${pathRaw}:\n\`\`\`json\n${rendered}\n\`\`\``,
      },
    };
  }
  const json = JSON.stringify(parsedBase, null, 2);
  return {
    shouldContinue: false,
    reply: { text: `⚙️ Config (raw):\n\`\`\`json\n${json}\n\`\`\`` },
  };
}

Technical Reproduction

  1. Deploy OpenClaw with a config file containing at least one sensitive field (e.g. gateway.token, irc.password, telegram.token, or any provider apiKey).
  2. As the configured owner, send /config show on any enabled channel.
  3. Observe: the bot replies with a full JSON dump of parsedBase — the raw parsed config — including any plaintext secret values. No values are masked or replaced with [REDACTED].

Alternatively, send /config show gateway.token to extract a specific secret field by path.

Demonstrated Impact

handleConfigCommand in commands-config.ts calls readConfigFileSnapshot() which returns the raw parsed config object at snapshot.parsed. It then calls structuredClone(snapshot.parsed) and immediately serializes this clone with JSON.stringify before returning it as the reply text. There is no call to redactConfigSnapshot() or redactConfigObject() anywhere in this path.

By contrast, the gateway's config.get HTTP endpoint (src/gateway/server-methods/config.ts:290) correctly calls redactConfigSnapshot(snapshot, schema.uiHints) before responding, which walks the schema and replaces all SecretInput / sensitive fields with the [REDACTED] sentinel.

Sensitive fields that can be exposed include: IRC password and nickserv password; gateway auth token and password; Slack signingSecret; Telegram token; Discord token; and all AI provider apiKey/token fields. These are marked as SecretInput or sensitive in the config schema (src/config/zod-schema.ts) but the chat command path never consults the schema hints.

Because the reply is delivered as a chat message, it is persisted in channel message history (Slack, Discord, iMessage, Telegram, etc.) and may be visible to any user with access to that channel history — including other channel members who are not the OpenClaw owner. The /config show <path> sub-command variant (lines 122–129) is equally affected, allowing targeted extraction of individual secrets by dot-path.

Existing controls do not prevent exploitation: the rejectNonOwnerCommand guard only checks that the sender is the configured owner, which is sufficient for issuing the command itself but does not constrain what data is returned in the reply. Log-level redaction in src/logging/redact.ts applies only to log output, not to chat reply text.

Environment

Affects all OpenClaw deployments where the config file contains at least one sensitive field and at least one messaging channel is enabled. Triggered by sending /config show from any owner session. No special runtime configuration required.

Remediation Advice

Apply redactConfigSnapshot() (from src/config/redact-snapshot.ts) to the parsed config clone before serializing it for the chat reply in handleConfigCommand, matching the pattern already used by the config.get gateway endpoint. Both the full-config path (line 131) and the per-path extraction path (line 122) must be covered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions