Back to skill

Security audit

amazon-daily-market-radar

Security checks for vulnerabilities and agentic risk

Overview

The skill is largely purpose-aligned for Amazon monitoring, but it has a real API-key transport weakness and a mutable install command that users should review before installing.

Review before installing. Use only the default HTTPS ZooData endpoint, do not set ZOODATA_BASE_URL to an http:// value, and prefer a pinned/verified installer instead of the README's unpinned npx command. Expect recurring runs to send ASINs, keywords, categories, and filters to ZooData and consume API credits; delete the skill data folder to reset retained baselines.

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

Error
Location
scripts/zoodata.py:64
Finding

Bearer Credential Can Be Transmitted over Plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: scripts/zoodata.py:64-86 and scripts/zoodata.py:338-378
Vulnerability Type: Insufficient transport security validation
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}"


BASE_URL = _resolve_base_url()
BASE_URL_TRUSTED = _is_trusted_host(BASE_URL)
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}

    for str_field in ("
...[truncated 3386 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse the configured URL with urllib.parse.urlparse() and validate the scheme, hostname, port, username, and password fields together.
  2. Require https for all ZooData domains before attaching the Authorization header.
  3. Prefer requiring HTTPS for loopback destinations as well.
  4. If plaintext loopback support is essential for development, disable it by default and require a separate explicit development-only opt-in, such as ZOODATA_ALLOW_INSECURE_LOOPBACK=1.
  5. Reject URLs containing embedded user information, fragments, malformed hosts, or unexpected schemes.
  6. Consider restricting production traffic to the exact expected hostname, api.zoodata.ai, rather than trusting every *.zoodata.ai subdomain.
  7. Construct the Bearer header only after all transport and destination checks succeed.
  8. Add automated tests proving that http://api.zoodata.ai, plaintext loopback URLs, non-ZooData hosts, deceptive suffixes, and URLs containing embedded credentials are rejected without resolving or transmitting the API key.

A hardened validation pattern should enforce the transport policy explicitly:

python
from urllib.parse import urlparse

def _is_trusted_api_url(url):
    parsed = urlparse(url)
    host = (parsed.hostname or "").lower()

    if parsed.username or parsed.password:
        return False
    if parsed.scheme != "https":
        return False
    if host != "api.zoodata.ai":
        return False

    return True

T08 · Insecure Dependencies

Warning
Location
README.md:16
Finding

Unpinned Package Is Downloaded and Executed during Installation

Content
View full analysis

Vulnerability Details

File Location: README.md:16-20
Vulnerability Type: Unpinned executable installation dependency
Risk Level: Medium

Vulnerable Code

markdown
## Install

```bash
npx skills add SerendipityOneInc/ZooData-Skills
text

### Technical Analysis

The documented installation procedure invokes `npx skills` without specifying a package version or integrity value. Depending on the local npm cache and `npx` behavior, this may retrieve and immediately execute the currently resolved version of the `skills` package.

The effective installer is therefore mutable after this Skill artifact has been reviewed. Its implementation and dependency graph are not included in the audited project, so the security properties of the installation process cannot be established from the bundled files.

This is a supply-chain weakness rather than evidence that the current installer is malicious. The risk arises because a compromised publisher account, registry package, transitive dependency, or later incompatible release could alter the code executed by users following the documented instructions.

### Attack Path

1. An attacker compromises the registry package, its publisher account, or a dependency used by the `skills` CLI.
2. A malicious or otherwise unsafe release becomes the version selected by the unpinned package reference.
3. A user follows the README and executes `npx skills add SerendipityOneInc/ZooData-Skills`.
4. `npx` downloads or selects the affected package version.
5. The package's CLI code executes with the permissions of the invoking user.
6. The compromised installer can read or modify files and access environment variables available to that process.

### Impact Assessment

Exploitation could execute arbitrary code with the invoking user's privileges during installation. Potential impact includes modification of user-owned files, installation of altered Skill content, theft of enviro
...[truncated 247 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin the installer to a specific audited version rather than relying on mutable package resolution:

    bash
    npx --yes skills@X.Y.Z add SerendipityOneInc/ZooData-Skills
    
  2. Publish the expected package name, version, registry, and integrity digest through a trusted release channel.

  3. Review and lock the installer's transitive dependencies.

  4. Prefer a verified installation mechanism that does not execute mutable latest-version registry code.

  5. Document how users can inspect the package and verify its integrity before execution.

  6. Use signed releases or provenance attestations for both the installer and the Skill bundle.

  7. Test upgrades separately and update the documented pinned version only after security review.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

A second, corroborating description-behavior mismatch exists: the skill claims day-over-day digesting and triaged alerting, but the visible behavior includes generic endpoint probing, broad analytics, and review-intelligence workflows without clear implementation of the promised scheduled comparison and alert synthesis. Such overclaiming/underdisclosure can cause unsafe delegation in automated environments, including unexpected API usage, data handling, and decisions based on capabilities the operator did not intend to enable.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

A second, corroborating description-behavior mismatch exists: the skill claims day-over-day digesting and triaged alerting, but the visible behavior includes generic endpoint probing, broad analytics, and review-intelligence workflows without clear implementation of the promised scheduled comparison and alert synthesis. Such overclaiming/underdisclosure can cause unsafe delegation in automated environments, including unexpected API usage, data handling, and decisions based on capabilities the operator did not intend to enable.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest describes a narrowly scoped unattended daily monitoring skill for tracked ASINs, but this file is a general-purpose ZooData CLI for many unrelated workflows, including market-entry analysis, pricing analysis, listing audits, opportunity scans, review deep-dives, and keyword research. Those operations are not merely implementation details of a daily radar digest; they substantially expand the behavioral surface beyond the claimed skill purpose.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The market-entry, opportunity, and opportunity-scan composites are designed for discovering new products, market opportunities, and entry analysis, including scanning many products and ranking candidates. That is a different capability from producing a yesterday-vs-today operational digest for user-specified ASIN watchlists.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises significant capabilities including environment-variable access, local file reads/writes, network access, and shell execution, but does not declare an explicit tool scope or permission boundary in the manifest. In an unattended automation context, this weakens reviewability and can let a broadly capable skill execute actions beyond what a user or platform reviewer would reasonably expect, increasing the risk of over-privileged execution and unintended data exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The file states that output language MUST match the user's input language and gives fixed examples for Chinese and English. This imposes a language behavior automatically rather than offering the user a choice, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The top-level docstring says this script is a single-script interface for all endpoints and composite workflows, but the code later enforces a per-skill allowed-commands manifest and may refuse much of that surface in a bundled skill. That documentation overstates what this bundled copy actually allows and can mislead maintainers or agents about the available behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/reference.md (reported line 25)May include surrounding context.

md
# ─── 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

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/zoodata.py (reported line 50)May include surrounding context.

python
# ─── 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

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/zoodata.py (reported line 87)May include surrounding context.

python
# ─── 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

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The review-tag prompt explicitly states 'Output must be in English' and instructs translation of non-English text before extraction. This imposes a specific language policy in natural-language instructions without offering the user or caller a language choice or documenting a justified locale constraint.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Competitor war-room, pricing analysis, and listing audit workflows perform deep one-off strategic benchmarking and audits rather than unattended daily change monitoring. Those capabilities may be useful generally, but they are not justified by the manifest’s narrowly framed operational radar purpose.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

These commands query keyword snapshots, market profiles, trend profiles, keyword expansion, SERP results, and ASIN traffic-term analytics. The manifest for this skill is about daily monitoring of specified ASINs and nearby market changes, not keyword research or search-term intelligence tooling.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/zoodata.py (reported line 1234)May include surrounding context.

python
"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_")

Static analysis

No suspicious patterns detected.