Back to skill

Security audit

amazon-market-entry-analyzer

Security checks for vulnerabilities and agentic risk

Overview

The skill is a mostly disclosed ZooData market-analysis wrapper, but it needs review because its API-key transport and install instructions create real credential and supply-chain risks.

Review before installing. Use the default HTTPS ZooData endpoint, do not set ZOODATA_BASE_URL unless you control and trust it, prefer ZOODATA_API_KEY in the environment over a persistent config file, and pin the installer/repository version when possible. Treat review-derived insights as untrusted evidence that can be influenced by marketplace review text.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/zoodata.py:65
Finding
Bearer Credential May Be Transmitted over Plaintext HTTP## Vulnerability Details **File Location**: `scripts/zoodata.py`, lines 65-84 and 329-362 **Vulnerability Type**: Insufficient URL scheme validation for authenticated API requests **Risk Level**: High ### Vulnerable Code ```python def _is_trusted_host(url): """True only for ZooData hosts and localhost — the sole destinations the API key (Bearer token) may be sent to. Any other host is untrusted and the key is withheld (see api_call), so credentials never reach an arbitrary host.""" host = _host_of(url) return host == "zoodata.ai" or host.endswith(".zoodata.ai") or host in ("localhost", "127.0.0.1") def _resolve_base_url(): """Resolve API base URL, allowing zoodata.ai / localhost hosts via ZOODATA_BASE_URL.""" configured = os.environ.get("ZOODATA_BASE_URL", DEFAULT_BASE_URL).strip().rstrip("/") if configured.rstrip("/") != DEFAULT_BASE_URL.rstrip("/") and not _is_trusted_host(configured): print(f"WARNING: ZOODATA_BASE_URL points at untrusted host '{_host_of(configured)}'. " "Your API key (Bearer token) will NOT be sent there — requests to untrusted " "hosts are refused. Use a zoodata.ai host or localhost.", file=sys.stderr) if configured.endswith(API_BASE_PATH): return configured return f"{configured}{API_BASE_PATH}" ``` ```python def api_call(endpoint: str, params: dict) -> dict: global _last_request_time url = f"{BASE_URL}/{endpoint}" if not BASE_URL_TRUSTED: print(f"ERROR: refusing to send your API key to untrusted host '{_host_of(BASE_URL)}'. " "Set ZOODATA_BASE_URL to a zoodata.ai host or localhost, or unset it.", file=sys.stderr) sys.exit(1) api_key = get_api_key() params = {k: v for k, v in params.items() if v is not None} actual_params = dict(params) body = json.dumps(params).encode("utf-8") headers = ...[truncated 1976 chars]
Remediation
## Remediation Suggestions 1. Parse the complete configured URL with `urllib.parse.urlparse`. 2. Require `scheme == "https"` for `zoodata.ai` and all permitted subdomains. 3. Reject URLs containing user-information, malformed ports, fragments, or unexpected path components. 4. Prefer a fixed production origin rather than allowing arbitrary ZooData subdomains. 5. Disable authenticated plaintext localhost requests by default. If local development support is required, place it behind an explicit development-only flag and use a separate non-production credential. 6. Validate the final constructed URL immediately before every authenticated request, rather than relying only on module-level state. 7. Add tests proving that `http://api.zoodata.ai`, `http://localhost`, protocol-relative URLs, and malformed URLs are rejected before credential resolution.

T01 · Skill Instruction Hijacking

