Back to skill

Security audit

Starling Home Hub (Nest/Google Home)

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent and disclosed, but it needs review because it can control locks and cameras while using weak transport and credential-handling defaults.

Install only if you understand that this can read camera data and change physical smart-home state, including locks. Use a narrowly scoped API key, pin the hub certificate with --cacert, avoid --http, keep the hub address private/local and verified, and avoid passing untrusted values into set or stream-start commands.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/starling.sh:137
Finding
API Key Exposed Through Process Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/starling.sh`, lines 137, 153, 160, and 176 **Vulnerability Type**: Sensitive credential exposure through process arguments **Risk Level**: High ### Vulnerable Code ```bash body=$(curl -sfS -w '\n%{http_code}' "${CURL_TIMEOUT[@]}" ${CURL_EXTRA[@]+"${CURL_EXTRA[@]}"} "$@" "${BASE_URL}${path}?key=${API_KEY}" 2>&1) || true ``` The same vulnerable URL construction is used by the other request functions: ```bash curl -sfS "${CURL_TIMEOUT[@]}" ${CURL_EXTRA[@]+"${CURL_EXTRA[@]}"} "$@" "${BASE_URL}${path}?key=${API_KEY}" || { ``` ```bash body=$(curl -sfS -w '\n%{http_code}' "${CURL_TIMEOUT[@]}" ${CURL_EXTRA[@]+"${CURL_EXTRA[@]}"} "$@" "${BASE_URL}${path}?key=${API_KEY}&${params}" 2>&1) || true ``` ```bash resp=$(curl -sfS -w '\n%{http_code}' "${CURL_TIMEOUT[@]}" ${CURL_EXTRA[@]+"${CURL_EXTRA[@]}"} -X POST \ -H "Content-Type: application/json" \ -d "$body" \ "${BASE_URL}${path}?key=${API_KEY}" 2>&1) || true ``` ### Technical Analysis The Starling API requires authentication through a `key` query parameter. Although the script initially obtains the key from `STARLING_API_KEY`, it interpolates the secret directly into curl's URL argument. Consequently, the complete URL, including `?key=<API_KEY>`, can appear in the curl process command line while a request is active. Users or monitoring services with permission to inspect that process can recover the credential. This conflicts with the script's warning that only use of the `--key` option creates process-list exposure. The network transmission itself is necessary for the declared functionality, and the query parameter is imposed by the underlying API. However, placing the resulting URL directly in an argv element is not the minimum-exposure way to perform that transmission. ### Attack Path 1. A victim configures a Starling API key through `STARLING_API_KEY`. 2. An attacker with local process-inspection access continuously monitors command lines, ...[truncated 1002 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Avoid placing an authenticated URL directly in a process argument. Where supported, provide sensitive curl configuration through a protected standard-input or file-descriptor channel. 2. Investigate whether the hub accepts an authentication header or another credential mechanism in newer API versions. 3. If query-string authentication is unavoidable, explicitly document that environment-variable use does not prevent curl argv exposure. 4. Run the integration under a dedicated operating-system account and restrict process inspection where the platform permits it. 5. Create narrowly scoped API keys: - Use read-only keys for status monitoring. - Use separate keys for camera access, lock control, and other write operations. - Avoid granting lock or camera permissions to unrelated automation. 6. Rotate the API key after any suspected local process-monitoring exposure. 7. Avoid logging command lines or collecting full process arguments in telemetry and audit systems. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/starling.sh:106
Finding
TLS Certificate Verification Disabled by Default<![CDATA[ ## Vulnerability Details **File Location**: `scripts/starling.sh`, lines 106-114 **Vulnerability Type**: Unauthenticated TLS connection carrying credentials and sensitive smart-home data **Risk Level**: High ### Vulnerable Code ```bash if $USE_HTTPS; then BASE_URL="https://${HUB_IP}:3443/api/connect/v1" if [[ -n "$CACERT" ]]; then CURL_EXTRA=(--cacert "$CACERT") else # Starling Home Hub uses a self-signed cert; -k required on trusted local networks CURL_EXTRA=(-k) fi else ``` These options are subsequently applied to authenticated curl requests: ```bash body=$(curl -sfS -w '\n%{http_code}' "${CURL_TIMEOUT[@]}" ${CURL_EXTRA[@]+"${CURL_EXTRA[@]}"} "$@" "${BASE_URL}${path}?key=${API_KEY}" 2>&1) || true ``` ### Technical Analysis The default HTTPS mode uses curl's `-k` option unless the user explicitly supplies `--cacert`. This disables both certificate-chain and hostname verification. Encryption without server authentication does not establish that the remote endpoint is the intended Starling hub. Any attacker able to intercept or redirect local traffic can present an arbitrary certificate, and curl will accept it. Authenticated URLs, request bodies, device responses, snapshots, and streaming information may then be exposed. Using a self-signed hub certificate does not inherently require disabling verification. The certificate or its issuing authority can instead be explicitly trusted or pinned. Because secure verification is optional rather than the default, the implementation unnecessarily weakens the confidentiality and integrity protections applied to a privileged smart-home interface. ### Attack Path 1. The victim runs the script without supplying `--cacert`, which is the documented default workflow. 2. An attacker gains a network position capable of redirecting traffic through ARP spoofing, route manipulation, DNS manipulation when a hostname is used, or a compromised local gateway. 3. The attacker impersonates the hub ...[truncated 938 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require authenticated TLS by default: - Enroll the hub's certificate during setup. - Store it in a user-protected location. - Pass it to curl with `--cacert`. 2. Alternatively, pin the expected public key or certificate fingerprint using an appropriate curl capability. 3. Remove automatic use of `-k`. If insecure TLS must remain available for initial enrollment or recovery, place it behind an explicit option such as `--insecure`. 4. Display a prominent warning and require confirmation before insecure mode transmits an API key. 5. Verify that the certificate identity corresponds to the configured hub rather than trusting any certificate. 6. Rotate API keys that may previously have traversed hostile or untrusted networks. 7. Keep HTTP downgrade support disabled unless explicitly requested, and consider removing it for commands involving camera or lock permissions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/starling.sh:82
Finding
Unrestricted Hub Destination Permits API Key Disclosure to an Attacker-Controlled Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/starling.sh`, lines 82-83 and 106-117 **Vulnerability Type**: Unvalidated network destination for sensitive authenticated requests **Risk Level**: High ### Vulnerable Code ```bash while [[ $# -gt 0 ]]; do case "$1" in --ip) HUB_IP="$2"; shift 2 ;; ``` The supplied value is used directly as the request destination: ```bash if $USE_HTTPS; then BASE_URL="https://${HUB_IP}:3443/api/connect/v1" if [[ -n "$CACERT" ]]; then CURL_EXTRA=(--cacert "$CACERT") else # Starling Home Hub uses a self-signed cert; -k required on trusted local networks CURL_EXTRA=(-k) fi else echo "WARNING: Using unencrypted HTTP. Local network traffic can be sniffed. Use HTTPS (default) when possible." >&2 BASE_URL="http://${HUB_IP}:3080/api/connect/v1" CURL_EXTRA=() fi ``` Authenticated requests are then sent to that destination: ```bash "${BASE_URL}${path}?key=${API_KEY}" ``` ### Technical Analysis `STARLING_HUB_IP` and `--ip` are trusted without validation. The script does not ensure that the value is: - A syntactically valid IP address. - A private or link-local address. - The expected Starling hub. - Free from hostname or URL metacharacter ambiguity. - Bound to a previously enrolled certificate. Although the documentation describes the API as local-network only, the implementation can send the API key to a public or attacker-controlled destination. The default use of `-k` makes this more exploitable because an attacker-controlled HTTPS endpoint does not need a trusted certificate. Allowing the user to select a hub is functionally necessary. Allowing an arbitrary unauthenticated destination to receive a privileged key exceeds the minimum network scope required for local hub management. ### Attack Path 1. An attacker influences configuration, automation instructions, shell environment setup, or invocation parameters. 2. `STARLING_HUB_IP` or `--ip` is set to an attacker-controlled hos ...[truncated 1238 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `HUB_IP` before constructing the URL. 2. By default, permit only literal local addresses appropriate to the deployment, such as: - RFC1918 IPv4 ranges. - Explicitly supported link-local addresses. - Properly bracketed private or link-local IPv6 addresses. 3. Reject public addresses, URL schemes, paths, user-info syntax, control characters, and ambiguous host strings. 4. If hostnames are required, resolve them and verify that all resulting addresses are local. Revalidate after connection or use destination pinning to reduce DNS-rebinding risk. 5. Pair destination validation with mandatory certificate or public-key pinning. 6. Persist an enrolled hub identity and require explicit re-enrollment before changing destinations. 7. Provide a clearly labeled high-risk override only for exceptional deployments, rather than accepting unrestricted destinations by default. 8. Continue enforcing least-privileged API keys so destination compromise does not automatically expose every device capability. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/starling.sh:239
Finding
Unsafe JSON Construction Allows Request-Body Manipulation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/starling.sh`, lines 239-258 **Vulnerability Type**: JSON injection through unescaped command arguments **Risk Level**: High ### Vulnerable Code ```bash # Build JSON from key=value pairs JSON="{" FIRST=true for kv in "$@"; do KEY="${kv%%=*}" VAL="${kv#*=}" validate_id "$KEY" "property name" $FIRST || JSON+="," FIRST=false # Auto-detect type: bool, number, or string case "$VAL" in true|false) JSON+="\"$KEY\":$VAL" ;; ''|*[!0-9.-]*) JSON+="\"$KEY\":\"$VAL\"" ;; *) JSON+="\"$KEY\":$VAL" ;; esac done JSON+="}" api_post "/devices/${DEV_ID}" "$JSON" | fmt_json ``` The streaming command has the same unsafe construction pattern: ```bash stream-start) [[ -z "${1:-}" || -z "${2:-}" ]] && { echo "Usage: starling.sh stream-start <id> <base64-sdp-offer>" >&2; exit 1; } validate_id "$1" "device ID" api_post "/devices/$1/stream" "{\"offer\":\"$2\"}" | fmt_json ;; ``` ### Technical Analysis The script constructs JSON through string concatenation. Property values and SDP offers are inserted between quotation marks without JSON escaping. Input containing quotation marks, backslashes, or control characters can therefore: - Produce malformed JSON. - Terminate the intended string value. - Insert additional object members. - Change the semantic meaning of a smart-home command. The validation applied to property names does not protect values. The numeric detection is also permissive: strings composed only of digits, periods, and minus signs are emitted as unquoted JSON even when they are not valid JSON numbers. This issue is especially significant because `set` can invoke safety- and privacy-sensitive writable properties. Arguments may also originate from external automation, generated content, or untrusted upstream data rather than direct trusted user input. ### Attack Path 1. An automation workflow passes an attacker-influenced value to `starling.sh set`. 2. Th ...[truncated 1274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace manual JSON concatenation with a real JSON encoder, such as `jq`. 2. Pass strings through `jq --arg` and validated primitive values through `--argjson`. 3. Construct the stream payload safely, for example: ```bash body=$(jq -n --arg offer "$2" '{offer: $offer}') api_post "/devices/$1/stream" "$body" ``` 4. Build `set` objects incrementally with an encoder rather than interpolating names and values into JSON source text. 5. Validate values against an allowlist derived from the API schema: - Enumerated strings for lock and HVAC modes. - Strict JSON number grammar and safe ranges for temperatures and percentages. - Boolean-only handling for boolean properties. - Maximum lengths for strings and SDP offers. 6. Restrict writable property names by device type instead of accepting every syntactically valid identifier. 7. Reject duplicate or unsupported properties before sending the request. 8. Add tests covering quotation marks, backslashes, newlines, duplicate keys, malformed numeric values, and attempted injected object members. ]]>
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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- Create separate keys for different automation tasks if possible

### TLS Certificate Verification
- HTTPS is the default, but the script uses `curl -k` (skip cert verification) because Starling Home Hub uses a self-signed certificate
- This is acceptable on a **trusted local network** but increases MITM risk on untrusted networks
- To pin the hub's certificate instead: `starling.sh --cacert /path/to/hub-cert.pem status`
- When `--cacert` is provided, `-k` is not used and full certificate verification applies
Confidence
92% confidence
Finding
Using curl -k disables TLS certificate verification, allowing a machine on the local network to impersonate the hub and intercept API keys or alter commands and responses. Although the skill notes this tradeoff and offers --cacert as a safer option, making insecure verification bypass part of the normal workflow is an unsafe transport default for a skill that controls locks, cameras, and home state.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
KEY_FROM_CLI=false
CACERT=""

# Curl timeouts to prevent hanging
CURL_TIMEOUT=(--connect-timeout 5 --max-time 30)

usage() {
Confidence
70% 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).

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### API Key Management
- **Always use the `STARLING_API_KEY` env var** — never pass keys via `--key` (visible in `ps` output)
- Never store keys in scripts, SKILL.md, or version-controlled files
- Use a `.env` file with restricted permissions: `chmod 600 .env`
- Consider a secrets manager for production/automated setups

### Least Privilege
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
### API Key Management
- **Always use the `STARLING_API_KEY` env var** — never pass keys via `--key` (visible in `ps` output)
- Never store keys in scripts, SKILL.md, or version-controlled files
- Use a `.env` file with restricted permissions: `chmod 600 .env`
- Consider a secrets manager for production/automated setups

### Least Privilege
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
### API Key Management
- **Always use the `STARLING_API_KEY` env var** — never pass keys via `--key` (visible in `ps` output)
- Never store keys in scripts, SKILL.md, or version-controlled files
- Use a `.env` file with restricted permissions: `chmod 600 .env`
- Consider a secrets manager for production/automated setups

### Least Privilege
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
- Always use HTTPS (default) to prevent local network sniffing of API keys and device data

### Snapshot Handling
- Camera snapshots contain sensitive imagery — don't store in world-readable locations
- The script sets snapshot files to `chmod 600` (owner-only) automatically
- Clean up temporary snapshot files when no longer needed
Confidence
70% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This markdown file describes endpoints for retrieving camera snapshots and starting/stopping WebRTC streams, which can expose sensitive in-home visual data. The documentation does not include any warning or disclosure about privacy implications, user consent, or handling of captured media.

External Transmission

Medium
Category
Data Exfiltration
Content
api_post() {
  local path="$1" body="$2"
  local http_code resp
  resp=$(curl -sfS -w '\n%{http_code}' "${CURL_TIMEOUT[@]}" ${CURL_EXTRA[@]+"${CURL_EXTRA[@]}"} -X POST \
    -H "Content-Type: application/json" \
    -d "$body" \
    "${BASE_URL}${path}?key=${API_KEY}" 2>&1) || true
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.