Skip to content

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

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

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions