T09 · Insecure Skill Coding Practices
- Location
model_switcher.py:424- Finding
Provider API Keys Persisted and Duplicated in Plaintext
- Content
View full analysis
Vulnerability Details
File Location:
model_switcher.py, lines 424-451
Vulnerability Type: Plaintext storage and duplication of sensitive credentials
Risk Level: MediumVulnerable Code
python env_val = os.environ.get(env_key, "") if not env_val: print(f" ❌ Environment variable {env_key} is not set") sys.exit(1) cfg = load_config() if provider not in cfg.get("models", {}).get("providers", {}): cfg.setdefault("models", {}).setdefault("providers", {})[provider] = {} cfg["models"]["providers"][provider]["apiKey"] = env_val save_config(cfg) print(f" ✅ Added to models.providers.{provider}.apiKey") # Also update every agent's auth profile for agent in cfg.get("agents", {}).get("list", []): aid = agent["id"] apf_path = HOME / ".openclaw" / "agents" / aid / "agent" / "auth-profiles.json" if apf_path.exists(): with open(apf_path) as f: apf = json.load(f) profile = f"{provider}:default" apf.setdefault("profiles", {})[profile] = { "type": "api_key", "provider": provider, "key": env_val } _atomic_write_json(apf_path, apf) print(f" ✅ {aid}: auth-profiles updated")The relevant configuration writer does not enforce restrictive permissions:
python def save_config(cfg): with open(CONFIG_PATH, "w") as f: json.dump(cfg, f, indent=2, ensure_ascii=False)Technical Analysis
The
add-keycommand reads an API credential from an environment variable and writes its plaintext value into~/.openclaw/openclaw.json. It then duplicates the same credential into every existing agent'sauth-profiles.json.Neither
save_config()nor_atomic_write_json()explicitly applies a restrictive file mode, validates file ownership, or rejects symbolic links. The resulting confidentiality therefore depends on the permissions of existing f ...[truncated 1530 chars]- Remediation
View remediation
Remediation Suggestions
- Prefer storing environment-variable references or secret-manager identifiers instead of copying credential values into JSON.
- If plaintext persistence is required by OpenClaw, minimize duplication and maintain one protected credential source rather than copying the key into every agent profile.
- Create credential-bearing files with mode
0600and ensure containing directories are accessible only to the owning user. - Before reading or replacing a file, verify that it is a regular file owned by the expected user and reject symbolic links.
- Preserve or explicitly harden permissions during atomic replacement. Create the temporary file with restrictive permissions and use an atomic same-filesystem replacement.
- Audit backup, diagnostic, logging, and support-bundle workflows to ensure these configuration files are excluded or redacted.
- Document secure credential rotation and remove all duplicated copies when a key is revoked.
- Add automated tests that verify restrictive permissions, symlink rejection, and the absence of credentials in command output.
