Back to skill

Security audit

Brouter

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent route-generation purpose, but it writes caller-directed files, sends precise route data over plain HTTP, and keeps undisclosed local logs of location details.

Review before installing. This skill sends route coordinates to brouter.de using plain HTTP and saves GPX files locally. It also logs detailed route and path information, and its output path controls should be tightened so it can only write inside a dedicated routes directory.

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

T09 · Insecure Skill Coding Practices

Error
Location
index.js:58
Finding
Caller-Controlled Output Path Allows Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 58-65 and 151-155 **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```javascript const { start, end, profile = 'trekking', originLabel, destinationLabel, outputDir = path.resolve(process.cwd(), 'routes'), fileName, } = options; ``` ```javascript const finalFileName = fileName || defaultFileName; const outputPath = path.resolve(outputDir, finalFileName); await fs.promises.mkdir(path.dirname(outputPath), { recursive: true }); await fs.promises.writeFile(outputPath, gpxText, 'utf8'); ``` ### Technical Analysis The caller can directly control both `outputDir` and `fileName`. The code resolves these values into an absolute path without verifying that the resulting path remains inside the intended `routes` directory. A `fileName` containing parent-directory traversal sequences such as `../../target` can escape the output directory. An absolute `fileName` can also cause `path.resolve()` to discard the preceding output directory. A caller-controlled absolute `outputDir` provides another direct way to select an arbitrary destination. The destination directory is recursively created, and `fs.promises.writeFile()` overwrites an existing file by default. The content written to the selected path is the GPX response received from the routing server. Exploitation is limited by the operating-system permissions of the Node.js process, but no application-level path boundary is enforced. ### Attack Path 1. An attacker gains the ability to invoke `run()` or influence its `options` object. 2. The attacker supplies a traversal or absolute path through `fileName` or `outputDir`. 3. The Skill requests route data from the remote routing service. 4. `path.resolve()` produces a destination outside the intended `routes` directory. 5. The code recursively creates missing parent directories where permitted. 6. `writeFile()` creates or ove ...[truncated 710 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove caller control over `outputDir` unless it is strictly required. - Store generated files beneath a single trusted directory selected by the application. - Permit only a basename for `fileName`; reject absolute paths, path separators, `.` segments, and `..` segments. - Resolve the candidate path and verify that it remains beneath the trusted directory: ```javascript const trustedRoot = path.resolve(process.cwd(), 'routes'); const safeName = path.basename(fileName || defaultFileName); const outputPath = path.resolve(trustedRoot, safeName); const relative = path.relative(trustedRoot, outputPath); if (relative.startsWith('..') || path.isAbsolute(relative)) { throw new Error('Invalid output path'); } ``` - Use an allowlist for filename characters and enforce the `.gpx` extension. - If overwriting is unnecessary, write with the exclusive `wx` flag. - Run the Skill under a dedicated, minimally privileged account with write access limited to the route directory. - Consider defenses against symbolic-link attacks if the output directory may be modified by untrusted local users. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
index.js:76
Finding
Route Coordinates and GPX Responses Are Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 76-87 **Vulnerability Type**: Cleartext transmission of sensitive location data and unauthenticated response content **Risk Level**: High ### Vulnerable Code ```javascript const lonlats = `${start}|${end}`; const baseUrl = 'http://brouter.de/brouter'; const params = new URLSearchParams(); params.set('lonlats', lonlats); params.set('profile', profile); params.set('format', 'gpx'); params.set('alternativeidx', '0'); params.set('nogos', ''); const requestUrl = `${baseUrl}?${params.toString()}`; let response; let status; try { response = await fetch(requestUrl); ``` ### Technical Analysis The Skill transmits the start and destination coordinates, routing profile, and other request parameters over unencrypted HTTP. The returned GPX content is likewise delivered without transport confidentiality or integrity protection. Any party able to observe or modify traffic between the Skill and `brouter.de` can read the user's route coordinates or alter the response. The implementation treats a successful HTTP response as trusted and writes its body to disk without authenticating its origin or validating that the response is a legitimate GPX document. This issue compounds the arbitrary-file-write finding: where an attacker can both influence the output path and intercept the network connection, the attacker can control the content written to a caller-selected writable file. ### Attack Path 1. A user requests a route, causing the Skill to place the start and destination coordinates in an HTTP query string. 2. An attacker on the local network, an untrusted proxy, or another network intermediary observes the plaintext request. 3. The attacker learns the user's route coordinates and selected profile. 4. The attacker may intercept the HTTP response and replace the expected GPX document with attacker-selected content. 5. The Skill accepts the successful response and writes the modified content to dis ...[truncated 587 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace the HTTP endpoint with the routing service's HTTPS endpoint: ```javascript const baseUrl = 'https://brouter.de/brouter'; ``` - Retain standard TLS certificate validation and do not disable verification. - Restrict redirects or verify that every redirect destination uses HTTPS and belongs to an approved host. - Apply request timeouts and response-size limits. - Validate the response content type and parse the GPX successfully before persisting it. - Consider applying structural GPX validation before returning the file to a user or downstream application. - Avoid including precise coordinates in logs or error messages. ]]>

