Back to skill

Security audit

FBA Send to Amazon

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Amazon FBA automation, but it needs Review because it can perform high-impact seller-account actions and includes an unguarded cancellation path plus weak API endpoint validation.

Review this before installing in a production seller account. Use a least-privilege SP-API authorization, keep the endpoint on the official Amazon SP-API host, avoid create_inbound_plan.py --void, use only the guarded void_plan.py dry run plus --yes flow for cancellations, and do not let an agent drive a signed-in Seller Central browser unless you explicitly intend that session use. Treat generated result JSON and label PDFs as sensitive business records.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/spapi.py:58
Finding
Configurable SP-API Endpoint Can Receive Amazon Access Tokens and Sensitive Payloads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/spapi.py:58-60, 90-109` **Vulnerability Type**: Unrestricted authenticated API destination **Risk Level**: High ### Vulnerable Code ```python ENDPOINT = os.environ.get("SPAPI_ENDPOINT") or _config().get( "endpoint", "https://sellingpartnerapi-na.amazon.com") MARKETPLACE = os.environ.get("SPAPI_MARKETPLACE_ID") or _config().get( "marketplace_id", "ATVPDKIKX0DER") ``` ```python def headers() -> dict: return {"x-amz-access-token": access_token(), "Content-Type": "application/json"} def request(method: str, url: str, body=None, params=None, retries: int = 6): if params: url += "?" + urllib.parse.urlencode(params) data = json.dumps(body).encode() if body is not None else None last: Exception | None = None for attempt in range(retries): req = urllib.request.Request(url, data=data, headers=headers(), method=method) try: with urllib.request.urlopen(req, timeout=40) as resp: raw = resp.read() return json.loads(raw) if raw else {} ``` ### Technical Analysis The base SP-API endpoint can be supplied through either the `SPAPI_ENDPOINT` environment variable or the `endpoint` property in `~/.config/sp-api/credentials.json`. The value is not validated to ensure that: - The scheme is HTTPS. - The destination belongs to an approved Amazon SP-API domain. - User information is not present in the URL. - Redirects do not move the request to an untrusted destination. Every request constructed from this endpoint includes the `x-amz-access-token` bearer credential. State-changing workflow requests can also include seller inventory, SKUs, source addresses, telephone numbers, email addresses, carrier selections, and shipment identifiers. Reading SP-API credentials and sending a derived access token to Amazon is necessary for the declared functionality. Allowing the authenticated destination to be an arbitrary host exceeds ...[truncated 1246 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the endpoint with `urllib.parse.urlparse`. 2. Require `scheme == "https"`. 3. Allowlist the documented regional Amazon SP-API hosts, for example: - `sellingpartnerapi-na.amazon.com` - `sellingpartnerapi-eu.amazon.com` - `sellingpartnerapi-fe.amazon.com` 4. Reject URL credentials, unexpected ports, fragments, and paths in the configured base endpoint. 5. Prevent authenticated requests from following redirects to hosts outside the allowlist. 6. Separate unauthenticated downloads from the authenticated SP-API request function. 7. Fail closed with a clear error when endpoint validation fails. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/spapi.py:125
Finding
Documented Smoke Test Logs a Portion of the Amazon Access Token<![CDATA[ ## Vulnerability Details **File Location**: `scripts/spapi.py:125-128` **Vulnerability Type**: Partial credential disclosure through logging **Risk Level**: Low ### Vulnerable Code ```python if __name__ == "__main__": print(f"endpoint {ENDPOINT}") print(f"marketplace {MARKETPLACE}") print(f"token {access_token()[:12]}… ok") ``` ### Technical Analysis The smoke test prints the first 12 characters of the LWA access token. Although this prefix alone is generally insufficient to authenticate, access-token material should not be exposed in terminal output. The Skill explicitly documents running `python3 scripts/spapi.py` as a smoke test, increasing the chance that this output will be captured in CI logs, shell transcripts, support bundles, screen recordings, or agent conversations. The token prefix provides no operational value beyond what a non-sensitive success message would provide. ### Attack Path 1. A user follows the documented smoke-test instruction. 2. The script obtains a valid access token. 3. The first 12 token characters are printed. 4. Terminal output is retained by automation, copied into a support request, or surfaced in an agent transcript. 5. Anyone able to read those records obtains partial secret material. ### Impact Assessment This does not disclose the complete bearer token and therefore does not independently provide authenticated SP-API access. It nevertheless weakens secret-handling hygiene and can assist token correlation or expose implementation details in shared logs. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions Replace the token-prefix output with a non-sensitive confirmation: ```python if __name__ == "__main__": print(f"endpoint {ENDPOINT}") print(f"marketplace {MARKETPLACE}") access_token() print("token exchange: ok") ``` Additionally, ensure exception handling never includes request bodies, authorization headers, refresh tokens, client secrets, or complete access tokens in logs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_inbound_plan.py:308
Finding
Alternate Cancellation Interface Bypasses Shipment Fingerprint and Confirmation Safeguards<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create_inbound_plan.py:308-329` **Vulnerability Type**: Unsafe destructive operation **Risk Level**: High ### Vulnerable Code ```python def void(plan_id): # Cancellation is PUT /cancellation — not DELETE, not POST /void. _op("PUT", f"/inboundPlans/{plan_id}/cancellation", {}, f"cancel {plan_id[:12]}") print(f"{plan_id} -> {_req('GET', f'/inboundPlans/{plan_id}').get('status')}") def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("config", nargs="?", help="plan config JSON") ap.add_argument("--resume", metavar="PLAN_ID") ap.add_argument("--void", nargs="+", metavar="PLAN_ID") ap.add_argument("--out", help="where to write the result JSON") args = ap.parse_args() if args.void: for pid in args.void: void(pid) return ``` ### Technical Analysis The project includes `void_plan.py`, whose documented safety design requires: - An explicitly supplied plan ID. - An exact set of expected FBA shipment IDs. - A dry run by default. - A separate `--yes` flag before cancellation. However, `create_inbound_plan.py` exposes an alternate `--void` option that immediately cancels every supplied plan ID. It performs no shipment-fingerprint comparison, has no dry-run default, and requires no explicit confirmation flag. This creates a direct bypass of the project's intended protection against cancelling a similar-looking or incorrectly selected production plan. ### Attack Path 1. A user, automation system, or agent selects an incorrect, stale, or mistyped plan ID. 2. It invokes: ```bash python3 scripts/create_inbound_plan.py --void <PLAN_ID> ``` 3. The script immediately sends: ```http PUT /inboundPlans/<PLAN_ID>/cancellation ``` 4. Amazon processes the cancellation without the local script checking the plan's expected shipment set. ...[truncated 669 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--void` from `create_inbound_plan.py`. 2. Make `void_plan.py` the only supported cancellation interface. 3. If cancellation must remain available from the main script, reuse the exact verification implementation from `void_plan.py`. 4. Require all of the following: - A single explicit plan ID. - The complete expected FBA shipment-ID set. - Exact equality between expected and actual shipment sets. - Dry-run behavior by default. - A separate `--yes` confirmation. 5. Refuse batch cancellation so a human confirms each destructive operation independently. 6. Display the current plan status and shipment fingerprint before accepting confirmation. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:93
Finding
Third-Party PDF and Numerical Dependencies Are Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:93-94` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Instruction ```text Dependencies: stdlib only, except `get_labels.py`, which needs `pip3 install pymupdf numpy` for the split and square steps. ``` The same installation guidance appears in `scripts/get_labels.py`: ```text Requires PyMuPDF and numpy: pip3 install pymupdf numpy ``` ### Technical Analysis The installation command resolves whatever versions are current when the command is run. There is no lock file, version constraint, hash verification, or isolated-environment requirement. These packages are imported while processing label PDFs obtained over the network: ```python import fitz import numpy as np ``` A compromised package release, package-index account compromise, or incompatible future release could therefore affect installation or runtime behavior. PyMuPDF also processes externally supplied PDF structures, making timely and deliberate security-version management important. No evidence shows that the named packages are currently malicious. The issue is the absence of reproducible and integrity-checked dependency controls. ### Attack Path 1. A user follows the documented `pip3 install pymupdf numpy` command. 2. The package resolver selects mutable latest releases and their dependencies. 3. A compromised or unexpectedly changed release is downloaded. 4. Package installation hooks or imported package code execute under the user's account. 5. Malicious package code could access files and credentials available to that user, including the SP-API credential file. ### Impact Assessment If the dependency supply chain is compromised, arbitrary code could execute with the invoking user's privileges. This could expose local files, Amazon credentials, shipment records, and any other resources accessible to that account. The practical likelihood is lower than a direct embedded paylo ...[truncated 131 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Provide a reviewed `requirements.txt` or lock file with exact versions. 2. Include package hashes and install with: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` 3. Document use of an isolated virtual environment. 4. Periodically update pins after reviewing upstream security advisories. 5. Avoid broad dependency ranges for PDF-processing libraries. 6. Consider separating raw label download from optional PDF transformation so users who do not need transformation do not need these packages. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/get_labels.py:184
Finding
Response-Controlled Label URL Is Downloaded and Parsed Without Destination or Size Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/get_labels.py:184-196` **Vulnerability Type**: Unrestricted remote download and unbounded content processing **Risk Level**: Medium ### Vulnerable Code ```python query = [("MarketplaceId", MARKETPLACE), ("PageType", args.page_type), ("LabelType", "UNIQUE")] + [("PackageLabelsToPrint", b) for b in box_ids] url = (f"{ENDPOINT}/fba/inbound/v0/shipments/{fba}/labels?" + urllib.parse.urlencode(query)) download_url = request("GET", url)["payload"]["DownloadURL"] pdf = urllib.request.urlopen(download_url, timeout=60).read() fba_out = outdir / f"fba_{fba}_{fc}.pdf" carrier_out = outdir / f"{args.carrier_prefix}_{fba}_{fc}.pdf" got_fba, got_carrier = split_and_fix(pdf, fba_out, carrier_out) ``` The downloaded bytes are subsequently passed to PyMuPDF: ```python src = fitz.open(stream=pdf_bytes, filetype="pdf") ``` ### Technical Analysis The script trusts the API response's `DownloadURL` and retrieves it with `urllib.request.urlopen` without validating: - That the URL uses HTTPS. - That the host belongs to an expected Amazon label-storage domain. - Whether redirects remain on approved HTTPS hosts. - The response media type. - The maximum allowed response size. - Whether the content begins with a valid PDF signature before parsing. Calling `.read()` without a limit loads the entire response into memory. A malicious or compromised API endpoint can return an arbitrary URL or an oversized response. This issue is more readily exploitable because `SPAPI_ENDPOINT` itself is configurable without host validation. ### Attack Path 1. An attacker controls or alters the configured API endpoint, or compromises the API response path. 2. The fake endpoint returns a successful label response containing an attacker-selected `DownloadURL`. 3. `get_labels.py` connects to that destination without checking its scheme or host. 4. The remote server returns either: - An extremely large response, causing me ...[truncated 935 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse `DownloadURL` before opening it. 2. Require HTTPS. 3. Allowlist documented Amazon or Amazon-controlled label-storage hosts. 4. Disable redirects or validate the scheme and destination after every redirect. 5. Stream the response in bounded chunks instead of calling unrestricted `.read()`. 6. Enforce a conservative maximum label-file size. 7. Check the response content type and verify the `%PDF-` signature. 8. Reject compressed or decoded content exceeding the configured limit. 9. Keep PyMuPDF pinned to a reviewed, supported security release. 10. Process PDFs in a restricted subprocess or sandbox when feasible. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill includes destructive plan-cancellation and shipment-lookup behavior that is not clearly disclosed in the top-level purpose statement. Hidden or under-declared destructive functionality is dangerous because an agent may be authorized for routine shipping tasks while unexpectedly being able to cancel active inbound plans and alter business operations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill includes destructive plan-cancellation and shipment-lookup behavior that is not clearly disclosed in the top-level purpose statement. Hidden or under-declared destructive functionality is dangerous because an agent may be authorized for routine shipping tasks while unexpectedly being able to cancel active inbound plans and alter business operations.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
---
name: fba-send-to-amazon
description: Create FBA inbound plans (Send to Amazon) via SP-API v2024-03-20 — identical-box packing for an even multi-warehouse split and $0 placement fee, carrier booking (Amazon SEND, partnered or your own), box-label download and square-sticker formatting. Use when asked to send inventory to FBA, create an inbound plan/shipment, book a freight carrier for an inbound, or print box labels. Triggers, send to amazon, STA, inbound plan, FBA shipment, box label, placement fee, Amazon SEND, partnered carrier, FBA发货, 创建入库计划, 箱唛.
version: 1.0.0
metadata:
  openclaw:
    emoji
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Credential Access

High
Category
Privilege Escalation
Content
bins:
        - python3
      config:
        - ~/.config/sp-api/credentials.json
    envVars:
      - name: LWA_CLIENT_ID
        required: false
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
bins:
        - python3
      config:
        - ~/.config/sp-api/credentials.json
    envVars:
      - name: LWA_CLIENT_ID
        required: false
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
bins:
        - python3
      config:
        - ~/.config/sp-api/credentials.json
    envVars:
      - name: LWA_CLIENT_ID
        required: false
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
bins:
        - python3
      config:
        - ~/.config/sp-api/credentials.json
    envVars:
      - name: LWA_CLIENT_ID
        required: false
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def access_token() -> str:
    """Return a cached LWA access token, refreshing ~1 min before expiry."""
    global _token
    if _token and time.time() < _token[1]:
        return _token[0]
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill declares access to Python, local config files, environment variables, and networked SP-API interactions, but does not constrain those capabilities with explicit tool permissions or allowed-tool scope. In an agent environment, that gap increases the chance of overbroad execution, unintended file/env access, and network actions beyond the user’s expectation.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: fba-send-to-amazon
description: Create FBA inbound plans (Send to Amazon) via SP-API v2024-03-20 — identical-box packing for an even multi-warehouse split and $0 placement fee, carrier booking (Amazon SEND, partnered or your own), box-label download and square-sticker formatting. Use when asked to send inventory to FBA, create an inbound plan/shipment, book a freight carrier for an inbound, or print box labels. Triggers, send to amazon, STA, inbound plan, FBA shipment, box label, placement fee, Amazon SEND, partnered carrier, FBA发货, 创建入库计划, 箱唛.
version: 1.0.0
metadata:
  openclaw:
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger text is broad enough to activate on many common logistics or Amazon-related requests, without clear boundaries about what the skill should and should not do. Overbroad activation increases the chance that the agent invokes a credentialed, network-capable, and partly destructive skill in situations where the user did not intend it.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The documentation instructs the agent to operate a browser session already signed in to Seller Central, which expands the trust boundary from API calls into direct use of an authenticated user session. That creates significant risk of unintended account actions, session abuse, or access to unrelated account data, especially because browser automation often bypasses the tighter controls applied to API credentials.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill advises querying an internal Seller Central endpoint using the user’s browser session credentials, which is outside the declared SP-API workflow and relies on an authenticated session to reach undocumented/internal functionality. Use of internal endpoints is risky because it can expose unstable, unsupported, or over-privileged operations and defeats expected security review based on public API boundaries.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
This code writes a result file containing shipment identifiers, warehouse details, box specifications, and item quantities, but there is no nearby warning or disclosure that operational data will be persisted locally. For a code-file warning check, the write is visible, but the user-facing output only says where it was saved and does not explain the sensitivity or persistence of the data.

External Transmission

Medium
Category
Data Exfiltration
Content
from pathlib import Path

CONFIG_FILE = Path.home() / ".config" / "sp-api" / "credentials.json"
LWA_URL = "https://api.amazon.com/auth/o2/token"

_cfg_cache: dict | None = None
_token: tuple[str, float] | None = None   # (access_token, expires_at_epoch)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.