Back to skill

Security audit

Mvg

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Munich transit CLI, but its install and live-tracking paths can run mutable or locally hijackable code, so it needs user review before installation.

Install only from a reviewed, pinned commit or trusted versioned package, avoid the sudo install option, and treat the live command as higher risk because it executes Node.js and trusts local ws module resolution. The normal MVG lookup commands are read-only network queries, but the live-tracking implementation and exposed geOps key should be fixed before broad use.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
README.md:17
Finding
Unpinned Remote Repository Retrieval and Execution## Vulnerability Details **File Location**: `README.md`, lines 17-20 **Vulnerability Type**: Mutable remote payload execution **Risk Level**: High ```bash # With uvx (recommended) — runs instantly without installation uvx --from git+https://github.com/Lars147/mvg-cli mvg search "Marienplatz" uvx --from git+https://github.com/Lars147/mvg-cli mvg departures "Marienplatz" uvx --from git+https://github.com/Lars147/mvg-cli mvg route "Marienplatz" "Garching" ``` ### Technical Analysis The recommended quick-start commands instruct users to retrieve and execute the current contents of a remote Git repository. The repository reference is not pinned to a reviewed commit hash, immutable tag, or hash-verified package artifact. Consequently, the code executed by future users can differ from the code covered by this audit. Although remote installation is relevant to distributing a CLI, directly executing a mutable repository exceeds the minimum trust necessary. A compromised maintainer account, repository, dependency declaration, or upstream branch could replace the effective payload without changing this audited artifact. ### Attack Path 1. An attacker compromises the referenced GitHub repository or a maintainer account. 2. The attacker modifies the default branch, package metadata, build backend behavior, or CLI entry point. 3. A user follows the recommended `uvx --from git+https://...` command. 4. `uvx` retrieves the changed repository and runs its build or application code. 5. The malicious code executes with the operating-system privileges of the invoking user. ### Impact Assessment Successful exploitation permits arbitrary code execution in the user's context. The resulting payload could access files readable by that user, modify user-owned configuration, use available network credentials, or install user-level persistence. Administrative privileges are not inherently obtained, but the impact expands if the command is run ...[truncated 41 chars]
Remediation
## Remediation Suggestions - Pin the Git source to a reviewed full commit hash rather than the mutable default branch. - Prefer a signed, versioned release distributed through a trusted package registry. - Publish and verify cryptographic hashes for release artifacts. - Use a lockfile for build and runtime dependencies. - Avoid presenting execution from an unpinned Git source as the recommended installation method. - Document how users can verify release signatures and source provenance before execution.

T08 · Insecure Dependencies

Error
Location
mvg_cli.py:439
Finding
Unsafe Node.js Module Resolution from a Shared Agent Workspace## Vulnerability Details **File Location**: `mvg_cli.py`, lines 439-448 **Vulnerability Type**: Untrusted dependency loading and local tool hijacking **Risk Level**: High ```javascript // Try multiple locations for ws module const paths = [ process.env.HOME + '/.openclaw/workspace/node_modules/ws', '/app/node_modules/.pnpm/ws@8.19.0/node_modules/ws', ]; let WebSocket; for (const p of paths) { try { WebSocket = require(p); break; } catch(e) {} } if (!WebSocket) { try { WebSocket = require('ws'); } catch(e) { process.stderr.write('ws module not found'); process.exit(1); } } ``` ### Technical Analysis The live-tracking implementation preferentially loads the `ws` module from hardcoded shared locations, including `~/.openclaw/workspace/node_modules/ws`. It does not verify the module version, package integrity, ownership, permissions, or cryptographic hash before calling `require()`. Loading a Node.js package executes its initialization code. If another workspace component or local process can create or replace the module at the preferred path, invoking `mvg live` executes attacker-controlled JavaScript. The generic `require('ws')` fallback also relies on ambient Node.js resolution rather than an isolated, locked project dependency. The Skill only needs a WebSocket implementation. Trusting arbitrary code from a shared Agent workspace is not necessary for that functionality and crosses the expected least-trust boundary. ### Attack Path 1. An attacker gains write access to `~/.openclaw/workspace/node_modules/ws`, or causes a malicious package to be installed there. 2. The attacker places malicious initialization code in the forged `ws` module. 3. The user invokes the `live` command. 4. Python creates and starts the temporary Node.js script. 5. The script prioritizes the shared workspace path and calls `require()` on the malicious module. 6. The attacker's JavaScript executes with the privileges and enviro ...[truncated 466 chars]
Remediation
## Remediation Suggestions - Declare `ws` as an explicit, project-local dependency and pin an audited version in a lockfile. - Remove resolution from `~/.openclaw/workspace` and other shared or hardcoded package directories. - Run Node.js with an isolated module path controlled by this project. - Verify package integrity using lockfile hashes and trusted registry metadata. - Reject dependency directories that are writable by unrelated users or workspace components. - Prefer a Python WebSocket implementation with a pinned dependency if this eliminates the cross-runtime module-resolution requirement. - Consider running live tracking in a restricted subprocess with a minimized environment and filesystem permissions.

T09 · Insecure Skill Coding Practices

