Back to skill

Security audit

Clawpulse Bridge

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent as a ClawPulse status bridge, but it exposes a network status service and credentials more broadly than its protections and warnings justify.

Review before installing. Only run it on a machine and network where exposing assistant status is acceptable, prefer 127.0.0.1 or a tightly controlled Tailscale interface, treat printed tokens and QR codes as secrets, and stop/remove the background processes when no longer needed.

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
scripts/clawpulse-monitor.py:165
Finding
Unauthenticated Internal Status Endpoint Exposes Operational Metadata<![CDATA[ ## Vulnerability Details **File Location**: `scripts/clawpulse-monitor.py:165-168` **Vulnerability Type**: Authentication bypass and sensitive information exposure **Risk Level**: High ### Vulnerable Code ```python def do_GET(self): if self.path == "/internal": with state_lock: self._json(200, dict(state)) return if self.path not in ["/health", "/status"]: self._json(404, {"error": "not_found"}) return auth = self.headers.get("Authorization", "") if APP_TOKEN and auth != f"Bearer {APP_TOKEN}": self._json(401, {"error": "unauthorized"}) return ``` The service also binds to every available network interface by default: ```python BIND_HOST = os.environ.get("MONITOR_BIND_HOST", "0.0.0.0") ``` ### Technical Analysis The `/internal` route is processed before the bearer-token authentication check. Consequently, requests to this endpoint return a complete copy of the monitor's internal state without verifying `MONITOR_TOKEN`. The disclosed state includes: - Assistant name - Online and work status - Current and daily token usage - Thought/status text - Source and monitor timestamps - Failure and recovery counters - Last state-change time - Raw usage counters - Activity-state transition counters Unlike the generated bridge, the monitor does not enforce a loopback or Tailscale source-address allowlist. Because its default bind address is `0.0.0.0`, `/internal` can be reached by any host with network access to TCP port 8788. This behavior contradicts the Skill's declared token-protected status model and exceeds the minimum access necessary for the application. ### Attack Path 1. The user starts the monitor through `setup_clawpulse_monitor.sh --apply`. 2. The monitor binds to `0.0.0.0:8788` by default. 3. An attacker on a reachable LAN, Tailscale network, exposed host interface, or forwarded port discovers TCP port 8788. 4. The attacker sends the following request without a ...[truncated 780 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply authentication before dispatching any endpoint that exposes state: ```python def do_GET(self): if self.path not in ["/health", "/status", "/internal"]: self._json(404, {"error": "not_found"}) return auth = self.headers.get("Authorization", "") if not APP_TOKEN or auth != f"Bearer {APP_TOKEN}": self._json(401, {"error": "unauthorized"}) return ``` 2. Prefer removing `/internal` if it is not required by the ClawPulse application. 3. If the endpoint is required for local diagnostics, restrict it to loopback clients and bind a separate diagnostic listener to `127.0.0.1`. 4. Add the same loopback and Tailscale source-IP validation used by the bridge. 5. Change the monitor's default bind address to `127.0.0.1`. Require an explicit option to expose it remotely. 6. Return a minimal diagnostic schema rather than the complete internal state. Exclude counters and metadata not required by the client. 7. Add tests confirming that `/internal`, `/health`, and `/status` all reject missing or invalid credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/clawpulse-monitor.py:11
Finding
Bridge Bearer Token Can Be Sent to an Arbitrary or Cleartext Destination<![CDATA[ ## Vulnerability Details **File Location**: `scripts/clawpulse-monitor.py:11-12, 46-50` **Vulnerability Type**: Sensitive credential transmission over an untrusted or unencrypted channel **Risk Level**: Medium ### Vulnerable Code ```python BRIDGE_URL = os.environ.get("BRIDGE_URL", "http://127.0.0.1:8787/health") BRIDGE_TOKEN = os.environ.get("BRIDGE_TOKEN", "") ``` ```python def fetch_bridge(): req = urllib.request.Request(BRIDGE_URL) if BRIDGE_TOKEN: req.add_header("Authorization", f"Bearer {BRIDGE_TOKEN}") with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as r: raw = r.read() obj = json.loads(raw.decode("utf-8")) return obj ``` The setup script accepts the destination directly from the environment and exports the privileged bridge token to the monitor: ```bash BRIDGE_URL="${BRIDGE_URL:-http://127.0.0.1:8787/health}" ``` ```bash set -a source "$ENV_FILE" export WORKSPACE MONITOR_BIND_HOST MONITOR_PORT BRIDGE_URL BRIDGE_TOKEN="$STATUS_TOKEN" set +a nohup python3 "$MONITOR_PY" >"$LOG_FILE" 2>&1 & ``` Client-facing endpoints are also generated with plain HTTP: ```bash if [[ -n "$TS_DNS" ]]; then ENDPOINT="http://${TS_DNS}:${MONITOR_PORT}/health" elif [[ -n "$TS_IP" ]]; then ENDPOINT="http://${TS_IP}:${MONITOR_PORT}/health" else ENDPOINT="http://127.0.0.1:${MONITOR_PORT}/health" fi ``` ### Technical Analysis `BRIDGE_URL` is not restricted by scheme, hostname, IP range, or origin. Every bridge request automatically includes `STATUS_TOKEN` as a bearer credential. If the configured URL points to an external HTTP server, that server receives the bridge token in the `Authorization` header. The default bridge URL uses loopback and is appropriate for the normal same-host deployment. The risk arises because the configuration permits arbitrary remote destinations without requiring TLS or warning that the bridge credential will be forwarded. The generated mobile endpoint also uses HTTP. Tailscale can prov ...[truncated 1924 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict `BRIDGE_URL` to loopback by default and reject unexpected destinations: - Permit `127.0.0.1` and `::1`. - Optionally permit documented Tailscale address ranges. - Reject public, unspecified, multicast, and link-local destinations as appropriate. 2. Require HTTPS for every non-loopback bridge URL. 3. Parse and validate the URL before constructing the request. Enforce an expected path such as `/health` and reject embedded user information or unsupported schemes. 4. Do not attach `BRIDGE_TOKEN` unless the validated destination matches an explicit allowlist. 5. Disable automatic cross-origin credential forwarding. Treat redirects as untrusted and either reject them or revalidate the destination before sending credentials. 6. Serve client-facing endpoints over HTTPS. If deployment relies on Tailscale, use an authenticated HTTPS mechanism such as Tailscale Serve rather than exposing a raw HTTP listener. 7. Clearly document that plain LAN HTTP does not protect bearer tokens from interception. 8. Rotate both `STATUS_TOKEN` and `MONITOR_TOKEN` after any suspected misconfiguration or network interception. 9. Consider using separate environment files or process-scoped secret injection so the monitor receives only the credentials it strictly requires. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (21)

Tainted flow: 'req' from os.environ.get (line 47, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(BRIDGE_URL)
    if BRIDGE_TOKEN:
        req.add_header("Authorization", f"Bearer {BRIDGE_TOKEN}")
    with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as r:
        raw = r.read()
    obj = json.loads(raw.decode("utf-8"))
    return obj
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The /internal endpoint returns the full monitor state without checking APP_TOKEN, even though /health and /status are token-protected. Because the state includes thought text, token usage, timestamps, assistant identity, and internal health data, anyone who can reach the service can query sensitive operational information, which is especially risky given the broad default bind setting.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The script claims a safer dry-run/default posture, but actually defaults to BIND_HOST=0.0.0.0 and prints setup secrets to stdout, including the bearer token and setup link/QR contents. In a setup/integration skill, this is dangerous because it can unintentionally expose a remotely reachable status service and leak credentials via terminal history, screenshots, logs, or shared sessions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill describes shell, network, environment, and file operations but does not declare any explicit tool scope or permissions boundary. In an agent setting, this weakens least-privilege controls and can let the skill invoke broader capabilities than a user may reasonably expect, increasing the chance of unsafe file, network, or command execution during setup and troubleshooting.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill explicitly recommends a remote-ready bind to `0.0.0.0` and states that setup prints bearer tokens and QR codes, but it does not foreground the risks of exposing an HTTP service and sensitive credentials on LAN/Tailscale. If the endpoint is reachable by unintended peers or the token/QR is observed in logs, terminal history, screenshots, or shared sessions, an attacker could query the status bridge or reuse the credential.

Session Persistence

Medium
Category
Rogue Agent
Content
Then configure app with monitor endpoint/token (from script output or QR), not bridge token.

## Troubleshooting
- HTTP blocked on iOS: ensure app Info.plist has ATS exception for development, or use HTTPS.
- 401 auth error: token mismatch; regenerate and reapply.
- 403 forbidden: source IP is not local/Tailscale; confirm the device is connected to Tailscale.
- Timeout: check bridge process and network reachability.
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.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The server binds to 0.0.0.0 by default, exposing the monitoring API on all network interfaces unless the operator overrides it. In the context of a status bridge that carries internal operational metadata and also contains an unauthenticated /internal endpoint, this expands the reachable attack surface and increases the chance of unauthorized access.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
PY
)
  printf 'STATUS_TOKEN=%s\n' "$TOKEN" > "$ENV_FILE"
  chmod 600 "$ENV_FILE"
else
  source "$ENV_FILE"
  TOKEN="${STATUS_TOKEN:-}"
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
PY
)
  printf 'STATUS_TOKEN=%s\n' "$TOKEN" > "$ENV_FILE"
  chmod 600 "$ENV_FILE"
else
  source "$ENV_FILE"
  TOKEN="${STATUS_TOKEN:-}"
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The generated Python service returns fixed Chinese strings such as "连接异常", "工作中", and Chinese thought messages in its API payload. This imposes a specific language on all consumers without any opt-in, configuration, or justification for a locale-specific deployment.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The setup link is generated from $STATUS_TOKEN, but the shell script stores the token in TOKEN unless the env file is sourced later, so the displayed QR/setup URL can contain an empty or incorrect token. This can lead users to deploy a broken configuration, disable safeguards while troubleshooting, or assume authentication is working when it is not.

Session Persistence

Medium
Category
Rogue Agent
Content
source "$ENV_FILE"
export BIND_HOST PORT WORKSPACE
set +a
nohup python3 "$BRIDGE_PY" >"$LOG_FILE" 2>&1 &
sleep 1

echo "ClawPulse bridge running"
Confidence
65% 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.

Intent-Code Divergence

Medium
Confidence
99% confidence
Finding
The runtime note states local-only mode is the safer default, but the script default is remote binding on 0.0.0.0, which creates a misleading trust boundary for operators. This kind of documentation/behavior mismatch is security-relevant because users may assume the service is local-only and expose a token-protected endpoint on the network without realizing it.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The script binds the monitor to 0.0.0.0 by default, exposing the token-protected health endpoint on all network interfaces rather than restricting it to localhost or a Tailscale-specific address. In the context of a status bridge carrying authentication tokens and machine status metadata, this materially increases the attack surface and can allow unintended remote access if host firewalling is weak or absent.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The script overwrites the environment file containing STATUS_TOKEN and MONITOR_TOKEN without an explicit confirmation step, which can silently rotate or replace credentials. In an integration setup flow, that can break existing clients, invalidate expected trust relationships, or cause accidental credential loss during routine use.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
exit 1
fi
cp "$MONITOR_SRC" "$MONITOR_PY"
chmod 700 "$MONITOR_PY"

TS_DNS=""
TS_IP=""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
exit 1
fi
cp "$MONITOR_SRC" "$MONITOR_PY"
chmod 700 "$MONITOR_PY"

TS_DNS=""
TS_IP=""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
source "$ENV_FILE"
export WORKSPACE MONITOR_BIND_HOST MONITOR_PORT BRIDGE_URL BRIDGE_TOKEN="$STATUS_TOKEN"
set +a
nohup python3 "$MONITOR_PY" >"$LOG_FILE" 2>&1 &
sleep 1

echo "ClawPulse monitor running"
Confidence
80% confidence
Finding
The script intentionally establishes a persistent background service using nohup, causing the monitor to continue running after the setup command exits. In this skill's context, persistence matters because the monitor serves a network endpoint and handles bridge credentials, so an operator may unintentionally leave a long-lived exposed service active.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
This Python file contains user-facing natural-language strings such as "连接异常", "监控器启动中。", and other Chinese status messages. The file does not offer a language choice or document that the monitor is intentionally region-specific, which conflicts with the policy against forcing a specific language without user opt-in.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The monitor emits user-visible work-state values and recovery messages only in Chinese, including "工作中", "闲置", and "监控器判定离线,等待恢复。". Because no opt-in or locale selection is present, these strings indicate a language policy violation in natural-language output.

Missing User Warnings

Low
Confidence
79% confidence
Finding
Starting a long-running background process is a safety-relevant operation for code files, and this script does not provide a warning or confirmation immediately before executing nohup python3. Although the dry-run mentions starting/restarting the monitor, the apply path lacks a direct disclosure right before process launch.

Static analysis

No suspicious patterns detected.