other

Warning
Location
index.js:55
Finding
Persistent Logging Exposes Complete Invocation Options and Location Data<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 55-71, 91-108, 123-140, and 195-215 **Vulnerability Type**: Sensitive data exposure through excessive logging **Risk Level**: Medium ### Vulnerable Code ```javascript console.log('[brouter] run() invoked with options:', options); const { start, end, profile = 'trekking', originLabel, destinationLabel, outputDir = path.resolve(process.cwd(), 'routes'), fileName, } = options; logEvent({ type: 'brouter.run.start', options: { ...options }, }); ``` ```javascript logEvent({ type: 'brouter.run.fetch_error', options: { ...options }, request: { url: requestUrl, lonlats, profile, }, response: null, error: { message: err.message, name: err.name, }, }); ``` ```javascript logEvent({ type: 'brouter.run.error', options: { ...options }, request: { url: requestUrl, lonlats, profile, }, response: { status, body: body || null, }, error: { message: error.message, }, }); ``` ```javascript logEvent({ type: 'brouter.run.success', options: { ...options }, request: { url: requestUrl, lonlats, profile, }, response: { status, result: { gpxPath: result.gpxPath, lonlats: result.lonlats, profile: result.profile, summary: result.summary, debug: result.debug, }, }, }); ``` ### Technical Analysis The complete caller-provided `options` object is printed to standard output and copied into persistent log records. Additional log fields duplicate route coordinates, the full request URL, profile, output path, server response body on errors, and result metadata. Because the code logs the entire options object rather than an allowlisted subset, any additional sensitive properties supplied by a caller will also be retained. The log is appended to `brouter.log` without rotation, retention controls, explicit restrictive permissions, or size limits. The documentation doe ...[truncated 1122 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not log the complete `options` object. - Use an explicit allowlist of non-sensitive operational fields. - Omit or redact precise coordinates, labels, request query strings, response bodies, and local filesystem paths. - Log a generated request identifier rather than the complete request URL. - Remove the startup `console.log()` or replace it with a minimal event message. - Create log files with restrictive permissions, such as mode `0600`, where supported. - Add log rotation, maximum-size limits, and a documented retention period. - Sanitize control characters and bound the length of any user-derived value retained in logs. - Clearly document any necessary collection of route metadata and obtain appropriate user consent where required. ]]>

T08 · Insecure Dependencies

