Back to skill

Security audit

DroneMobile

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says by controlling a DroneMobile vehicle, but it can perform real-world vehicle actions with weak guardrails and ambiguous vehicle targeting.

Review before installing. Only use this with a dedicated, trusted environment; prefer a virtualenv with pinned dependencies; set DRONEMOBILE_DEVICE_KEY for the exact vehicle; and require manual confirmation before start, stop, unlock, or trunk commands.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/dronemobile.py:37
Finding
Unsafe Vehicle Selection Fallback Can Execute Commands on the Wrong Vehicle## Vulnerability Details **File Location**: `scripts/dronemobile.py`, lines 37–48 **Vulnerability Type**: Unsafe fail-open resource selection **Risk Level**: High **Vulnerable Code**: ```python def get_vehicle(client, device_key=None): vehicles = client.get_vehicles() if not vehicles: print("ERROR: No vehicles found on account") sys.exit(1) if device_key: for v in vehicles: dk = str(v.info.device_key) if hasattr(v, 'info') else str(getattr(v, 'device_key', '')) if dk == str(device_key): return v print(f"WARNING: Device key {device_key} not found — using first vehicle") return vehicles[0] ``` ### Technical Analysis The vehicle-selection function fails open when the configured `DRONEMOBILE_DEVICE_KEY` does not match any returned vehicle. Instead of terminating, it warns the user and returns `vehicles[0]`. It also automatically chooses the first vehicle whenever no device key is configured, regardless of how many vehicles exist on the account. This is particularly dangerous because the selected object is subsequently used for safety- and security-sensitive physical commands, including engine start, engine stop, door unlock, and trunk opening. A stale or mistyped key, a change in the dependency's vehicle object structure, or a change in API ordering can result in a command targeting an unintended vehicle. ### Attack Path 1. A DroneMobile account contains multiple registered vehicles. 2. `DRONEMOBILE_DEVICE_KEY` is absent, mistyped, stale, or fails to match because the dependency exposes the key through an unexpected object structure. 3. A user or agent requests a command such as `start`, `unlock`, or `trunk`. 4. `get_vehicle()` fails to identify the intended vehicle. 5. The function silently returns the first vehicle from `client.get_vehicles()`. 6. The requested physical-control command executes against that unintend ...[truncated 636 chars]
Remediation
## Remediation Suggestions Implement fail-closed and unambiguous vehicle selection: 1. If an explicit device key is supplied but no exact match is found, terminate without executing any command. 2. If no device key is supplied, automatically select a vehicle only when exactly one vehicle exists. 3. If multiple vehicles exist, require an explicit device key or another stable unique identifier. 4. Validate that each vehicle exposes a usable device key before command dispatch. 5. Display and confirm the selected vehicle's non-sensitive name or identifier before safety-sensitive operations. 6. Add tests covering absent keys, invalid keys, duplicate or missing identifiers, multiple vehicles, and changes in vehicle ordering. A safer implementation would follow this logic: ```python def get_vehicle(client, device_key=None): vehicles = client.get_vehicles() if not vehicles: raise RuntimeError("No vehicles found on account") if device_key: matches = [] for vehicle in vehicles: info = getattr(vehicle, "info", None) actual_key = getattr(info, "device_key", None) if actual_key is None: actual_key = getattr(vehicle, "device_key", None) if actual_key is not None and str(actual_key) == str(device_key): matches.append(vehicle) if len(matches) != 1: raise RuntimeError("Configured device key did not uniquely identify a vehicle") return matches[0] if len(vehicles) != 1: raise RuntimeError("Multiple vehicles found; DRONEMOBILE_DEVICE_KEY is required") return vehicles[0] ```

T08 · Insecure Dependencies

Warning
Location
SKILL.md:22
Finding
Unpinned Third-Party Package Is Installed into System Python## Vulnerability Details **File Location**: `SKILL.md`, lines 22–25 **Vulnerability Type**: Unsafe and non-reproducible dependency installation **Risk Level**: Medium **Vulnerable Code**: ```markdown Install the library if not present: ```bash pip install drone-mobile --break-system-packages ``` ``` ### Technical Analysis The setup instructions install the latest version resolved for `drone-mobile` and its transitive dependencies without a version pin, lock file, or package hash verification. Consequently, the code reviewed during the audit may not be the same dependency code installed later. The `--break-system-packages` option also bypasses Python's externally managed environment protection and permits pip to modify the system-managed Python environment. Installation may execute package-controlled build or installation code and may replace versions required by other applications. Although the project history contains no evidence that the named package is currently malicious, this installation method unnecessarily increases supply-chain exposure and weakens environment isolation. ### Attack Path 1. A user follows the installation command in `SKILL.md`. 2. Pip resolves the package and transitive dependencies at installation time from its configured package index. 3. A compromised, malicious, or unexpectedly changed future package release is selected because no reviewed version or hash is enforced. 4. Package-controlled installation or build code executes with the privileges of the user running pip. 5. The package is installed into the system Python environment because `--break-system-packages` bypasses the normal protection. 6. The Skill imports the installed package, which receives the DroneMobile email and password and performs authenticated vehicle operations. ### Impact Assessment Installation-time code can act with the privileges of the account running pip. If the command is run as an administrator, im ...[truncated 477 chars]
Remediation
## Remediation Suggestions 1. Create and use a dedicated virtual environment rather than modifying system Python. 2. Pin `drone-mobile` to a specifically reviewed version. 3. Lock all transitive dependencies to reproducible versions. 4. Record and enforce cryptographic package hashes using a requirements file and `pip install --require-hashes`. 5. Remove `--break-system-packages` from the documented installation command. 6. Review dependency updates before changing locked versions and use automated vulnerability and provenance scanning. 7. Run the Skill under a dedicated, least-privileged operating-system account where practical. Example hardened installation workflow: ```bash python3 -m venv .venv . .venv/bin/activate python3 -m pip install --require-hashes -r requirements.txt ``` The corresponding `requirements.txt` should contain reviewed, exact versions and hashes for the direct package and every transitive dependency.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Missing User Warnings

High
Confidence
93% confidence
Finding
This skill can remotely start or stop a vehicle, unlock doors, and open the trunk without any explicit confirmation, warning, or additional authorization step at the point of action. In an agent setting, mistaken invocation, prompt confusion, or unauthorized access to the agent could trigger real-world safety and security consequences, including theft facilitation or unsafe vehicle operation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill requires sensitive environment credentials and performs privileged vehicle-control actions, but it does not declare any explicit tool scope or permissions boundary. That omission can allow broader-than-expected access patterns and makes it harder for a host platform to enforce least privilege for secrets and command execution.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This skill enables safety-critical remote actions such as starting a vehicle, unlocking doors, and opening the trunk, yet the description provides no explicit warning, confirmation requirement, or misuse caveat. In context, these are real-world physical actions affecting property and potentially personal safety, so absent warnings and guardrails increase the risk of accidental or unauthorized activation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code reads DRONEMOBILE_EMAIL and DRONEMOBILE_PASSWORD from environment variables to authenticate against a vehicle-control service. Although it errors if they are missing, there is no comment, docstring warning, or user-facing disclosure that the skill accesses sensitive credentials from the environment.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
print(f"   Engine: {engine} | Doors: {locked} | Battery: {batt}V | Temp: {temp}°C")
            print(f"   Door: {door} | Hood: {hood} | Mileage: {mileage} mi")
        else:
            method = getattr(vehicle, cmd)
            response = method()
            success = parse_success(response)
            temp, batt, engine = get_telemetry(response.raw_data)
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.