Back to skill

Security audit

BVG (Berliner Verkehrsbetriebe) Route Planner

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent Berlin transit route planner that calls the disclosed BVG API, with minor privacy and input-validation caveats but no hidden persistence, credential access, or destructive behavior.

Use this for Berlin transit queries with the understanding that your route details, locations, and travel times may be sent to the public BVG transport.rest service. Treat the bundled shell script as a convenience helper and avoid passing untrusted or malformed datetime strings until it URL-encodes that argument.

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

T09 · Insecure Skill Coding Practices

Note
Location
scripts/journeys.sh:18
Finding
Unencoded datetime permits HTTP query-parameter injection## Vulnerability Details **File Location**: `scripts/journeys.sh`, lines 18–20 **Vulnerability Type**: HTTP query-parameter injection caused by missing URL encoding and input validation **Risk Level**: Low ### Vulnerable Code ```bash if [[ "$mode" == "arrival" ]]; then q="/journeys?from=${from_enc}&to=${to_enc}&arrival=${when}&results=3&stopovers=true" else q="/journeys?from=${from_enc}&to=${to_enc}&departure=${when}&results=3&stopovers=true" fi ``` ### Technical Analysis The script correctly URL-encodes the origin and destination but inserts the caller-controlled `when` argument directly into the query string. Reserved characters such as `&`, `=`, and `#` can therefore change the request structure rather than being treated as part of the datetime value. A legitimate ISO 8601 timestamp containing a `+` timezone offset may also be interpreted incorrectly because form-style query parsers commonly decode `+` as a space. Furthermore, `mode` is not strictly validated: every value other than `arrival` silently selects the `departure` branch. This is HTTP parameter injection, not shell-command injection. The completed URL is passed to `curl` as one quoted argument, and the destination scheme and host remain fixed. ### Attack Path 1. An attacker or untrusted caller invokes the wrapper and controls its fourth argument. 2. The caller supplies a value containing query delimiters, for example: ```text x&results=100 ``` 3. The script directly interpolates that value into the URL: ```text /journeys?...&arrival=x&results=100&results=3&stopovers=true ``` 4. The fixed BVG endpoint receives attacker-injected query parameters. 5. Depending on duplicate-parameter handling by the upstream service, the injected parameter may alter processing, increase the response size, or produce misleading journey data. ### Impact Assessment Exploitation requires control over the script arguments. It can manip ...[truncated 444 chars]
Remediation
## Remediation Suggestions 1. Strictly validate `mode` rather than treating every unexpected value as `departure`: ```bash if [[ "$mode" != "arrival" && "$mode" != "departure" ]]; then printf 'Invalid mode: expected arrival or departure\n' >&2 exit 2 fi ``` 2. Validate `when` as an accepted ISO 8601 datetime or other explicitly supported format before sending it to the API. 3. URL-encode the datetime just as the origin and destination are encoded: ```bash when_enc=$(urlencode "$when") if [[ "$mode" == "arrival" ]]; then q="/journeys?from=${from_enc}&to=${to_enc}&arrival=${when_enc}&results=3&stopovers=true" else q="/journeys?from=${from_enc}&to=${to_enc}&departure=${when_enc}&results=3&stopovers=true" fi ``` 4. Prefer delegating query construction to `curl` so that every value is independently encoded: ```bash curl --silent --get "${base}/journeys" \ --data-urlencode "from=${from_raw}" \ --data-urlencode "to=${to_raw}" \ --data-urlencode "${mode}=${when}" \ --data-urlencode "results=3" \ --data-urlencode "stopovers=true" ``` 5. Add tests covering timezone offsets and reserved characters, including `+`, `&`, `=`, `#`, and percent-encoded input.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill performs external API access and the analyzer detected broader code-capable behaviors, but the manifest does not declare any explicit tool scope or permissions boundary. That creates a least-privilege and transparency problem: an agent runtime may grant more capability than users or reviewers expect, increasing the chance of unintended network access or command execution if the skill is extended or interpreted loosely.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The skill instructs the agent to send route and stop queries to an external BVG API but does not warn users that their provided locations, stop names, coordinates, and travel times may be transmitted off-platform. This is a privacy and consent issue because travel queries can reveal sensitive location patterns even when the API usage itself is legitimate.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The line instructs consumers to 'Prefer Europe/Berlin timezone for parsing,' which is a locale-specific requirement stated as a default rather than an optional or user-selected behavior. Under the policy rule, forcing a specific locale without opt-in can be a natural-language policy violation unless it is explicitly justified as region-specific.

Static analysis

No suspicious patterns detected.