Note
Location
package.json:4
Finding
Unpinned Dependencies and Missing Lockfile Make Installations Non-Reproducible<![CDATA[ ## Vulnerability Details **File Location**: `package.json`, lines 4-6 **Vulnerability Type**: Dependency supply-chain hardening weakness **Risk Level**: Low ### Vulnerable Code ```json "dependencies": { "node-fetch": "^2", "gpx-parse": "^0.10.1" } ``` No dependency lockfile is present in the audited project structure. ### Technical Analysis The project specifies dependencies using semver ranges and does not include a lockfile. Consequently, separate installations may resolve to different package versions without any corresponding change to the reviewed repository. This is not evidence that the named packages are malicious. It is a supply-chain hardening weakness: a future compromised, defective, or unexpectedly changed version satisfying the declared range could be installed and executed through the existing `require()` calls. The absence of committed integrity metadata also prevents reviewers from determining the exact transitive dependency graph used in deployment. ### Attack Path 1. The project is installed without a committed lockfile. 2. The package manager resolves the broad dependency ranges against the registry state at installation time. 3. A newly published compatible release or changed transitive dependency is selected. 4. The selected package contents differ from those previously reviewed or tested. 5. The Skill loads the dependency through `require()`, executing package code within the Node.js process's permission boundary. This path requires an upstream compromise, malicious compatible release, registry incident, or unsafe dependency update; no such compromise was established during this audit. ### Impact Assessment If a resolved dependency were compromised, its code would execute with the same filesystem, network, and process privileges as the Skill. Potential consequences could include data disclosure, file modification, or arbitrary code execution within that existing privilege boundary. As currently evidenced, the conf ...[truncated 112 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Generate and commit the appropriate package-manager lockfile with integrity hashes. - Install dependencies in deployment and CI using a frozen or clean-install mode, such as `npm ci`. - Pin reviewed direct dependency versions where operationally appropriate. - Review the complete transitive dependency tree before release. - Run automated dependency vulnerability and provenance checks in CI. - Use controlled dependency-update tooling so version changes receive explicit review and testing. - Consider package-manager policies or registry controls that restrict installation to approved packages and sources. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared purpose understates important behaviors: the skill writes persistent route/log files, depends on pre-geocoded coordinates, and stores output locally. This mismatch can mislead users and reviewers about data handling, especially when route endpoints may reveal sensitive location information.

Ae1

High
Category
analysis-evasion
Content
Parse all location data to coordinates before using `index.js`. Never use free-text descriptions of locations directly as input for `index.js`. Use coordinates
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Parse all location data to coordinates before using `index.js`. Never use free-text descriptions of locations directly as input for `index.js`. Use coordinates
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Parse all location data to coordinates before using `index.js`. Never use free-text descriptions of locations directly as input for `index.js`. Use coordinates
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Parse all location data to coordinates before using `index.js`. Never use free-text descriptions of locations directly as input for `index.js`. Use coordinates
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The caller can fully control outputDir and fileName, and the code resolves and writes the resulting path without constraining it to a safe base directory. This turns the skill into a general arbitrary file writer, allowing overwrite or creation of files outside the intended routes directory if an attacker can influence inputs.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill invokes a networked routing service but does not declare any tool scope or permissions in the manifest. That creates a transparency and policy-enforcement gap: agents or users may not realize the skill sends data externally, and platforms cannot easily constrain or review that capability.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill directs the agent to send origin and destination data to brouter.de without informing the user that their route endpoints will be shared with a third party. Location data is often sensitive, so undisclosed transmission creates a meaningful privacy risk even if the service itself is legitimate.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The skill persistently writes operational data to local disk, including a dedicated log file and generated GPX content. While file output for a GPX-producing skill is partly expected, the persistent logging extends data retention beyond the narrowly described purpose and can expose sensitive route/location information to other local users or later processes.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill logs the full options object at invocation and later records request/response context, which can include route coordinates, labels, filenames, and paths. Persisting this information without user awareness creates unnecessary local privacy exposure and long-term retention of sensitive movement/location data.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill sends precise route coordinates to an external service over plain HTTP, not HTTPS, with no user disclosure. This creates both a privacy issue and a transport-security issue: location data can be observed or modified in transit by network attackers.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The instructions tell the agent to run the implementation and return a file from the routes folder without warning that route artifacts are written to local disk. Silent persistence of user route data can expose travel patterns to later processes, users, or logs on the same system.

Intent-Code Divergence

Low
Confidence
81% confidence
Finding
The inline documentation says callers must provide coordinates and use a separate geocoding step, while the manifest describes use for routes between two places. That is an intent-level discrepancy because the documentation narrows supported input semantics compared with the advertised behavior.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The skill creates directories and writes the fetched GPX route to a local file, which affects user data on disk. The code documents this behavior for developers, but it does not provide a user-facing disclosure or confirmation about file creation and storage location.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"name": "brouter",
  "main": "index.js",
  "dependencies": {
    "node-fetch": "^2",
    "gpx-parse": "^0.10.1"
  }
}
Confidence
94% confidence
Finding
The dependency version for node-fetch is specified with a caret range (^2), which allows different 2.x releases to be installed over time. This weakens build reproducibility and can unexpectedly pull in vulnerable or behavior-changing versions, especially relevant here because node-fetch has known advisories in some 2.x releases.

Unverifiable Dependency: node-fetch has 3 known advisory(ies) (CVE-2022-0235 (node-fetch forwards secure headers to untrusted sites); CVE-2022-2596 (node-fetch Inefficient Regular Expression Complexity ); CVE-2020-15168 (The `size` option isn't honored after following a redirect in node-fetch)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
The manifest uses an unpinned node-fetch dependency while that package has known published advisories affecting some versions. Because the exact installed release is not fixed, consumers of the skill may unknowingly install an affected version, which is particularly relevant in a skill that performs network requests to generate routes.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"main": "index.js",
  "dependencies": {
    "node-fetch": "^2",
    "gpx-parse": "^0.10.1"
  }
}
Confidence
85% confidence
Finding
The dependency gpx-parse is also specified with a caret range (^0.10.1), allowing non-deterministic installs within the permitted range. This creates supply-chain and reproducibility risk because future installs may resolve to different code than what was originally tested.

Static analysis

No suspicious patterns detected.