Warning
Location
scripts/zoodata.py:910
Finding
Externally Controlled Review Text Is Embedded Directly into Agent Instructions## Vulnerability Details **File Location**: `scripts/zoodata.py`, lines 910-953; workflow invocation in `SKILL.md`, lines 91-101 **Vulnerability Type**: Indirect prompt injection through untrusted marketplace content **Risk Level**: Medium ### Vulnerable Code ```python def render_review_map_prompt(review: dict, product_title: str = "", product_category: str = "") -> str: title = review.get("title") or "" body = review.get("body") or "" full = f"{title}. {body}" if title else body text = full[:500] rating = review.get("rating") or 3 verified = bool(review.get("verifiedPurchase")) return f"""IMPORTANT: Respond ONLY with a JSON object matching the schema below. Output must be in English — translate non-English text before extracting. You are an expert data extraction specialist analyzing product reviews. Extract only what is EXPLICITLY mentioned — do not infer. JSON schema: {{ "sentiment": "positive" | "neutral" | "negative", "mentioned_scenarios": [string], "mentioned_issues": [string], "mentioned_positives": [string], "mentioned_improvements": [string], "mentioned_buying_factors": [string], "mentioned_pain_points": [string], "user_profiles": [string], "mentioned_usage_times": [string], "mentioned_usage_locations": [string], "mentioned_behaviors": [string], "keywords": [string] }} Rules: - sentiment: positive (4-5 stars or praise), neutral (3 stars / mixed), negative (1-2 stars or complaint) - pain_points = problems EXPERIENCED AFTER USE. NOT problems the product solves. - issues vs pain_points: issues = product defects (hardware/software fault); pain_points = UX frustrations - user_profiles: include ONLY if the reviewer explicitly states an identity ("I'm a...", "As a..."). NEVER infer. - consistent naming across reviews (e.g. always "Workouts", not "At the gym") - use empty arrays [] for categories with no mentions, never null INPUT ...[truncated 2849 chars]
Remediation
## Remediation Suggestions 1. Treat all review fields as hostile data and state explicitly that instructions appearing inside them must never be followed. 2. Use separate structured message fields or a constrained extraction API instead of concatenating review content into the instruction string. 3. Serialize review data as JSON and avoid delimiter-based interpolation. If delimiters remain necessary, escape delimiter sequences and control characters. 4. Validate every model response against `REVIEW_MAP_SCHEMA`, including required keys, exact types, allowed sentiment values, maximum list sizes, and string-length limits. 5. Reject unexpected keys, non-string elements, malformed output, and content that resembles tool or control instructions. 6. Ensure model output cannot directly select commands, trigger tools, alter workflow policy, or write persistent state. 7. Add adversarial tests covering triple quotes, Markdown fences, fake system messages, tool-call syntax, and instructions to ignore the extraction schema. 8. Mark review-derived conclusions as untrusted or inferred unless corroborated by independent data.

T08 · Insecure Dependencies

Warning
Location
README.md:22
Finding
Documented Installation Uses Unpinned Third-Party Tooling and Mutable Repository Content## Vulnerability Details **File Location**: `README.md`, lines 22-26 **Vulnerability Type**: Unpinned software supply-chain installation **Risk Level**: Medium ### Vulnerable Code ```markdown ## Install ```bash npx skills add SerendipityOneInc/ZooData-Skills ``` Select **Amazon Market Entry Analyzer** when prompted. ``` ### Technical Analysis The installation procedure invokes the `skills` package through `npx` without specifying a package version. It also identifies repository content without pinning a release tag or immutable commit hash. As a result, the code executed by `npx` and the Skill content installed from the repository can change after this artifact has been audited. Users following the documentation are not guaranteed to receive the reviewed version. No evidence shows that the current package or repository is malicious. The confirmed weakness is the absence of version and integrity pinning, which creates a supply-chain substitution opportunity. ### Attack Path 1. The npm package, package-publishing account, repository, or repository-maintainer account is compromised, or a future mutable release introduces unsafe code. 2. An attacker publishes a modified `skills` package or changes the repository content resolved by the unpinned identifier. 3. A user follows the documented `npx skills add SerendipityOneInc/ZooData-Skills` command. 4. `npx` resolves and executes the currently available package rather than a previously audited version. 5. The installer retrieves and installs repository content that may differ from this audited artifact. 6. The substituted installer or Skill code executes with the permissions of the user running the installation. ### Impact Assessment If an upstream component is compromised, the substituted code may obtain all privileges available to the installing user. Depending on the installer and host environment, this can include reading user files and environment variabl ...[truncated 296 chars]
Remediation
## Remediation Suggestions 1. Pin the npm installer to a reviewed exact version, such as `npx skills@X.Y.Z`, and disable automatic acceptance of newer versions. 2. Pin Skill repository content to an immutable release artifact or full commit hash. 3. Publish cryptographic checksums or signed provenance for release artifacts. 4. Verify downloaded content before installation and fail closed on integrity mismatch. 5. Document the exact package version, repository commit, and expected hashes corresponding to each audited release. 6. Prefer a package manager lockfile or a verified installer already present in the environment instead of executing newly resolved code through unpinned `npx`. 7. Apply least privilege during installation and avoid exposing production credentials to the installer process.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The file claims a specific GO/CAUTION/AVOID market-viability workflow using 11 endpoints, but the described capabilities include broader endpoint probing, keyword intelligence, historical retrieval, and review prompt-rendering/aggregation without showing the promised decision logic. This creates a confused-deputy risk where the skill can be selected for a simple assessment but operate as a general API/shell wrapper, expanding attack surface and weakening policy enforcement, auditability, and user consent around what data is processed and which actions occur.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The file claims a specific GO/CAUTION/AVOID market-viability workflow using 11 endpoints, but the described capabilities include broader endpoint probing, keyword intelligence, historical retrieval, and review prompt-rendering/aggregation without showing the promised decision logic. This creates a confused-deputy risk where the skill can be selected for a simple assessment but operate as a general API/shell wrapper, expanding attack surface and weakening policy enforcement, auditability, and user consent around what data is processed and which actions occur.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The manifest describes a skill that should evaluate a specific niche/category and return one GO/CAUTION/AVOID market-entry recommendation. This file implements numerous additional workflows such as competitor war-room analysis, pricing analysis, daily monitoring, listing audits, opportunity discovery, and review deep-dives, which materially exceed that described behavior even if they are gated by an allowlist at runtime.

