Back to skill

Security audit

Coffee Prices by City

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed China-focused coffee price estimator, with privacy and hygiene cautions around automatic IP-based city lookup, CSV output, and an unpinned dependency.

Install only if a reference-price estimator is acceptable, not a live official-price source. Provide --city explicitly to avoid the automatic ipinfo.io lookup, avoid opening CSV output from untrusted city inputs in spreadsheet software, and prefer a pinned dependency set for reproducible installs.

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

Warning
Location
scripts/coffee_prices.py:206
Finding
Spreadsheet Formula Injection in CSV Output## Vulnerability Details **File Location**: `scripts/coffee_prices.py`, lines 206-213 **Vulnerability Type**: CSV/spreadsheet formula injection **Risk Level**: Medium ### Vulnerable Code ```python def output_csv(rows: List[CoffeePriceRow]) -> None: writer = csv.writer(os.sys.stdout) writer.writerow( ["brand", "brand_en", "city", "drink", "drink_en", "price", "currency"] ) for row in rows: writer.writerow( [row.brand, row.brand_en, row.city, row.drink, row.drink_en, row.price, row.currency] ) ``` ### Technical Analysis The `city` value can originate directly from the `--city` command-line argument or the `OPENCLAW_CITY` environment variable. It is retained in `row.city` and written to CSV without neutralizing spreadsheet formula prefixes. The Python CSV writer correctly quotes and escapes CSV syntax, but CSV quoting does not stop spreadsheet software from treating a cell beginning with `=`, `+`, `-`, or `@` as a formula. Consequently, an attacker-controlled city such as `=HYPERLINK(...)` may be evaluated when the resulting file is opened in a spreadsheet application. ### Attack Path 1. An attacker influences the city through `--city` or `OPENCLAW_CITY`. 2. The script passes the value through `resolve_city()` and `build_rows()` without formula-prefix validation. 3. The user selects CSV output, for example: ```bash python3 scripts/coffee_prices.py --city '=HYPERLINK("https://attacker.example","Click")' --output csv ``` 4. `output_csv()` writes the attacker-controlled value into the `city` column. 5. A victim opens the generated CSV in spreadsheet software. 6. Depending on the spreadsheet application and its security settings, the cell may be interpreted as a formula, potentially causing an external request or presenting deceptive content. ### Impact Assessment Exploitation does not grant privileges within the Python process itself. The i ...[truncated 452 chars]
Remediation
## Remediation Suggestions Sanitize every externally controllable string before writing it to CSV. At minimum, neutralize values whose first non-whitespace character is `=`, `+`, `-`, or `@`. Prefixing such values with an apostrophe is commonly used when spreadsheet compatibility is required. ```python def sanitize_csv_cell(value: object) -> object: if not isinstance(value, str): return value stripped = value.lstrip() if stripped.startswith(("=", "+", "-", "@")): return "'" + value return value def output_csv(rows: List[CoffeePriceRow]) -> None: writer = csv.writer(os.sys.stdout) writer.writerow( ["brand", "brand_en", "city", "drink", "drink_en", "price", "currency"] ) for row in rows: writer.writerow( [ sanitize_csv_cell(row.brand), sanitize_csv_cell(row.brand_en), sanitize_csv_cell(row.city), sanitize_csv_cell(row.drink), sanitize_csv_cell(row.drink_en), row.price, sanitize_csv_cell(row.currency), ] ) ``` Apply the safeguard to all string columns rather than only `city`, so future changes do not reintroduce the issue. Add tests covering leading formula characters, leading whitespace, tabs, carriage returns, and ordinary city names. If strict input validation is acceptable, reject city values containing control characters or beginning with spreadsheet formula markers.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unpinned Third-Party Dependency Allows Non-Reproducible Installation## Vulnerability Details **File Location**: `requirements.txt`, line 1 **Vulnerability Type**: Unbounded dependency version and missing integrity hashes **Risk Level**: Low ### Vulnerable Code ```text requests>=2.31.0 ``` The documented installation command in `SKILL.md`, line 25, installs this mutable dependency set: ```bash pip install -r requirements.txt ``` ### Technical Analysis The lower-bound-only constraint permits the package resolver to install any future version of `requests` that satisfies the requirement, together with whatever transitive dependency versions are selected at installation time. No lock file or package hashes are provided. This configuration does not establish that the current `requests` package is malicious. However, it makes installations non-reproducible and prevents verification that users receive the exact dependency versions reviewed and tested by the project. A compromised package-index account, compromised configured index, or malicious future dependency release could therefore affect subsequent installations without any change to this repository. ### Attack Path 1. A user follows the documented command `pip install -r requirements.txt`. 2. `pip` resolves the latest available versions satisfying `requests>=2.31.0`, using the user's configured package indexes. 3. If a selected package release or package index has been compromised, attacker-controlled package content is downloaded. 4. The content is installed into the user's Python environment and may subsequently execute when imported or invoked. This path depends on an external supply-chain compromise or untrusted package-index configuration; the audited repository itself does not contain evidence of a malicious dependency. ### Impact Assessment If the dependency supply chain were compromised, malicious package code could execute with the privileges of the user performing installation or running the skill. This could ...[truncated 346 chars]
Remediation
## Remediation Suggestions Pin reviewed direct and transitive dependency versions and verify downloaded artifacts with cryptographic hashes. For example, generate a locked requirements file with `pip-tools` or an equivalent dependency-management tool: ```bash pip-compile --generate-hashes requirements.in pip install --require-hashes -r requirements.txt ``` The generated file should contain exact versions and SHA-256 hashes for `requests` and all transitive dependencies. Update dependencies through a controlled review process, run automated tests and vulnerability scanning before accepting updates, and avoid installing from untrusted or unexpected package indexes. Document the supported Python version so dependency resolution remains consistent across environments.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill claims to fetch current coffee prices, but the document itself reveals it actually uses hardcoded reference prices, reads OPENCLAW_CITY, and may call ipinfo.io for IP-based city inference. This mismatch is security-relevant because it obscures real data flows and capabilities, preventing informed consent and making it easier to hide unexpected external access or environment-derived behavior behind a harmless-seeming description.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The skill metadata promises to fetch current coffee prices from major chains, but the implementation only derives synthetic estimates from hard-coded base prices and city-tier multipliers. This creates a deceptive capability mismatch that can mislead downstream users or agents into treating fabricated data as live market information, which is especially risky for benchmarking or decision-making workflows.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documentation describes behavior that uses environment data and external network access, but it does not declare any corresponding tool scope or permissions. This is dangerous because an agent or platform may execute the skill with broader capabilities than the user expects, enabling silent access to local environment state and outbound requests for IP-based geolocation.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The natural-language instructions, examples, and operational guidance in the skill are predominantly Chinese, which effectively forces a specific language for users. The file does not indicate that Chinese is optional, configurable, or limited to a justified region-specific deployment.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The code performs IP-based geolocation via a third-party service to infer the runtime city, which exceeds the minimum functionality needed for a coffee-price comparison tool. This leaks network/contextual metadata to an external service without necessity and may reveal deployment location or user environment information.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The CLI/help text frames the tool as operating on a user-specified city, yet the implementation silently falls back to environment-derived or IP-derived location. This mismatch undermines informed consent and can cause the tool to process or disclose location context that the user did not intentionally provide.

