Back to skill

Security audit

Wellness Hub

Security checks for vulnerabilities and agentic risk

Overview

This wellness hub is purpose-aligned, but it needs Review because it handles sensitive health data through public tunnels, local storage, and third-party skill installs without enough safeguards.

Install only if you are comfortable handling health data in this environment. Treat the bridge token as a password, avoid exposing the bridge longer than necessary, do not place its state directory on shared or broadly synced storage, review any source skill before installing it, and send digests only to private channels after checking what data they contain.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/wellness_push.py:33
Finding

Working-Directory-Relative Subprocess Allows Local Tool Hijacking

Content
View full analysis
str: cmd = [ sys.executable, "scripts/wellness_digest.py", "--date", args.date, "--tz", args.tz, "--render", args.render, "--channel", args.channel, ] for p in args.inputs: cmd += ["--in", p] if args.use_bridge: cmd += ["--use-bridge", "--bridge-dir", args.bridge_dir] if args.out: cmd += ["--out", args.out] res = subprocess.run(cmd, capture_output=True, text=True) ``` ### Technical Analysis The subprocess command identifies `wellness_digest.py` using the relative path `scripts/wellness_digest.py`. Python resolves that path against the caller's current working directory, not against the directory containing the legitimate `wellness_push.py` file. Although list-form invocation avoids shell metacharacter injection, it does not prevent executable or script substitution. If the helper is invoked while the current working directory is controlled by another user, a malicious file at `scripts/wellness_digest.py` will be executed instead of the intended sibling script. ### Attack Path 1. An attacker gains write access to a directory from which the victim may invoke `wellness_push.py`. 2. The attacker creates a malicious `scripts/wellness_digest.py` beneath that directory. 3. The victim invokes the legitimate `wellness_push.py` by absolute path or through an automation while retaining the attacker-controlled directory as the current working directory. 4. `subprocess.run()` resolves `scripts/wellness_digest.py` from that current directory. 5. The malicious Python file executes with the victim's account privileges. ### Impact Assessment Successful exploitation results in arbitrary local c ...[truncated 281 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wellness_bridge.py:222
Finding

Sensitive Health Records Are Written with Umask-Dependent Permissions

Content
View full analysis
None: Path(p).mkdir(parents=True, exist_ok=True) ``` ```python # Store payload target_dir = os.path.join(inbox_dir(), day) mkdirp(target_dir) fname = f"{int(time.time())}_{secrets.token_hex(4)}.json" out_path = os.path.join(target_dir, fname) with open(out_path, "w", encoding="utf-8") as f: json.dump(doc, f, indent=2, sort_keys=True) f.write("\n") ``` ### Technical Analysis The bearer-token file is explicitly changed to mode `0600`, but the inbox directories and uploaded JSON records receive no equivalent protection. Their permissions are consequently determined by the process umask. With a common umask of `0022`, directories may be created as `0755` and files as `0644`. This can make wellness records readable by other local users. The records may contain sleep history, weight, blood pressure, blood oxygen, glucose, heart rate, workout data, and free-form notes. The payload is also written directly to its final path rather than through an atomically created private temporary file, making permission and partial-write behavior less robust. ### Attack Path 1. The bridge runs on a shared or multi-user host with a permissive default umask. 2. A phone submits a valid health payload. 3. The bridge creates the date directory and JSON file using default filesystem permissions. 4. Another local account enumerates the predictable `~/.config/openclaw/wellness/bridge/inbox/` hierarchy. 5. If group or world permissions permit access, that account reads the stored health records. ### Impact Assessment The issue can disclose highly sensitive personal wellness and health information to other local users or processes. Exposure may extend to shared backup agents, indexing services, container users, or other softw ...[truncated 220 chars]
Remediation
View remediation
None: Path(path).mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(path, 0o700) mkdir_private(target_dir) fd = os.open( out_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(doc, f, indent=2, sort_keys=True) f.write("\n") ``` Apply the same private-permission policy to `meta.json` and any merged digest output containing health information. Document that the state directory should not be placed on a shared or broadly synchronized filesystem. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wellness_bridge.py:155
Finding

Unauthenticated Public Status Endpoint Leaks Synchronization and Filesystem Metadata

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wellness_bridge.py:120
Finding

Unbounded Request Buffering and Persistent JSON Storage Enable Resource Exhaustion

Content
View full analysis
bytes: n = int(handler.headers.get("Content-Length", "0") or "0") if n <= 0: return b"" return handler.rfile.read(n) ``` ```python raw = read_body(self) try: doc = json.loads(raw.decode("utf-8")) except Exception: self.send_response(400) self.end_headers() return # Expect doc.date, doc.source day = safe_day(str(doc.get("date") or "")) src = str(doc.get("source") or doc.get("sources_present") or "unknown") # Store payload target_dir = os.path.join(inbox_dir(), day) mkdirp(target_dir) fname = f"{int(time.time())}_{secrets.token_hex(4)}.json" out_path = os.path.join(target_dir, fname) with open(out_path, "w", encoding="utf-8") as f: json.dump(doc, f, indent=2, sort_keys=True) f.write("\n") ``` ### Technical Analysis The server trusts the supplied `Content-Length` without imposing a maximum and buffers the entire body in memory. After decoding, it accepts and stores arbitrary JSON without a schema, field allowlist, nesting limit, rate limit, retention policy, or storage quota. Bearer authentication reduces exposure to anonymous clients, but it does not address a compromised phone exporter, leaked token, malicious local process, or excessive uploads from an authorized client. Pretty-printing the parsed JSON can also increase the stored size. ### Attack Path 1. An attacker obtains the bridge bearer token from a compromised phone, plaintext configuration, command-line history, or another local source. 2. The attacker sends an authenticated request with a very large `Content-Length` and JSON body. 3. The bridge attempts to buffer the complete request in memory. 4. If parsing succeeds, the bridge writes the complete document to persistent storage ...[truncated 530 chars]
Remediation
View remediation
MAX_BODY_BYTES: raise PayloadTooLarge() body = handler.rfile.read(length) ``` Token rotation should also be immediate and documented for suspected exporter compromise. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:15
Finding

Mutable Third-Party Skills Are Installed Without Version or Integrity Pinning

Content
View full analysis
`), then run the source skill’s connect/fetch workflow. 5) Produce a digest using the normalized schema in `references/schema.md`. ``` ### Technical Analysis The Skill instructs the Agent to install third-party Skills from ClawHub using mutable package slugs. It does not require: - A pinned version or immutable content hash. - Publisher identity verification. - Review of the downloaded Skill's code and instructions. - Permission or network-destination review. - Explicit confirmation before running authorization workflows. These third-party integrations are expected to handle OAuth credentials, personal access tokens, account tokens, and sensitive wellness records. Consequently, a compromised or substituted dependency would operate at a particularly sensitive trust boundary. This is a supply-chain weakness rather than evidence that any specifically listed package is currently malicious. ### Attack Path 1. A listed package is compromised, transferred to a malicious publisher, replaced with an unsafe release, or confused with a lookalike package. 2. The hub directs the Agent to install the current package associated with the mutable slug. 3. The Agent runs the installed package's connect or fetch workflow without validating its contents or permissions. 4. The dependency receives access to OAuth tokens or wellness data as part of its expected operation. 5. Malicious code ...[truncated 564 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as a full wellness hub that can identify sources, install other skills, guide authorization, normalize health data, and schedule digests, but the described implementation apparently only generates digests/templates. That mismatch can mislead users into trusting the skill with sensitive health workflows it does not actually control or safeguard, causing unsafe operational assumptions around OAuth, reminders, data routing, and installation behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill encourages unified summaries and pushing them to any channel, but it does not prominently warn users that sensitive health data may be transmitted through chat channels, external integrations, local scripts, or third-party services. In the health context, this omission is especially dangerous because users may disclose protected or intimate information without understanding retention, visibility, or downstream exposure risks.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/catalog.md (reported line 18)May include surrounding context.

md
- Skill: `openclaw-whoop`

- Oura Ring (sleep, readiness, activity)
  - Connect: Personal Access Token (Tier 1)
  - Skill: `openclaw-oura`

- Fitbit (sleep, activity, heart)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill describes behaviors that require powerful capabilities such as shell, network, file access, and environment handling, but it does not declare any explicit tool scope or allowed-tools boundary. In a skill that handles health data and installation/orchestration tasks, this increases the risk of over-privileged execution, unintended command use, and access to sensitive local files or tokens.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The invocation language is broad enough that the skill could trigger on generic wellness, health, or summary-related requests rather than on clearly consented data-integration tasks. Because this skill deals with highly sensitive health information and cross-channel summaries, overly broad routing increases the chance of accidental activation, unnecessary data handling, or prompting users into sensitive setup flows unintentionally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document instructs users to expose a local health-data ingestion service to the public internet via Cloudflare Tunnel or ngrok, but it does not prominently warn about the privacy and attack-surface implications of doing so. Because the bridge handles sensitive wellness data and publishes helper endpoints, users may unknowingly make a health-data receiver reachable from anywhere, increasing risk of unauthorized access, token abuse, endpoint probing, and accidental disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The document instructs users to send sensitive wellness/health data along with a bearer token to a tunnel-hosted endpoint, but provides no privacy, retention, endpoint trust, or credential-handling warnings. In this skill context, the data involved can include highly sensitive personal health information, and the use of a tunnel URL increases exposure risk if users misconfigure, leak, or reuse the token or send data to an unintended host.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The document instructs users to persist and use a bearer token for automated health-data uploads, including a suggestion to store the Sync URL and token in iCloud Drive, but it does not warn that the token is a credential or that the payload contains sensitive health information. If the token or config file is exposed, an attacker could submit fraudulent data to the bridge or access a live ingestion endpoint, and the cloud tunnel increases exposure beyond a local-only workflow.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This protocol documents transmission of sensitive health data, including activity, sleep, body, and vital metrics, but provides no privacy, consent, retention, or data-handling warning. In a wellness skill, this omission is security-relevant because implementers may deploy collection and transfer flows for regulated or highly sensitive personal data without clear safeguards or user disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The statement that the bridge will store the full JSON as-is means sensitive body and vitals data may be persisted indefinitely, including any extra fields a client sends. Without minimization, retention limits, or warnings, this increases exposure of highly sensitive health information and broadens breach impact if the bridge is compromised or logs/backups are accessed.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/ingest-protocol.md (reported line 41)May include surrounding context.

  • Keep values numeric; do not include units in strings.
  • The bridge will store the full JSON as-is.

Example curl

bash
curl -X POST "https://<tunnel-host>/ingest" \

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/wellness_bridge.py (reported line 16)May include surrounding context.

python
- Requests must include: Authorization: Bearer <token>

Files:
- Token stored at: ~/.config/openclaw/wellness/bridge/token.json (chmod 600)
- Payloads stored under: ~/.config/openclaw/wellness/bridge/inbox/YYYY-MM-DD/*.json

No third-party deps.

Tainted flow: 'out_path' from open (line 228, file read) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/wellness_bridge.py (reported line 229)May include surrounding context.

python
mkdirp(target_dir)
        fname = f"{int(time.time())}_{secrets.token_hex(4)}.json"
        out_path = os.path.join(target_dir, fname)
        with open(out_path, "w", encoding="utf-8") as f:
            json.dump(doc, f, indent=2, sort_keys=True)
            f.write("\n")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The init command prints the bearer token directly to stdout, which can leak the credential through terminal logs, shell history capture tools, CI logs, remote session recording, or shoulder-surfing. Because this token protects a network-exposed ingest endpoint for sensitive wellness data, disclosure would allow unauthorized uploads and potential poisoning of health records.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script defaults --tz to Asia/Shanghai, which imposes a specific locale policy on all users unless they explicitly override it. This matches the language/locale policy violation category because the locale constraint is neither optional by default nor justified as region-specific in the file's natural-language content.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/wellness_push.py (reported line 55)May include surrounding context.

python
if args.out:
        cmd += ["--out", args.out]

    res = subprocess.run(cmd, capture_output=True, text=True)
    if res.returncode != 0:
        raise SystemExit(res.stderr.strip() or "wellness_digest failed")
    return res.stdout

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/wellness_push.py (reported line 78)May include surrounding context.

python
args = ap.parse_args()
    args.bridge_dir = args.bridge_dir.replace("~", "{HOME}")
    args.bridge_dir = args.bridge_dir.format(HOME=str(__import__("os").path.expanduser("~")))

    msg = run_digest(args).strip() + "\n"
    print(msg, end="")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code hard-codes a locale-related default of "Asia/Shanghai" for the timezone argument, which can impose a specific regional setting on users who do not explicitly choose it. The policy allows locale constraints only when they are user-selectable or clearly justified, neither of which is present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script sets the default --tz value to Asia/Shanghai, which imposes a specific locale by default. Under the policy, locale constraints should be user-selectable or explicitly justified as region-specific; this file does not provide such justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.