Back to skill

Security audit

Amazon Listing Optimizer

Security checks for vulnerabilities and agentic risk

Overview

This Amazon seller tool is mostly purpose-aligned, but it has unsafe input handling that can send research terms to non-Amazon hosts and write reports outside its intended folder.

Review before installing. Use only ordinary marketplace values such as com or co.uk, avoid confidential product plans or proprietary keywords, and be aware that the scripts scrape/query Amazon and save local JSON reports. The publisher should add marketplace allowlisting, ASIN/path validation, and clearer disclosure of report writing and third-party keyword queries.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyzer.py:30
Finding

Unvalidated Marketplace Input Allows Requests to Attacker-Controlled Hosts

Content
View full analysis

Vulnerability Details

File Locations:

  • scripts/analyzer.py:30-40
  • scripts/keyword_extractor.py:17-44
  • scripts/competitor_spy.py:15-26

Vulnerability Type: User-controlled network destination
Risk Level: Medium

Vulnerable Code

scripts/analyzer.py:30-40:

python
def fetch_listing(asin, marketplace="com"):
    """Fetch an Amazon listing page."""
    url = f"https://www.amazon.{marketplace}/dp/{asin}"
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
        "Accept-Language": "en-US,en;q=0.9",
    }
    req = urllib.request.Request(url, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=15) as resp:
            return resp.read().decode("utf-8", errors="ignore")

scripts/keyword_extractor.py:17-44:

python
def get_amazon_suggestions(keyword, marketplace="com"):
    """Get Amazon search autocomplete suggestions."""
    encoded = urllib.parse.quote(keyword)
    url = (
        f"https://completion.amazon.{marketplace}/api/2017/suggestions"
        f"?session-id=000-0000000-0000000"
        f"&customer-id=000000000"
        f"&request-id=000000000"
        f"&page-type=Gateway"
        f"&lop=en_US"
        f"&site-variant=desktop"
        f"&client-info=amazon-search-ui"
        f"&mid=ATVPDKIKX0DER"
        f"&alias=aps"
        f"&prefix={encoded}"
        f"&event=onKeyPress"
        f"&limit=11"
        f"&fb=1"
        f"&suggestion-type=KEYWORD"
    )

    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
        "Accept": "application/json",
    }

    try:
        req = urllib.request.Request(url, headers=headers)
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode())

scripts/competitor_spy.py:15-26:

python
def search_amazon(q
...[truncated 2725 chars]
Remediation
View remediation

Remediation Suggestions

Replace hostname interpolation with a fixed mapping from accepted marketplace identifiers to exact trusted hosts:

python
MARKETPLACE_HOSTS = {
    "com": "www.amazon.com",
    "co.uk": "www.amazon.co.uk",
    "de": "www.amazon.de",
    "fr": "www.amazon.fr",
    "it": "www.amazon.it",
    "es": "www.amazon.es",
    "ca": "www.amazon.ca",
    "com.au": "www.amazon.com.au",
}

def marketplace_host(marketplace):
    try:
        return MARKETPLACE_HOSTS[marketplace]
    except KeyError:
        raise ValueError("Unsupported Amazon marketplace")

Maintain a separate allowlist for autocomplete hosts where necessary. Additional hardening should include:

  • Rejecting marketplace values not present in the allowlist.
  • Parsing the final URL and verifying its exact hostname before each request.
  • Preventing redirects to hosts outside the allowlist with a restrictive redirect handler.
  • Applying response-size limits before loading remote content into memory.
  • Avoiding checks based only on hostname prefixes or the presence of the word amazon.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/analyzer.py:388
Finding

Unvalidated ASIN Allows Report Writes Outside the Reports Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/analyzer.py:388-404
Vulnerability Type: User-controlled output path
Risk Level: Low

Vulnerable Code

python
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 analyzer.py <ASIN> [marketplace]")
        print("Example: python3 analyzer.py B0XXXXXXXXX")
        print("Example: python3 analyzer.py B0XXXXXXXXX co.uk")
        sys.exit(1)

    asin = sys.argv[1]
    marketplace = sys.argv[2] if len(sys.argv) > 2 else "com"

    report = analyze_listing(asin, marketplace)

    if report:
        # Save report to file
        import os
        report_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "reports")
        os.makedirs(report_dir, exist_ok=True)
        report_file = os.path.join(report_dir, f"{asin}-report.json")
        with open(report_file, "w") as f:
            json.dump(report, f, indent=2)