Rp1

Medium
Category
MCP Rug Pull
Confidence
89% confidence
Finding
The README instructs users to run `npx skills add SerendipityOneInc/ZooData-Skills` without pinning a specific package or repository version. This can expose users to supply-chain risk if the referenced package, installer, or resolved content changes over time or is compromised, causing different code to execute than what was originally reviewed.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill exposes sensitive capabilities (environment access for API keys, file reads, network access, and shell execution) but does not declare a restrictive tool scope such as permissions or allowed-tools. That creates a governance gap: a host agent may grant broader capabilities than the skill actually needs, increasing the chance of unintended command execution, credential access, or data exfiltration if the skill is misrouted or later modified.

External Transmission

Medium
Category
Data Exfiltration
Content
# ─── Configuration ───────────────────────────────────────────────────────────

DEFAULT_BASE_URL = "https://api.zoodata.ai/openapi/v2"
API_BASE_PATH = "/openapi/v2"
KEYWORD_DATE_RANGE_MAX_DAYS = 93
KEYWORD_TIMELINE_MAX_DAYS = 61
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# ─── Configuration ───────────────────────────────────────────────────────────

DEFAULT_BASE_URL = "https://api.zoodata.ai/openapi/v2"
API_BASE_PATH = "/openapi/v2"
KEYWORD_DATE_RANGE_MAX_DAYS = 93
KEYWORD_TIMELINE_MAX_DAYS = 61
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# ─── Configuration ───────────────────────────────────────────────────────────

DEFAULT_BASE_URL = "https://api.zoodata.ai/openapi/v2"
API_BASE_PATH = "/openapi/v2"
KEYWORD_DATE_RANGE_MAX_DAYS = 93
KEYWORD_TIMELINE_MAX_DAYS = 61
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
print("    export ZOODATA_API_KEY='hms_live_yourkey'", file=sys.stderr)
    print("", file=sys.stderr)
    print("  Method 2: User-home config (persistent, shared across all skills)", file=sys.stderr)
    print("    mkdir -p ~/.zoodata && chmod 700 ~/.zoodata", file=sys.stderr)
    print('    (umask 077; echo \'{"api_key":"hms_live_yourkey"}\' > ~/.zoodata/config.json)', file=sys.stderr)
    print("    # keep the file private (0600) — it holds a bearer credential", file=sys.stderr)
    print("", file=sys.stderr)
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
print("    export ZOODATA_API_KEY='hms_live_yourkey'", file=sys.stderr)
    print("", file=sys.stderr)
    print("  Method 2: User-home config (persistent, shared across all skills)", file=sys.stderr)
    print("    mkdir -p ~/.zoodata && chmod 700 ~/.zoodata", file=sys.stderr)
    print('    (umask 077; echo \'{"api_key":"hms_live_yourkey"}\' > ~/.zoodata/config.json)', file=sys.stderr)
    print("    # keep the file private (0600) — it holds a bearer credential", file=sys.stderr)
    print("", file=sys.stderr)
Confidence
82% confidence
Finding
The CLI explicitly supports persisting a bearer API key in ~/.zoodata/config.json, creating long-lived credential storage on disk shared across skills. Even with recommended private permissions, persistent secrets increase the blast radius of local compromise, accidental backup/sync exposure, or reuse by other bundled tooling beyond the user's immediate intent.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This code file contains a natural-language instruction that says the output 'must be in English' and requires translation of non-English text before extraction. That imposes a specific language policy on downstream use without offering a language choice or documenting a justified region-specific constraint.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The manifest says this skill is for one-click market viability assessment of a named niche/category, but the in-file help and subcommand docs actively present commands like review-deepdive, listing-audit, daily-radar, pricing-analysis, and opportunity-scan as available features. That documentation conflicts with the skill's stated intent and would mislead users or agents about what this particular skill is meant to do.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This markdown file documents authentication via `Bearer $ZOODATA_API_KEY`, which involves use of a sensitive credential. Under the markdown-file warning criterion, the document does not include any caution about protecting the API key, avoiding accidental disclosure, or handling authenticated requests carefully.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
"priceMin", "priceMax", "ratingMin", "ratingMax", "bsrMin", "bsrMax",
                 "salesGrowthRateMin", "salesGrowthRateMax", "sellerCountMin", "sellerCountMax",
                 "variantCountMin", "variantCountMax"):
        val = getattr(args, attr.replace("Min", "_min").replace("Max", "_max")
                      .replace("monthly", "monthly_").replace("review", "review_")
                      .replace("sales", "sales_").replace("Growth", "_growth_")
                      .replace("Rate", "rate_").replace("price", "price_")
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.