Skip to content

maybeRecoverSuspiciousConfigRead should validate .bak is not polluted with redacted values before restoring #68423

Description

@qiqi-victor

Problem

OpenClaw's config self-healing mechanism (maybeRecoverSuspiciousConfigRead) restores from .bak when the main config file appears suspicious, but does not validate whether .bak itself has been polluted with redacted values.

This creates a pollution fixation chain:

  1. An agent accidentally writes redacted values ("***" or "<prefix>…<suffix>") back into the main config (e.g. by reading redacted tool output → json.loads → modifying a different field → json.dump)
  2. maintainConfigBackups copies the polluted main file to .bak as-is
  3. On next startup, maybeRecoverSuspiciousConfigRead restores from the polluted .bak
  4. Pollution is fixated — even manual repair of the main file gets overwritten on next restart

Evidence

Real incident on 2026-04-18: A Hermes agent wrote redacted "***" into openclaw.json apiKey fields. OpenClaw's backup chain .bak / .bak.1 / .bak.2 / .bak.3 / .bak.4 all got polluted (3 apiKey fields each). Only a backup from a week prior (bak-20260409-114830) was still clean. The system was 401-failing for ~14 hours before root cause was identified.

Detection command:

grep -cE '"apiKey"\s*:\s*"(\*\*\*|[^"]*…[^"]*)"' ~/.openclaw/openclaw.json.bak*

Proposed Fix

1. Validate backups before restoring

function validateBackupNotPolluted(bakPath: string): { clean: boolean; pollutedFields?: string[] } {
  const content = fs.readFileSync(bakPath, 'utf-8');
  const REDACT_PATTERNS = [
    { name: 'triple-asterisk', re: /"apiKey"\s*:\s*"\*\*\*"/g },            // Hermes-style redact
    { name: 'unicode-ellipsis', re: /"apiKey"\s*:\s*"[^"]*\u2026[^"]*"/g }  // OpenClaw maskApiKey
  ];
  const hits = REDACT_PATTERNS.flatMap(p => [...content.matchAll(p.re)].map(() => p.name));
  return { clean: hits.length === 0, pollutedFields: hits };
}

// In maybeRecoverSuspiciousConfigRead:
const result = validateBackupNotPolluted(bakPath);
if (!result.clean) {
  logger.error(`Refusing to restore from polluted backup ${bakPath}: ${result.pollutedFields}`);
  // Surface to user via channel alert, wait for human intervention
  return;
}

2. Validate before creating backups (prevent pollution spread)

In maintainConfigBackups, also run validateBackupNotPolluted on the source file before copying. If polluted, skip backup creation and alert — keep the last known-clean .bak intact.

3. Multi-level backup rotation

Retain more than one level of history (e.g. .bak / .bak.daily / .bak.weekly) so accidental pollution of recent backups still leaves a clean recovery path.

4. openclaw config validate CLI

Add a command that scans the main config + all backups for redact patterns and reports pollution state. Helps users detect silent pollution before it causes 401 storms.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:data-lossCan lose, corrupt, or silently drop user/session/config data.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions