Back to skill

Security audit

Always On Agent Ops

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed operations helper for keeping an OpenClaw agent running, with sensitive but user-directed cron, credential, polling, and cleanup behavior.

Install only on a dedicated, physically secured host. Use scoped queue tokens, keep secrets.env chmod 600 and out of git, verify every cron job ID and tool profile before enabling unattended runs, and confirm that the queue and notification destinations are systems you are authorized to use.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill declares no explicit permissions while clearly requiring shell execution, file mutation, cron control, and network-capable helper scripts. This mismatch increases the chance of the skill being invoked or approved under an incomplete trust model, leading operators to underestimate its authority.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The skill description frames the behavior as keeping an agent alive, but the body also performs log deletion, workspace measurement, authenticated outbound polling, and event-to-job dispatch orchestration. This broader behavior materially expands the attack and impact surface beyond what a user may reasonably consent to from the description alone.

External Transmission

Medium
Category
Data Exfiltration
Content
scripts/oauth-expiry-check.sh scripts/purge-retention.sh   # cron-message.sh is sourced: 644
# The scripts read nothing from the network until this file exists.
cat > scripts/secrets.env <<'EOF'
QUEUE_URL=https://api.acme-corp.example/queue
API_TOKEN=replace-me
EOF
chmod 600 scripts/secrets.env      # never commit it; it is not shipped with this skill
Confidence
78% confidence
Finding
The skill is designed to perform outbound authenticated HTTP polling to a configured queue endpoint, which is an external data transmission path. While disclosed, this still creates a real exfiltration and dependency surface because operational metadata, queue contents, and authorization tokens may traverse third-party infrastructure.

Credential Access

High
Category
Privilege Escalation
Content
| Credentials | A gateway token (loopback only), a provider auth profile, and any queue Bearer you add. All in `~/.openclaw/` or the OS keychain — never in git, never in the agent workspace. |
| Data persisted | Cron job definitions + run state (`~/.openclaw/cron/jobs.json`), the agent workspace and its memory files, logs. |
| Retention — **executed, not declared** | Logs: **30 days**, deleted by `scripts/purge-retention.sh` (`cr-22`, weekly). Orphaned lock dirs: **15 min** TTL, reclaimed by the dispatcher. Cron run state: life of the job — it goes when you delete the job. Workspace/memory: **no automatic purge** — `cr-22` reports its size so growth cannot stay invisible; the number you keep is yours to set. |
| Network out | Only what YOU configure: the queue you poll, the notification channel you name. Default is no network — with no `secrets.env`, the preflight emits `Human action required` and exits 0. |
| Network in | None. The gateway binds loopback. The optional loopback listener (below) is opt-in and never leaves the host. |
| Notifications | `${NOTIFY_CHANNEL}` receives short status lines. They leave the machine. Never put secrets, PII, or payload dumps in them. |
Confidence
80% confidence
Finding
The skill requires access to sensitive credentials stored under ~/.openclaw or the OS keychain, including gateway tokens and provider auth profiles. Any skill with shell/file access and awareness of these locations materially increases credential exposure risk if misused or combined with another flaw.

Credential Access

High
Category
Privilege Escalation
Content
chmod +x scripts/preflight-queue.sh scripts/event-dispatch.sh \
         scripts/oauth-expiry-check.sh scripts/purge-retention.sh   # cron-message.sh is sourced: 644
# The scripts read nothing from the network until this file exists.
cat > scripts/secrets.env <<'EOF'
QUEUE_URL=https://api.acme-corp.example/queue
API_TOKEN=replace-me
EOF
Confidence
81% confidence
Finding
Use of a .env-style secret file is a real credential-handling risk because such files are frequently copied, backed up, or accidentally committed despite warnings. The skill acknowledges this risk but still normalizes the pattern as part of setup.

Credential Access

High
Category
Privilege Escalation
Content
| Variable | Where it lives | Who substitutes it |
|---|---|---|
| `QUEUE_URL`, `API_TOKEN` | `scripts/secrets.env` | the shell, at script run time |
| `OPENCLAW_HOME`, `OPENCLAW_AGENT_ID` | environment (optional) | the shell; default `~/.openclaw` and `main` |
| `GATEWAY_TOKEN`, `MODEL_PRIMARY`, `MODEL_FALLBACK`, `RUNTIME_ID`, `PROVIDER`, `*_PLUGIN`, `CHANNEL`, `NOTIFY_CHANNEL` | `openclaw.json` / job definitions below | **you, by hand** — OpenClaw does not interpolate `${...}` in its config. A literal `${GATEWAY_TOKEN}` left in the file is a literal token, and strict validation will not save you from it. |
| *(third-party, beyond `metadata.requires`)* | an authenticated queue endpoint you own or may call, a model provider account, a notification channel | you, before the first run |
Confidence
79% confidence
Finding
The skill explicitly maps QUEUE_URL and API_TOKEN to scripts/secrets.env, confirming that runtime secrets are expected to be read from disk by shell scripts. This design broadens the blast radius of any local file-read capability and makes secret handling part of normal execution flow.

Credential Access

High
Category
Privilege Escalation
Content
# counts; an API renaming `items` to `results` degrades to a count, not a crash loop). "Counted zero"
# is NOT "found nothing to count": an empty array is 0, but no-count-and-no-array — a 200 carrying
# {"error":"unauthorized"}, 502 HTML, a captive portal — is an Error. A green "Nothing to do" while the
# queue fills is worse than a crash. No secrets.env, dead host, junk body -> one status line, exit 0,
# never a bare shell error. Hence no `set -e`: a broken preflight must still produce a line.
# Output: pf-10-queue - Nothing to do / Result: Queue empty. / ... / Details: count=0
# Output: pf-10-queue - Error / Result: Payload carries no count and no array... / Details: reason=no_countable_field
Confidence
75% confidence
Finding
The preflight logic's reliance on secrets.env means operational status and outbound polling depend on local credentials being present and readable. That is not inherently malicious, but it confirms credential-coupled automation where any compromise of the script path or local file access can expose or misuse tokens.

Credential Access

High
Category
Privilege Escalation
Content
chmod +x scripts/preflight-queue.sh scripts/event-dispatch.sh \
         scripts/oauth-expiry-check.sh scripts/purge-retention.sh   # cron-message.sh is sourced: 644
# The scripts read nothing from the network until this file exists.
cat > scripts/secrets.env <<'EOF'
QUEUE_URL=https://api.acme-corp.example/queue
API_TOKEN=replace-me
EOF
Confidence
81% confidence
Finding
Use of a .env-style secret file is a real credential-handling risk because such files are frequently copied, backed up, or accidentally committed despite warnings. The skill acknowledges this risk but still normalizes the pattern as part of setup.

Credential Access

High
Category
Privilege Escalation
Content
| Item | What this skill needs |
|---|---|
| Host access | A logged-in GUI desktop session, permanently. The daemon is not headless. |
| Credentials | A gateway token (loopback only), a provider auth profile, and any queue Bearer you add. All in `~/.openclaw/` or the OS keychain — never in git, never in the agent workspace. |
| Data persisted | Cron job definitions + run state (`~/.openclaw/cron/jobs.json`), the agent workspace and its memory files, logs. |
| Retention — **executed, not declared** | Logs: **30 days**, deleted by `scripts/purge-retention.sh` (`cr-22`, weekly). Orphaned lock dirs: **15 min** TTL, reclaimed by the dispatcher. Cron run state: life of the job — it goes when you delete the job. Workspace/memory: **no automatic purge** — `cr-22` reports its size so growth cannot stay invisible; the number you keep is yours to set. |
| Network out | Only what YOU configure: the queue you poll, the notification channel you name. Default is no network — with no `secrets.env`, the preflight emits `Human action required` and exits 0. |
Confidence
74% confidence
Finding
The skill requires access to credentials in the OS keychain, which is a sensitive capability. In the context of an always-on operational skill with shell and host access, keychain access significantly raises the consequences of misuse because it may unlock broader provider or gateway credentials.

Credential Access

High
Category
Privilege Escalation
Content
exit 0
fi
# shellcheck disable=SC1091
source "$DIR/secrets.env"

if [ -z "${QUEUE_URL:-}" ] || [ -z "${API_TOKEN:-}" ]; then
  cron_message "pf-10-queue" "Human action required" "QUEUE_URL or API_TOKEN missing from secrets.env." \
Confidence
78% confidence
Finding
This duplicate '.env' finding maps to the same real issue: the script sources a local env file as executable shell syntax. In this always-on agent context, compromise of that file could turn a routine cron preflight into persistent arbitrary code execution and credential exposure.

Credential Access

High
Category
Privilege Escalation
Content
exit 0
fi
# shellcheck disable=SC1091
source "$DIR/secrets.env"

if [ -z "${QUEUE_URL:-}" ] || [ -z "${API_TOKEN:-}" ]; then
  cron_message "pf-10-queue" "Human action required" "QUEUE_URL or API_TOKEN missing from secrets.env." \
Confidence
78% confidence
Finding
This duplicate '.env' finding maps to the same real issue: the script sources a local env file as executable shell syntax. In this always-on agent context, compromise of that file could turn a routine cron preflight into persistent arbitrary code execution and credential exposure.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
| Gateway won't boot after an edit | Strict validation rejected an unknown key | `openclaw doctor --fix`, read the stripped-key list and the `.bak` |
| Config edited, behaviour unchanged | Last-known-good restored, or `gateway.*`/`plugins.*` needs a restart | `openclaw gateway restart`, then verify live state |
| Persistent "unauthorized" on loopback | A `launchctl setenv` token overrides the config file | Unset it, restart |
| Dispatch always says `Blocked`, no job ever runs | A killed run (restart, sleep, `kill -9`) orphaned its lock dir; the channel looks healthy | `rm -rf "${TMPDIR:-/tmp}"/openclaw-event-*.lock`. The TTL reclaims it after `LOCK_TTL_MIN`; lower it if your jobs are short |
| `cr-23` says `ok` while every turn fails | Several profiles in one auth file, and a fresh one masked the dead one — the shipped checker takes the **earliest** expiry precisely for this | Read `field` in the JSON: it names the profile that decided. If you wrote your own checker, take the min — and make "cannot determine" a `warn`, never an `ok` |
| `cr-22` reports `Not an OpenClaw home` | `OPENCLAW_HOME` points somewhere without `openclaw.json` or `agents/` — a typo or a stale env var | Intended: the purge refuses any dir it cannot prove is an agent home rather than deleting on faith. Fix the variable |
| All runs fail overnight, no alert | OAuth expired headless | `cr-23` + human relogin; consider an API key |
Confidence
77% confidence
Finding
The documented remediation includes a wildcard rm -rf against ${TMPDIR:-/tmp}/openclaw-event-*.lock, which is destructive shell behavior operating on an environment-influenced path. Although scoped by prefix, unsafe deletion patterns can remove unintended files or be abused if path assumptions or naming guarantees fail.

Static analysis

No suspicious patterns detected.