Skip to content

gateway config.get leaks secrets into session transcript JSONL #19137

@BoxiYu

Description

@BoxiYu

gateway config.get leaks secrets into session transcript JSONL

Summary

When the gateway tool's config.get action is invoked during a conversation, the full configuration (including plaintext API keys, tokens, and credentials) is returned as a toolResult and persisted verbatim into the session transcript file (~/.openclaw/agents/<agentId>/sessions/*.jsonl).

This means any secret stored in openclaw.json — such as skills.entries.*.apiKey, gateway.auth.token, or provider credentials — ends up in plaintext on disk in session files, which may have weaker access controls than the config file itself.

Reproduction

  1. In any conversation, trigger a config.get call (e.g., ask the agent to check its config, or invoke it via a skill that reads config).
  2. Inspect the corresponding session JSONL:
    grep -r "AIzaSy\|sk-\|xoxb-\|ghp_" ~/.openclaw/agents/*/sessions/*.jsonl
  3. Observe that API keys from config are stored in plaintext inside toolResult entries.

Evidence

Using GardaClaw session forensics scanner on a real deployment:

  • 64 hits for Google API keys (skills.entries.goplaces.apiKey) — all originating from gateway config.get toolResult
  • The same key appeared 64 times across multiple config reads in a single session file
  • The key was AIzaSy... (Google Places API key configured in skills.entries)

Root cause trace

User/agent invokes: gateway(action="config.get")
  → Gateway returns full openclaw.json as toolResult
    → Session engine persists toolResult to JSONL (no redaction)
      → Secrets now live in ~/.openclaw/agents/*/sessions/*.jsonl

Impact

  • Credential exposure: Anyone with read access to session files can extract all secrets from config. Session files are typically 644 (more permissive than the config file at 600).
  • Backup/sync leakage: Session files may be backed up (Time Machine, cloud sync, git) without the user realizing they contain secrets.
  • Accumulation: Each config.get call creates another copy. Over time, secrets are duplicated dozens of times across session files.
  • Existing redaction doesn't help: logging.redactSensitive: "tools" only affects log output, not session transcript persistence.

Suggested Fix

Option A: Redact known secret fields in config.get response (recommended)

Before returning the config as a toolResult, mask fields that are known to contain secrets:

gateway.auth.token        → "gw-***"
gateway.auth.password     → "***"
gateway.remote.token      → "***"
skills.entries.*.apiKey   → "AIza***"
channels.*.token          → "***"
channels.*.tokenFile      → (keep, it's a path not a secret)

A simple approach: walk the config object and mask any key matching /token|apiKey|password|secret|credential/i where the value is a string.

Option B: Redact toolResults before JSONL persistence

Apply the same redactSensitive patterns used for logging to session transcript writes. This would protect against all tools leaking secrets, not just config.get.

Option C: Both A + B

Redact at the source (config.get) and add a redaction layer to session persistence. Defense in depth.

Additional Context

  • openclaw security audit does not check for this issue
  • Session JSONL files can grow large and are rarely reviewed by users
  • This was discovered during development of GardaClaw, a security scanning skill for OpenClaw

Environment

  • OpenClaw 2026.2.6
  • macOS (arm64)
  • Model: Claude Opus 4.6

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