Back to skill

Security audit

Bluebubbles Healthcheck

Security checks for vulnerabilities and agentic risk

Overview

This skill has a legitimate troubleshooting purpose, but its repair path can delete webhook settings and expose the BlueBubbles password if used with unsafe URLs or logs.

Review this carefully before installing. Use diagnose.sh only for read-only checks unless you are prepared for heal.sh/reset-webhook.sh to restart the gateway and replace BlueBubbles webhook configuration. Do not run it on a BlueBubbles server with other webhook integrations, do not pass remote BB_URL or OPENCLAW_WEBHOOK_URL values, and rotate BB_PASSWORD if prior runs may have exposed password-bearing webhook URLs in logs or transcripts.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/diagnose.sh:14
Finding
Unrestricted destination URLs allow BlueBubbles credential disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/diagnose.sh:14-19, 61, 77, 107-111, 133-155`; `scripts/reset-webhook.sh:13-18, 34-43, 56, 107-111, 128` **Vulnerability Type**: Unvalidated credential destination and plaintext credential transmission **Risk Level**: High ### Complete Code Snippet From `scripts/diagnose.sh`: ```bash while [[ $# -gt 0 ]]; do case $1 in --bb-url) BB_URL="$2"; shift 2 ;; --password) BB_PASSWORD="$2"; shift 2 ;; --webhook-url) OPENCLAW_WEBHOOK_URL="$2"; shift 2 ;; --quiet|-q) QUIET=1; shift ;; --json) JSON_OUTPUT=1; shift ;; *) echo "Unknown arg: $1"; exit 1 ;; esac done ``` ```bash HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer ${BB_PASSWORD}" "${BB_URL}/api/v1/ping" 2>/dev/null || echo "000") ``` ```bash ENDPOINT_RESPONSE=$(curl -s -X POST --max-time 5 \ -H "Authorization: Bearer ${BB_PASSWORD}" \ -H "Content-Type: application/json" \ -d '{"type":"ping","data":{}}' \ "${OPENCLAW_WEBHOOK_URL}" 2>/dev/null || echo "") ``` From `scripts/reset-webhook.sh`: ```bash # Build full webhook URL with password (for BB to call OpenClaw) # Note: The password is included in the registered URL so BB can authenticate with OpenClaw if [[ "$OPENCLAW_WEBHOOK_URL" == *"password="* ]]; then FULL_WEBHOOK_URL="$OPENCLAW_WEBHOOK_URL" else if [[ "$OPENCLAW_WEBHOOK_URL" == *"?"* ]]; then FULL_WEBHOOK_URL="${OPENCLAW_WEBHOOK_URL}&password=${BB_PASSWORD}" else FULL_WEBHOOK_URL="${OPENCLAW_WEBHOOK_URL}?password=${BB_PASSWORD}" fi fi ``` ```bash REGISTER_RESULT=$(curl -s -X POST --max-time 10 \ -H "Authorization: Bearer ${BB_PASSWORD}" \ -H "Content-Type: application/json" \ -d "{\"url\": \"${ESCAPED_URL}\", \"events\": [\"*\"]}" \ "${BB_URL}/api/v1/webhook" 2>/dev/null) ``` ### Technical Analysis The scripts accept `BB_URL` and `OPENCLAW_WEBHOOK_URL` from environment variables or command-line arguments without validating the destination ...[truncated 2254 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce loopback-only destinations by default: - Resolve the hostname and require every resolved address to be `127.0.0.0/8` or `::1`. - Do not rely only on string comparisons such as checking for `localhost`. 2. Reject non-HTTPS remote URLs. If remote operation is genuinely required, require an explicit opt-in and interactive confirmation. 3. Maintain an allowlist of expected URL schemes, hosts, ports, and paths. 4. Do not send the BlueBubbles API password to the OpenClaw webhook endpoint during a health probe unless that exact authentication protocol is required. 5. Use a separate, narrowly scoped webhook secret instead of reusing the BlueBubbles administrative/API password. 6. Avoid query-string credentials. Prefer an authentication header or signed webhook mechanism because URLs are commonly stored in logs, databases, and diagnostic output. 7. Refuse redirects or constrain them to the validated origin by using `curl --max-redirs 0`, unless redirects are explicitly required. 8. Update the documentation to accurately state the enforced network policy and warn users before any remote configuration is accepted. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/diagnose.sh:77
Finding
Diagnostic output discloses credentials contained in registered webhook URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/diagnose.sh:77-93` **Vulnerability Type**: Sensitive information exposure through logs and structured output **Risk Level**: High ### Complete Code Snippet ```bash WEBHOOK_RESPONSE=$(curl -s --max-time 5 -H "Authorization: Bearer ${BB_PASSWORD}" "${BB_URL}/api/v1/webhook" 2>/dev/null || echo "{}") # Extract webhook URLs using Python (portable JSON parsing) WEBHOOK_URLS=$(echo "$WEBHOOK_RESPONSE" | python3 -c " import sys, json try: d = json.load(sys.stdin) for w in d.get('data', []): print(w.get('url', '')) except: pass " 2>/dev/null) # Check if any webhook points to OpenClaw (port 18789) if echo "$WEBHOOK_URLS" | grep -q "18789"; then MATCHING_URL=$(echo "$WEBHOOK_URLS" | grep "18789" | head -1) check_pass "webhook_registered" "$MATCHING_URL" ``` The selected URL is passed to: ```bash check_pass() { local name="$1" local detail="${2:-}" log "✅ CHECK $name: PASS${detail:+ ($detail)}" RESULTS+=("{\"check\":\"$name\",\"status\":\"pass\",\"detail\":\"$detail\"}") } ``` ### Technical Analysis BlueBubbles webhook URLs may include the password in the query string, as created by `reset-webhook.sh`. The diagnostic script extracts the complete registered URL and passes it directly to `check_pass` without redaction. The unredacted URL is emitted in normal terminal output and is also interpolated into the `RESULTS` array used by JSON output. This directly contradicts the documentation’s claim that the password is masked in all script output. The exposure is especially significant in an Agent environment because command output may be copied into conversation history, execution traces, centralized logs, monitoring systems, or support reports. The JSON construction also performs no proper JSON escaping, which can produce malformed output for unusual URL content, although the confirmed security issue here is secret disclosure. ### Attack Path 1. `reset-webhook.sh` or anot ...[truncated 1102 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Redact sensitive query parameters before passing URLs to `check_pass`. 2. Apply redaction to both normal and JSON output. Sensitive parameter names should include at least `password`, `token`, `secret`, `key`, and common authentication variants. 3. Prefer printing only a normalized destination, such as scheme, validated host, port, and path, with the complete query string omitted. 4. Generate JSON using a real JSON serializer rather than manual string interpolation. 5. Add tests verifying that output never contains the value of `BB_PASSWORD`, including: - Default terminal output. - Quiet and JSON modes. - Existing webhook responses. - Error messages and malformed responses. 6. Rotate the BlueBubbles password after running the vulnerable version where logs or transcripts may have retained it. 7. Remove or sanitize historical execution logs containing credential-bearing webhook URLs. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/reset-webhook.sh:68
Finding
Webhook repair deletes all registrations and creates an overbroad wildcard subscription<![CDATA[ ## Vulnerability Details **File Location**: `scripts/reset-webhook.sh:68-89, 100-111` **Vulnerability Type**: Destructive cross-integration modification and excessive webhook scope **Risk Level**: High ### Complete Code Snippet ```bash # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Step 2: Delete all existing webhooks # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ IDS=$(echo "$WEBHOOKS" | python3 -c " import sys, json try: d = json.load(sys.stdin) for w in d.get('data', []): print(w['id']) except: pass " 2>/dev/null) DELETED=0 if [[ -n "$IDS" ]]; then while IFS= read -r id; do if [[ -n "$id" ]]; then log "Deleting webhook id=$id" curl -s -X DELETE --max-time 10 -H "Authorization: Bearer ${BB_PASSWORD}" "${BB_URL}/api/v1/webhook/${id}" > /dev/null 2>&1 ((DELETED++)) fi done <<< "$IDS" log "Deleted $DELETED webhook(s)" else log "No existing webhooks to delete" fi ``` ```bash # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Step 4: Register new webhook # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ log "Registering new webhook..." # Escape the URL for JSON ESCAPED_URL=$(echo "$FULL_WEBHOOK_URL" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read().strip()))" | sed 's/^"//;s/"$//') REGISTER_RESULT=$(curl -s -X POST --max-time 10 \ -H "Authorization: Bearer ${BB_PASSWORD}" \ -H "Content-Type: application/json" \ -d "{\"url\": \"${ESCAPED_URL}\", \"events\": [\"*\"]}" \ "${BB_URL}/api/v1/webhook" 2>/dev/null) ``` ### Technical Analysis The declared task is to repair the BlueBubbles-to-OpenClaw webhook. However, the reset implementation enumerates every registered webhook ID and deletes all of them without checking the destination, owner, path, event set, or whether the registration belongs to OpenClaw. It then creates one new webhook subscrib ...[truncated 1923 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Identify the exact OpenClaw webhook using a validated combination of host, port, path, and an integration-specific identifier. 2. Delete or update only the matching OpenClaw registration. Preserve every unrelated webhook. 3. Avoid broad port-only matching because an unrelated URL can also contain port `18789`. 4. Preserve the prior event filter and subscribe only to event types required by OpenClaw rather than using `["*"]`. 5. Before destructive changes: - Save a complete backup of matching registrations. - Display the exact registrations that will change. - Require confirmation unless an explicit noninteractive repair flag is supplied. 6. Create or update the replacement before deleting the old registration when the API permits it. 7. If deletion must occur first, implement rollback that restores the previous URL and event set when registration or verification fails. 8. Check HTTP status codes and response schemas for every DELETE and POST operation instead of suppressing failures. 9. Verify the exact expected webhook rather than merely checking that the total webhook count equals one. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/heal.sh:47
Finding
Auto-healing masks diagnostic failures and reports false success<![CDATA[ ## Vulnerability Details **File Location**: `scripts/heal.sh:47-54, 174-175` **Vulnerability Type**: Incorrect exit-status handling and false health reporting **Risk Level**: Medium ### Complete Code Snippet Initial diagnosis: ```bash # Capture diagnose output for analysis DIAGNOSE_OUTPUT=$("${SCRIPT_DIR}/diagnose.sh" 2>&1) || true DIAGNOSE_EXIT=$? echo "$DIAGNOSE_OUTPUT" if [[ "$DIAGNOSE_EXIT" == "0" ]]; then log "" log "✅ All checks passed — no healing needed" exit 0 fi ``` Verification uses the same pattern: ```bash VERIFY_OUTPUT=$("${SCRIPT_DIR}/diagnose.sh" 2>&1) || true VERIFY_EXIT=$? ``` ### Technical Analysis The command list: ```bash DIAGNOSE_OUTPUT=$(...) || true ``` returns the exit status of `true` whenever `diagnose.sh` fails. The following assignment therefore records zero in `DIAGNOSE_EXIT`, not the diagnostic script’s nonzero status. As a result, failed diagnostics enter the branch that prints “All checks passed” and exits successfully. The same defect affects post-healing verification, allowing failed verification to be interpreted as successful. This is a security-relevant reliability flaw because health monitoring and automatic recovery depend on accurate status propagation. It can suppress recovery during a real connectivity outage and mislead users or supervising Agents into believing the integration is healthy. ### Attack Path 1. A real failure occurs, such as the BlueBubbles server becoming unavailable, the OpenClaw endpoint failing, or webhook delivery stopping. 2. `diagnose.sh` emits failed checks and exits with a nonzero status. 3. The `|| true` clause executes and returns zero. 4. `DIAGNOSE_EXIT=$?` stores zero. 5. `heal.sh` prints the diagnostic text but immediately enters the “All checks passed” branch. 6. No gateway restart or webhook repair occurs. 7. Scheduled automation, heartbeat monitoring, or an Agent receives a successful process exit code and may report a healthy state. 8. If the same pattern i ...[truncated 757 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Capture the command status without appending `|| true`. For example: ```bash if DIAGNOSE_OUTPUT=$("${SCRIPT_DIR}/diagnose.sh" 2>&1); then DIAGNOSE_EXIT=0 else DIAGNOSE_EXIT=$? fi ``` Apply the same correction to verification: ```bash if VERIFY_OUTPUT=$("${SCRIPT_DIR}/diagnose.sh" 2>&1); then VERIFY_EXIT=0 else VERIFY_EXIT=$? fi ``` Additional hardening steps: 1. Add automated tests where `diagnose.sh` exits with zero and several nonzero values. 2. Assert that healing actions execute only for the relevant failed checks. 3. Verify that failed post-healing diagnostics result in a nonzero final exit status. 4. Prefer structured diagnostic output over parsing human-readable lines with `grep`. 5. Do not suppress failures from `openclaw gateway restart` or `reset-webhook.sh`; capture and report their statuses explicitly. 6. Ensure the final success result requires both successful remediation commands and successful verification. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This mismatch includes a materially important undeclared destructive behavior: deleting all existing BlueBubbles webhooks and replacing them. Undisclosed destructive actions can cause denial of service, break other integrations, or overwrite intended configuration, especially when users think they are running a bounded connectivity healthcheck.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
This mismatch includes a materially important undeclared destructive behavior: deleting all existing BlueBubbles webhooks and replacing them. Undisclosed destructive actions can cause denial of service, break other integrations, or overwrite intended configuration, especially when users think they are running a bounded connectivity healthcheck.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This mismatch includes a materially important undeclared destructive behavior: deleting all existing BlueBubbles webhooks and replacing them. Undisclosed destructive actions can cause denial of service, break other integrations, or overwrite intended configuration, especially when users think they are running a bounded connectivity healthcheck.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Delete Webhook
```
DELETE /api/v1/webhook/:id
```
Removes webhook by ID. Use to clear broken registrations before re-registering.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The manifest says the skill 'diagnoses and auto-heals' and specifically claims it 'auto-fixes webhook backoff, stale registrations, and gateway issues.' In this file, the script only performs four read-oriented checks, reports pass/fail, and exits with status; it never invokes any BlueBubbles or OpenClaw API to modify webhook state, clear registrations, reset backoff, or restart/repair a gateway.

External Script Fetching

High
Category
Supply Chain
Content
" 2>/dev/null)

  # Also check if there are any recent messages (proxy for activity)
  MSG_COUNT=$(curl -s --max-time 5 -H "Authorization: Bearer ${BB_PASSWORD}" "${BB_URL}/api/v1/message/count" 2>/dev/null | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
while IFS= read -r id; do
    if [[ -n "$id" ]]; then
      log "Deleting webhook id=$id"
      curl -s -X DELETE --max-time 10 -H "Authorization: Bearer ${BB_PASSWORD}" "${BB_URL}/api/v1/webhook/${id}" > /dev/null 2>&1
      ((DELETED++))
    fi
  done <<< "$IDS"
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
sleep 1

VERIFY=$(curl -s --max-time 10 -H "Authorization: Bearer ${BB_PASSWORD}" "${BB_URL}/api/v1/webhook" 2>/dev/null)
COUNT=$(echo "$VERIFY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('data',[])))" 2>/dev/null || echo "0")

log "Webhook count after reset: $COUNT"
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README describes auto-heal behavior that can restart services and delete all existing webhooks, but it does not prominently warn that these are destructive configuration changes. This is dangerous because operators or agents may run the skill expecting a safe health check, while the healing path can disrupt active integrations or remove intentionally configured webhook state.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The manual trigger phrases are very broad and map to ordinary troubleshooting requests, which can cause the agent to invoke a skill that performs operational changes when the user may only be asking for diagnosis or advice. In this skill’s context, that ambiguity is more dangerous because the documented behavior includes auto-healing actions such as restarting the gateway and deleting/re-registering webhooks.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill explicitly instructs users to run shell scripts that can restart services and modify webhook configuration, but it declares no tool scope or permissions boundary. In an agent ecosystem, missing an explicit shell/tool declaration increases the chance the skill is invoked with broader-than-expected execution capability and without adequate user review of destructive actions.

Vague Triggers

Medium
Confidence
82% confidence
Finding
Encouraging periodic healthchecks in a skill that also advertises auto-healing increases the risk of unattended execution of disruptive actions such as service restarts and webhook resets. In agentic environments, broad recurring triggers can turn a maintenance script into an automated configuration mutator without incident-specific human intent.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The Auto-Heal instructions describe operations that may restart the gateway and delete/re-register webhooks, but they do not prominently warn about service disruption or side effects before the command is presented. Users may execute the command expecting a harmless healthcheck and inadvertently interrupt messaging or remove other webhook integrations.

External Transmission

Medium
Category
Data Exfiltration
Content
## Issue: BB Server Unreachable

**Symptom:** `diagnose.sh` fails at health check, curl to `/api/v1/ping` times out.

**Causes:**
- BlueBubbles app not running on Mac
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The troubleshooting guide instructs operators to delete and recreate a webhook, which changes server state and can temporarily disrupt integrations if performed incorrectly. In an operational skill that may be followed quickly during incident response, omitting an explicit warning, prerequisites, or confirmation step increases the chance of accidental service impact or misconfiguration.

External Transmission

Medium
Category
Data Exfiltration
Content
log "─── CHECK 3: OpenClaw webhook endpoint alive ───"

# Test OpenClaw webhook endpoint with Authorization header
ENDPOINT_RESPONSE=$(curl -s -X POST --max-time 5 \
  -H "Authorization: Bearer ${BB_PASSWORD}" \
  -H "Content-Type: application/json" \
  -d '{"type":"ping","data":{}}' \
Confidence
97% confidence
Finding
This POST transmits a sensitive Bearer token to an external endpoint defined by configuration, so a malicious or compromised webhook target can capture it. In this skill context, the danger is increased because the endpoint is user-configurable and not constrained to localhost or a trusted allowlist.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script sends the BlueBubbles bearer token in an Authorization header to the configured webhook URL, which may be remote or attacker-controlled if the URL is misconfigured. That unnecessarily discloses a sensitive credential to a second service and could allow reuse of the token against the BlueBubbles API.

External Transmission

Medium
Category
Data Exfiltration
Content
# Escape the URL for JSON
ESCAPED_URL=$(echo "$FULL_WEBHOOK_URL" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read().strip()))" | sed 's/^"//;s/"$//')

REGISTER_RESULT=$(curl -s -X POST --max-time 10 \
  -H "Authorization: Bearer ${BB_PASSWORD}" \
  -H "Content-Type: application/json" \
  -d "{\"url\": \"${ESCAPED_URL}\", \"events\": [\"*\"]}" \
Confidence
83% confidence
Finding
The script transmits sensitive data to an external service by embedding BB_PASSWORD into the webhook URL and then sending that URL to the BlueBubbles server for storage and later use. Query-string secrets are commonly exposed via logs, admin UIs, proxies, crash reports, and webhook listings, so this creates a real credential leakage risk even though the network call itself is expected behavior for this skill.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script requires `BB_PASSWORD` via argument or environment variable and uses it across multiple authenticated requests. While it errors if the value is missing, there is no clear warning or explanatory usage text telling the user that a sensitive credential will be consumed and sent in outbound requests during diagnostics.

Static analysis

No suspicious patterns detected.