Natural-Language Policy Violations

Medium
Confidence
79% confidence
Finding
The CLI description, help text, supported city examples, and output labels are all in Chinese, and the city handling logic is tailored to Chinese city names. There is no indication that users can opt into another language or locale, which can violate language/locale policy when a skill forces a specific language by default.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
When no city is supplied, the skill automatically makes a network request to ipinfo.io to infer location, without an execution-time warning or explicit consent. Even if limited, this transmits runtime metadata to a third party and creates an unnecessary privacy and supply-chain exposure for a nonessential feature.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
Confidence
97% confidence
Finding
The dependency is specified as `requests>=2.31.0`, which allows any newer release to be installed without review. This is a real supply-chain hygiene issue because builds may become non-reproducible and could unexpectedly pull in a vulnerable or breaking version later, even though it is not an immediately exploitable flaw by itself.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
Because `requests` is not pinned, it is impossible to verify from this manifest whether deployment will use a version affected by known advisories. In a skill that likely performs external HTTP fetching for coffee-price data, that uncertainty matters more because network-facing libraries are directly exercised against untrusted remote endpoints.

Context-Inappropriate Capability

Low
Confidence
84% confidence
Finding
The manifest frames the skill as taking a user-specified city and outputting brand comparisons. Reading OPENCLAW_CITY introduces ambient environment-data access that is not necessary to the core pricing task and is not mentioned in the skill description.

Static analysis

No suspicious patterns detected.