Back to skill

Security audit

OpenClaw Update Runbook

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent OpenClaw update runbook, but parts of its troubleshooting guidance can expose live channel tokens in terminal or agent transcripts.

Review before installing. Use it only for explicit OpenClaw maintenance tasks, require confirmation before updates/restarts/config edits, and do not run token-inspection commands that print raw values. If a token has already appeared in a transcript, support ticket, log, or backup, rotate it and remove plaintext fallbacks after recovery.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
references/failure-patterns.md:482
Finding
Authentication Tokens Can Be Printed and Disclosed Through Support Artifacts<![CDATA[ ## Vulnerability Details **File Location**: `references/failure-patterns.md`, lines 482–498; related unsafe inspection command at lines 231–232 **Vulnerability Type**: Sensitive information exposure through diagnostic output **Risk Level**: High ### Vulnerable Snippet ```text What to inspect: - the raw bytes of the env line, not just the masked output: ``` node -e 'const fs=require("fs"); const p=process.env.HOME+"/.openclaw/service-env/<service-env-file>"; const l=fs.readFileSync(p,"utf8").split("\n").find(l=>l.startsWith("export <TOKEN_ENV_NAME>=")); console.log(JSON.stringify(l))' ``` - a clean line has a single shell-quoted token value with no inner literal double quotes - a corrupted line has literal `"` characters just inside the shell quotes - check every `*_TOKEN` / `*_API_KEY` line in the env file the same way; the same writer emits all of them Recovery: - back up the env file: `cp <env> <env>.bak-token-fix-<date>` - rewrite the affected lines using the value from `secrets.json` (which is the canonical clean value), shell-single-quoted with no inner JSON wrapping; only safe if the secret itself contains no single quotes (almost always the case for API tokens) - restart the gateway through the host service manager - re-run `openclaw channels status --deep` and confirm the channel reconnects Why it matters: - this is a packaging defect in the env-file writer, not operator drift; the local fix is fragile because the next regeneration will re-corrupt the file - share upstream or with support: exact line bytes, the source `secrets.json` value type (string), and the affected host version ``` A related command directly prints the configured Discord token: ```text - To disambiguate, inspect the actual config field directly: - `node -e 'const c=JSON.parse(require("fs").readFileSync(process.env.HOME+"/.openclaw/openclaw.json","utf8")); console.log(typeof c.channels?.discord?.token, c.channels?.discord?.token)'` ``` ### Technical Analysis ...[truncated 2630 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace value-printing commands with structural checks that never emit secret contents. - Report only whether the variable exists. - Report whether unwanted inner quotes are present. - Report the value length only if operationally necessary. - Avoid hashes unless there is a specific comparison need, since hashes of low-entropy secrets may also be sensitive. 2. Change the configuration inspection command to print only the field type: ```sh node -e 'const c=JSON.parse(require("fs").readFileSync(process.env.HOME+"/.openclaw/openclaw.json","utf8")); console.log(typeof c.channels?.discord?.token)' ``` 3. For environment-line validation, parse the assignment locally and emit a boolean result such as: ```text token variable found: yes unexpected inner double quotes: yes ``` 4. Replace the instruction to share “exact line bytes” with a requirement to share a redacted representation, for example: ```text export CHANNEL_TOKEN='<redacted>' outer shell quotes: present inner literal double quotes: present source value type: string ``` 5. Add an explicit warning that command output generated by older versions of the runbook may contain live credentials and must not be uploaded or pasted into reports. 6. If a token has already appeared in an Agent transcript, terminal recording, support ticket, or other external artifact: - Revoke and rotate it. - Remove the artifact where possible. - Review channel or API logs for unauthorized use. - Update the sanitized handoff notes with the rotation time, but not the replacement value. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/failure-patterns.md:200
Finding
Runbook Recommends Persisting Channel Tokens in Plaintext Configuration<![CDATA[ ## Vulnerability Details **File Location**: `references/failure-patterns.md`, lines 200–227 **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: Medium ### Vulnerable Snippet ```text Observed workaround: - add the token to the service env from the existing local secret source - remove the broken channel token config field so the channel falls through to the env-token path - restart gateway and verify `channels status` reports `token:env` and connected Why it matters: - schema validation and secrets audit can both pass while the channel runtime still cannot consume the SecretRef - this can leave a channel integration down after an update even though the secret exists Confirmed regression scope: - Reproduced cleanly across multiple adjacent channel-plugin versions with a SecretRef pointing at a valid `secrets.json` entry. - `openclaw secrets audit` reports `unresolved=0`, `openclaw secrets reload` says "Secrets reloaded.", but the channel plugin still throws `unresolved SecretRef ... Resolve this command against an active gateway runtime snapshot before reading it.` at startup. - Sibling plugins using the same SecretRef shape (e.g. brave's `/brave_api_key`) resolve fine — the bug is plugin-side, not in the secrets layer. - Pragmatic workaround (when env fallback isn't available): inline the literal token into the affected channel token field. This adds one entry to `secrets audit --plaintext` findings but restores the channel. Plan to revert once upstream `@openclaw/discord` ships a fix that resolves SecretRefs against the runtime snapshot. ``` ### Technical Analysis The runbook acknowledges that the proposed fallback causes `secrets audit --plaintext` to report a finding, but nevertheless recommends embedding the literal authentication token into the channel configuration. Moving a token from a SecretRef or dedicated secret source into ordinary configuration broadens the number of places and processes that may access it. The ...[truncated 1860 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Keep the supported secret provider or a restricted service-environment file as the preferred recovery mechanism. Do not present inline plaintext configuration as a routine workaround. 2. If no secure recovery path exists, require explicit operator approval before using a plaintext fallback and clearly label it as temporary and high-risk. 3. Before applying any temporary fallback: - Back up configuration only to an access-restricted location. - Ensure both configuration and backup files are owned by the gateway service user. - Set permissions to `0600` or a platform-equivalent restrictive ACL. - Confirm the files are excluded from source control and support bundles. 4. Record a mandatory rollback condition and deadline. Once a fixed plugin is installed: - Restore the SecretRef. - Remove the literal value from active configuration. - Remove or securely handle plaintext backups. - Re-run `openclaw secrets audit --plaintext`. - Confirm the channel still works through the secret provider. 5. Rotate the token after removing the plaintext workaround if it may have appeared in backups, transcripts, diagnostics, or externally shared artifacts. 6. Update the runbook so type inspection never prints the token value. It should report only whether the field is a string or SecretRef object. 7. Prefer a narrowly scoped service-environment fallback with strict permissions over embedding the token in general configuration, provided that this is supported by the affected integration. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (16)

Ae1

High
Category
analysis-evasion
Content
ngle file. It expects `references/failure-patterns.md` to exist locally beside `SKILL.md` inside the same skill bundle.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Self-Modification

High
Category
Rogue Agent
Content
- cron/session isolation and channel-lane ownership
- runtime performance
- command-path and update-channel assumptions
- self-update hazards when an agent updates the gateway that is running it
- supply-chain and package-integrity spot checks after plugin/npm churn

## Quick workflow
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- cron/session isolation and channel-lane ownership
- runtime performance
- command-path and update-channel assumptions
- self-update hazards when an agent updates the gateway that is running it
- supply-chain and package-integrity spot checks after plugin/npm churn

## Quick workflow
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Credential Access

High
Category
Privilege Escalation
Content
- this can leave a channel integration down after an update even though the secret exists

Confirmed regression scope:
- Reproduced cleanly across multiple adjacent channel-plugin versions with a SecretRef pointing at a valid `secrets.json` entry.
- `openclaw secrets audit` reports `unresolved=0`, `openclaw secrets reload` says "Secrets reloaded.", but the channel plugin still throws `unresolved SecretRef ... Resolve this command against an active gateway runtime snapshot before reading it.` at startup.
- Sibling plugins using the same SecretRef shape (e.g. brave's `/brave_api_key`) resolve fine — the bug is plugin-side, not in the secrets layer.
- Pragmatic workaround (when env fallback isn't available): inline the literal token into the affected channel token field. This adds one entry to `secrets audit --plaintext` findings but restores the channel. Plan to revert once upstream `@openclaw/discord` ships a fix that resolves SecretRefs against the runtime snapshot.
Confidence
92% confidence
Finding
The runbook explicitly recommends a workaround that inlines a literal channel token into config when SecretRef resolution is broken. That weakens secret handling by moving credentials from a managed secrets store into plaintext configuration, increasing exposure through config files, backups, logs, and support artifacts.

Credential Access

High
Category
Privilege Escalation
Content
Symptom:
- a previously working channel returns auth failure from the upstream API immediately after a host upgrade, even though `channels status` reports `token:env` and the channel was healthy before the upgrade
- `secrets audit` reports `unresolved=0` and the underlying value in `secrets.json` is unchanged
- the channel reconnects fine if you manually re-paste the token into the env file

Root cause:
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Symptom:
- a previously working channel returns auth failure from the upstream API immediately after a host upgrade, even though `channels status` reports `token:env` and the channel was healthy before the upgrade
- `secrets audit` reports `unresolved=0` and the underlying value in `secrets.json` is unchanged
- the channel reconnects fine if you manually re-paste the token into the env file

Root cause:
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- the channel reconnects fine if you manually re-paste the token into the env file

Root cause:
- the service-env writer JSON-encodes string values from `secrets.json` (wrapping them in `"`) and **then** shell-single-quotes the result for the env file
- the resulting line looks like `export CHANNEL_TOKEN='"<token>"'` — the outer single quotes are correct shell quoting, but the inner literal `"` characters become part of the value when the env file is sourced
- the upstream API receives a token with stray leading and trailing `"` chars and rejects it
- the bug only surfaces the next time the env file is regenerated (a host upgrade, certain `doctor --fix` runs, plugin reinstalls), so it presents as "the upgrade broke the channel" rather than a config drift
Confidence
90% confidence
Finding
The runbook explains how secrets are written into a service env file and shows the exact malformed export shape containing the token value. Even though framed as debugging guidance, it normalizes direct handling of credential material in env files and risks accidental disclosure if operators copy raw lines into tickets, chats, or logs.

Credential Access

High
Category
Privilege Escalation
Content
Recovery:
- back up the env file: `cp <env> <env>.bak-token-fix-<date>`
- rewrite the affected lines using the value from `secrets.json` (which is the canonical clean value), shell-single-quoted with no inner JSON wrapping; only safe if the secret itself contains no single quotes (almost always the case for API tokens)
- restart the gateway through the host service manager
- re-run `openclaw channels status --deep` and confirm the channel reconnects
Confidence
94% confidence
Finding
This recovery step tells operators to rewrite env-file secret lines directly from `secrets.json`, which creates a manual secret-handling path outside the secret-management mechanism. That increases the risk of plaintext credential exposure, persistence in shell history or backups, and inconsistent secret state across future regenerations.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The skill enables implicit invocation without any visible activation constraints, allowing the agent to auto-select this runbook in contexts that only loosely resemble update or debugging tasks. Because the skill is an operational runbook for updating and repairing a system, unintended invocation could trigger risky maintenance guidance in the wrong context, increasing the chance of disruptive or unsafe actions.

Session Persistence

Medium
Category
Rogue Agent
Content
- prefer reconciling install records or reinstalling exact target versions before trusting `openclaw plugins update --all`

Refinement:
- `openclaw plugins registry --refresh` does NOT rewrite the install record's `spec` field. It refreshes `hostContractVersion` and compatibility data only.
- After a refresh, install records can still carry pinned specs like `<plugin>@<older-version>` even when the disk version is `<newer-version>`. `plugins update --all` will then **downgrade** the on-disk plugin to match the pinned spec.
- Correct sequence to actually move a third-party plugin forward:
  1. `openclaw plugins update <id> @<scope>/<pkg>@latest` (note: `update` accepts an explicit spec; this rewrites the install record's spec).
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
- reconnecting with a fresh ssh shows the box has actually completed most or all of the work — versions bumped, gateway running, plugins on disk

What's happening:
- when one of the inner steps restarts launchd or replaces the wrapper script the gateway plist sources, the parent shell association can break and the local ssh client stops receiving stdout, even though the remote `zsh -c '...'` keeps running detached and finishes the script.
- the remote orphan can persist as a `zsh -c` process for minutes after the parent ssh exits.

What to inspect:
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
- reconnecting with a fresh ssh shows the box has actually completed most or all of the work — versions bumped, gateway running, plugins on disk

What's happening:
- when one of the inner steps restarts launchd or replaces the wrapper script the gateway plist sources, the parent shell association can break and the local ssh client stops receiving stdout, even though the remote `zsh -c '...'` keeps running detached and finishes the script.
- the remote orphan can persist as a `zsh -c` process for minutes after the parent ssh exits.

What to inspect:
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
- reconnecting with a fresh ssh shows the box has actually completed most or all of the work — versions bumped, gateway running, plugins on disk

What's happening:
- when one of the inner steps restarts launchd or replaces the wrapper script the gateway plist sources, the parent shell association can break and the local ssh client stops receiving stdout, even though the remote `zsh -c '...'` keeps running detached and finishes the script.
- the remote orphan can persist as a `zsh -c` process for minutes after the parent ssh exits.

What to inspect:
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
Recovery:
- back up the env file: `cp <env> <env>.bak-token-fix-<date>`
- rewrite the affected lines using the value from `secrets.json` (which is the canonical clean value), shell-single-quoted with no inner JSON wrapping; only safe if the secret itself contains no single quotes (almost always the case for API tokens)
- restart the gateway through the host service manager
- re-run `openclaw channels status --deep` and confirm the channel reconnects
Confidence
78% confidence
Finding
Manually rewriting service env files can create persistent plaintext secret state outside the intended secrets layer, and that state survives restarts until regenerated. While not classic session persistence, it does establish durable sensitive configuration in a less controlled location.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
managed service label
- common package-manager paths such as `/usr/local/node24/bin/openclaw`,
  `/opt/homebrew/bin/openclaw`, and `~/.local/bin/openclaw`
- whether `sudo -H -u <service-user> env PATH=<package-bin>:$PATH openclaw ...`
  reaches the same config and state dir as the gateway

Recovery:
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
Why it matters:
- the login user's missing PATH is not proof OpenClaw is uninstalled
- running `doctor --fix` as the wrong user can inspect or create the wrong
  `~/.openclaw` tree
- service health, config, plugins, and sessions must be audited from the same
  user context as the managed gateway
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Static analysis

No suspicious patterns detected.