Back to skill

Security audit

Supermarket Deals (DE)

Security checks for vulnerabilities and agentic risk

Overview

This deal-search skill is not destructive, but it should be reviewed because it scrapes and stores Marktguru API/client keys and sends searches plus ZIP codes to that third party.

Install only if you are comfortable with this skill using Marktguru's embedded API/client keys, storing those keys and your default ZIP/store preferences under ~/.supermarket-deals, and sending each product query plus ZIP code to Marktguru. Avoid sensitive searches, and prefer a version that uses a documented API or clearly approved authentication path and sanitizes terminal output.

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

Warning
Location
src/formatter.ts:136
Finding

Unsanitized Remote Data Enables Terminal Escape-Sequence Injection

Content
View full analysis
{ const safeId = sanitizeOfferId(offer.id); return safeId ? `https://www.marktguru.de/offers/${safeId}` : null; })(), }; ``` ```ts const rows = deals.map((deal) => { const productDetails = formatProductDetails(deal); const url = deal.url ?? "-"; return [ pad(productDetails, headers.productDetails), pad(deal.store, headers.store), pad(deal.size, headers.size), pad(formatPrice(deal.price), headers.price), pad(formatPricePerLitre(deal.pricePerLitre), headers.litre), pad(`${deal.validFrom} – ${deal.validTo}`, headers.validity), url, ].join(" | "); }); return [headerLine, separator, ...rows].join("\n"); ``` ### Technical Analysis The product name, description, advertiser name, unit name, and potentially invalid date values originate from the remote Marktguru API. These strings are trimmed and truncated but are not sanitized for ANSI escape sequences, carriage returns, C0/C1 control characters, or terminal-specific operating system command sequences. The resulting values are included directly in the table returned by `formatDealsTable()` and subsequently passed to `console.log()`. Truncating a string does not make terminal control sequences safe because short escape sequences can still clear the screen, reposition the cursor, ...[truncated 1828 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Scraping HTML to extract apiKey/clientKey values is credential acquisition behavior unrelated to normal client-side input handling and materially expands the skill’s trust boundary. In the context of a simple deal-search skill, this is especially concerning because it obtains third-party access tokens from page internals rather than through a supported integration path.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

The code explicitly extracts API keys from Marktguru’s homepage configuration, which is a form of credential acquisition from a third-party service. In this skill context, that behavior is more dangerous because it is hidden behind a benign shopping use case and facilitates unauthorized or unsupported API access.

Content

Scanner excerpt · src/api.ts (reported line 97)May include surrounding context.

ts
}
  }

  throw new Error("Could not extract API keys from marktguru homepage JSON config.");
}

async function fetchFreshKeys(): Promise<ApiKeys> {

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README describes sending product queries and a ZIP code to Marktguru, but it does not clearly warn users that their location-adjacent data and search terms are transmitted to an external third-party service. This creates a privacy/transparency issue because users may unknowingly disclose shopping interests and approximate location data, especially when the skill is used in automated agent pipelines.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill performs network access to Marktguru and fetches rotating API keys, but the manifest does not declare any tool scope such as permissions or allowed-tools. This creates a capability-transparency problem: an agent or reviewer cannot easily tell from the skill metadata that external network requests will occur, which can lead to unintended data exposure, policy bypass, or execution in environments that assume no outbound access.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · src/api.ts (reported line 6)May include surrounding context.

ts
import path from "node:path";

const MARKTGURU_HOME = "https://www.marktguru.de";
const SEARCH_ENDPOINT = "https://api.marktguru.de/api/v1/offers/search";
const CACHE_TTL_MS = 6 * 60 * 60 * 1000;
const CACHE_DIR = path.join(os.homedir(), ".supermarket-deals");
const KEYS_PATH = path.join(CACHE_DIR, "keys.json");

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill writes fetched third-party API keys to a local file in the user’s home directory without transparency or protection controls. Even if these are not the user’s own secrets, local persistence can expose them to other local processes or users and normalizes undisclosed credential storage.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code programmatically harvests hidden API credentials from Marktguru’s homepage, uses them to access a backend API, and stores them locally despite the skill claiming no API key is needed. This bypasses normal credential ownership and disclosure expectations, creates legal/abuse risk, and can break if the provider rotates or restricts those keys.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The function sends the user's search term and location data to https://api.marktguru.de via fetch, which is a network operation transmitting potentially sensitive user data. This file does not include a confirmation prompt, visible log, or explanatory docstring/comment informing the user that their inputs are sent to a third-party service.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The README states the skill is for German supermarkets and later requires a German postal code, which imposes a locale constraint. While the domain is Germany-focused, the documentation does not explicitly frame this as a region-specific limitation or user opt-in choice, so it can be read as a fixed locale requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The package description states that the skill searches "German supermarket deals," which imposes a specific locale context in the skill metadata. Because there is no accompanying indication of user choice or opt-in in this file, this can be interpreted as a locale-specific policy constraint expressed in natural language.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 26)May include surrounding context.

json
"node": ">=18"
  },
  "devDependencies": {
    "@types/node": "^22.15.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  }

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 27)May include surrounding context.

json
},
  "devDependencies": {
    "@types/node": "^22.15.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 28)May include surrounding context.

json
"devDependencies": {
    "@types/node": "^22.15.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  }
}

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The manifest frames the skill as searching supermarket flyers and ranking deals, which implies network retrieval and result processing. This file additionally creates a hidden directory under the user's home directory and reads/writes a config file there, introducing local persistent state that is not mentioned in the description.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The code persists configuration data with saveConfig(cfg), which writes user-provided ZIP/store preferences to a local config file. While this is part of the config command's purpose, this file provides no explicit user-facing notice at the point of write beyond printing the resulting config afterward, so users are not warned beforehand that data will be stored on disk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.