Warning
Location
mvg_cli.py:43
Finding
Hardcoded External-Service API Key Exposed in Source and URL## Vulnerability Details **File Location**: `mvg_cli.py`, lines 43-45 and line 480 **Vulnerability Type**: Hardcoded credential and query-string secret exposure **Risk Level**: Medium ```python GEOPS_WS_URL = "wss://api.geops.io/realtime-ws/v1/" GEOPS_API_KEY = "5cc87b12d7c5370001c1d655112ec5c21e0f441792cfc2fafe3e7a1e" GEOPS_ORIGIN = "https://s-bahn-muenchen-live.de" ``` ```python url = f"{GEOPS_WS_URL}?key={GEOPS_API_KEY}" ``` ### Technical Analysis A geOps API key is embedded directly in the source and appended to the WebSocket URL as a query parameter. Anyone with access to the repository or distributed artifact can extract and reuse it. Query-string credentials may additionally be exposed through debugging output, process instrumentation, network middleware, proxy logs, or service-side request logs. The audit could not establish the key's server-side scope, so it should not be assumed to be harmless merely because it may be intended for a public-facing application. ### Attack Path 1. An attacker obtains the source repository or packaged Skill. 2. The attacker extracts the `GEOPS_API_KEY` constant. 3. The attacker submits direct requests to the geOps WebSocket endpoint using the exposed key. 4. The attacker consumes associated quota or accesses any operations permitted by the key. 5. Legitimate live tracking may be disrupted if the key is rate-limited, suspended, or revoked following abuse. ### Impact Assessment The direct impact is unauthorized reuse of the external-service credential. Depending on server-side restrictions, this may cause quota exhaustion, service degradation, unexpected charges, attribution of abusive traffic to the legitimate client, or unauthorized access to additional API capabilities. This finding does not expose local user credentials and does not independently provide local system privileges.
Remediation
## Remediation Suggestions - Revoke and rotate the exposed key. - Remove the key from source code and repository history. - Load the credential from a protected runtime secret or environment variable. - Avoid placing credentials in query strings when the service supports an authorization header or another protected authentication mechanism. - Restrict the key by origin, endpoint, scope, rate, and quota wherever supported. - Use a dedicated low-privilege key intended for this client. - Ensure exception messages, subprocess diagnostics, and network logs redact credentials. - If geOps explicitly treats this as a public client identifier, document that status and still apply strict server-side restrictions to prevent abuse.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (11)

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The README instructs users to execute code directly from a Git repository via `uvx --from git+https://github.com/Lars147/mvg-cli` without pinning to a specific commit, tag, or release. This creates a supply-chain risk: if the upstream repository is later compromised or changed, users will transparently run attacker-controlled code.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
This command repeats the same unsafe pattern of running the tool directly from an unpinned GitHub repository. Because the command is presented as a quickstart path, it meaningfully increases the chance that users execute whatever code currently exists at the remote source.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
Like the other quickstart examples, this line encourages execution from mutable remote source code without version pinning. In a CLI skill context, users may treat README commands as trustworthy setup steps, making repository compromise or malicious force-pushes especially dangerous.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
alias mvg="python3 /pfad/zu/mvg_cli.py"

# Option B: Ins PATH kopieren
sudo cp mvg_cli.py /usr/local/bin/mvg
```

**Voraussetzungen:** Python 3.9+ und `requests`
Confidence
91% confidence
Finding
The README suggests `sudo cp mvg_cli.py /usr/local/bin/mvg`, which normalizes using elevated privileges to install an unreviewed script from the repository into a globally trusted execution path. If the script is malicious or later replaced before copy, the user may grant system-wide execution trust to attacker-controlled code.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises executable capabilities involving environment access, file writing, network access, and shell usage, but the manifest does not declare any explicit tool scope or permissions boundaries. This creates an authorization ambiguity where an agent may invoke a more powerful runtime than users expect, increasing the risk of unintended command execution, network egress, or local file modification if the implementation is compromised or behaves unexpectedly.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger description is broad and includes generic public-transit terms, which can cause the skill to activate in conversations that are only loosely related to Munich transit. Over-broad activation expands the attack surface by increasing the chance the agent routes unrelated user input into a networked, code-capable skill, potentially causing unnecessary external requests or unexpected tool use.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Natural-language help text, command descriptions, and user-facing output are written exclusively in German, which imposes a language choice on all users. Under the policy, locale or language constraints should either be optional, user-selectable, or clearly justified as region-specific.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
A hard-coded third-party API key embedded in source code is a credential exposure issue. Anyone with access to the code can reuse the key, abuse the third-party service, exhaust quota, or cause attribution and billing problems for the maintainer.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The live-tracking feature writes and executes a temporary JavaScript file through a local Node.js runtime and probes multiple local module paths. That expands the skill from simple transit API querying into local code execution and environment inspection, increasing risk if the runtime or searched module paths are compromised or manipulated.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
            url = f"{GEOPS_WS_URL}?key={GEOPS_API_KEY}"
            out_file = js_path + ".json"
            result = subprocess.run(
                ["node", js_path, url, GEOPS_ORIGIN, str((timeout - 2) * 1000), out_file],
                capture_output=True, text=True, timeout=timeout
            )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
97% confidence
Finding
The skill description, headings, examples, and usage instructions are entirely in German, which can constitute a language/locale policy issue when no user opt-in or alternative language is provided. Although the tool is Munich-specific, the README does not explicitly justify that the documentation itself is intentionally German-only or offer another language option.

Static analysis

No suspicious patterns detected.