Technical Analysis

The asin argument is used as part of the report filename without format validation or path confinement. Under Python path semantics, if the filename argument passed to os.path.join is absolute, the preceding report_dir is discarded.

For example:

python
os.path.join("/skill/reports", "/tmp/audit-report")

resolves to:

text
/tmp/audit-report

Because the code appends -report.json, an ASIN such as /tmp/audit causes the destination to resolve to /tmp/audit-report.json rather than a file beneath the intended reports directory.

The write occurs only when analyze_listing returns a report, so exploitation requires the listing fetch to return a response that the script treats as successful. The written content is generated JSON rather than arbitrary attacker-selected bytes.

Attack Path

  1. An attacker supplies an absolute-path-like value as the ASIN:
    bash
    python3 analyzer.py /tmp/audit
    

...[truncated 1061 chars]

Remediation
View remediation

Remediation Suggestions

Validate ASINs before using them in either URLs or filenames. Standard ASINs should be constrained to ten uppercase alphanumeric characters:

python
ASIN_PATTERN = re.compile(r"^[A-Z0-9]{10}$")

if not ASIN_PATTERN.fullmatch(asin):
    raise ValueError("Invalid ASIN")

Also enforce path confinement independently of input validation:

python
report_dir = os.path.realpath(
    os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "reports")
)
os.makedirs(report_dir, exist_ok=True)

report_file = os.path.realpath(
    os.path.join(report_dir, f"{asin}-report.json")
)

if os.path.commonpath([report_dir, report_file]) != report_dir:
    raise ValueError("Invalid report path")

Additional hardening should include:

  • Deriving filenames only from validated identifiers.
  • Using os.path.basename where user-derived filename components are unavoidable.
  • Rejecting absolute paths and path separators explicitly.
  • Avoiding overwrites by using exclusive creation mode when replacement is unnecessary.
  • Running the Skill with minimal filesystem permissions.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill's trigger conditions are broad enough to match generic Amazon-related requests, such as listing quality, competitor analysis, or mentions of common seller tools. This can cause unintended invocation outside a narrowly scoped user intent, leading the agent to route users into scraping/analysis workflows they did not explicitly request and increasing the chance of inappropriate or excessive external data access.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The exact-match list contains highly generic triggers such as "seasoning blend," "seasoning blend for chicken," and similar broad commercial phrases that are likely to match ordinary user shopping or cooking queries. This can cause the skill to activate outside its intended scope, leading to skill hijacking, misrouting, or inappropriate interception of benign user requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The broad-match entries include vague phrases like "seasoning blender," "seasoning blends for cooking," and product-like strings without explicit scoping rules, making activation conditions ambiguous. Broad matching on such common language increases the chance of accidental triggering from normal cooking, shopping, or recipe-related requests, which is dangerous because it expands interception well beyond a narrow user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The request header hard-codes "Accept-Language": "en-US,en;q=0.9", which imposes a specific language/locale behavior on all users. This is a natural-language policy concern because the file does not offer a user choice or explain why English is required for this skill.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The top-level documentation says the script 'analyzes listings' and 'finds gaps in the market,' which implies substantive competitor evaluation. In practice, the implemented logic only fetches Amazon search results, extracts ASINs, and emits canned suggestions; it does not inspect listings in detail or perform any actual market-gap analysis.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The top-level documentation says the tool finds keywords from both competitor listings and search suggestions. In the implementation, all keyword discovery flows through get_amazon_suggestions, which only calls Amazon's autocomplete API; there is no code to fetch, parse, or analyze competitor listings.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script transmits the user-provided seed keyword and derived variations to Amazon's autocomplete endpoint without explicit advance disclosure or consent. If a user supplies sensitive business terms, product plans, or proprietary research queries, those terms are exposed to a third party and may be logged or profiled externally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The script saves keyword research results into a JSON file under the reports directory, but this behavior is not disclosed in the top-level description or CLI usage text before execution. The save is only revealed after the operation completes.

Content

No source excerpt is available for this finding.