Back to skill

Security audit

Amazon SEND CATCH Invoice

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent invoice-generation purpose, but it handles portal credentials, cached tokens, arbitrary portal URLs, and order-placing uploads without enough transport and destination safeguards.

Install only if you trust the publisher and can run it in a constrained project environment. Use a verified HTTPS CATCH portal URL, avoid exposing broad ERP or database credentials to the agent, review generated invoices before upload, and treat upload as an order-placement action requiring explicit approval.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/catch_api.py:59
Finding

Credentials, Authentication Tokens, and Customs Invoices May Be Transmitted over Plaintext HTTP

Content
View full analysis
str: if not BASE: sys.exit("set $CATCH_BASE_URL to the portal origin, e.g. https://portal.example.com") return BASE ``` ```python def login() -> str: user, password = os.environ.get("CATCH_USER"), os.environ.get("CATCH_PASS") if not (user and password): sys.exit("set $CATCH_USER and $CATCH_PASS") r = _post("/v1/user/login", {"username": user, "password": password}, auth=False) ``` ```python def _req(method, path, data=None, headers=None, auth=True): h = dict(COMMON) if auth: token = load_token() if not token: sys.exit("no token — run `python3 catch_api.py login` or set $CATCH_TOKEN") h["x-token"] = token h.update(headers or {}) req = urllib.request.Request(_base() + path, data=data, headers=h, method=method) try: with urllib.request.urlopen(req, timeout=120) as resp: return json.loads(resp.read()) ``` The related documentation explicitly describes upload “over pure HTTP” and allows a configurable portal origin: ```markdown export CATCH_BASE_URL=https://portal.example.com ``` ### Technical Analysis Uploading customs invoices and authenticating to the forwarder portal are necessary for the declared functionality. However, the implementation accepts any value in `CATCH_BASE_URL` and does not verify that its scheme is HTTPS. If the variable is accidentally or maliciously configured with an `http://` URL, the following information is transmitted without encryption: - Portal username and password during `/v1/user/login` - The reusable `x-token` authentication token - Complete customs invoice files - FBA shipment and box identifiers - Amazon r ...[truncated 1567 chars]
Remediation
View remediation
str: if not BASE: sys.exit("set $CATCH_BASE_URL") parsed = urlsplit(BASE) if parsed.scheme != "https" or not parsed.hostname: sys.exit("CATCH_BASE_URL must be a valid HTTPS origin") if parsed.username or parsed.password or parsed.query or parsed.fragment: sys.exit("CATCH_BASE_URL must contain only a trusted HTTPS origin") return f"https://{parsed.netloc}" ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetch_images.py:77
Finding

Arbitrary Image URLs Enable Server-Side Request Forgery and Unbounded Response Downloads

Content
View full analysis
Path | None: from PIL import Image out = cache_dir() / f"{key}.bmp" if out.exists() and not force: return out try: if url.startswith(("http://", "https://")): req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) with urllib.request.urlopen(req, timeout=15) as r: data = r.read() img = Image.open(io.BytesIO(data)) else: img = Image.open(Path(url).expanduser()) # local file path ``` ### Technical Analysis The image-map input completely controls the URL passed to `urllib.request.urlopen`. The implementation: - Allows both HTTP and HTTPS. - Does not restrict destination hostnames. - Does not reject loopback, link-local, private, multicast, or reserved IP addresses. - Follows HTTP redirects without validating the redirected destination. - Reads the entire response into memory with `r.read()` and imposes no byte limit. - Does not validate the response `Content-Type` before downloading it. - Passes downloaded content to Pillow, increasing exposure to malformed-image parser vulnerabilities and decompression bombs. This network capability is broader than required. The declared function only needs product images from trusted catalog/CDN locations or explicit local files. ### Attack Path 1. An attacker supplies or modifies `images.json`. 2. The attacker sets an image URL to an internal service, for example: - `http://127.0.0.1:PORT/...` - `http://169.254.169.254/...` - A private network address such as `http://10.0.0.5/...` - A public URL that redirects to one of those destinations 3. The user or agent runs `fetch_images.py`. 4. The process issues a request ...[truncated 929 chars]
Remediation
View remediation
MAX_IMAGE_BYTES: raise ValueError("image exceeds maximum allowed size") ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_images.py:77
Finding

Unsanitized Image Keys Permit Filesystem Path Traversal and Arbitrary BMP Writes

Content
View full analysis
Path | None: from PIL import Image out = cache_dir() / f"{key}.bmp" if out.exists() and not force: return out try: if url.startswith(("http://", "https://")): req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) with urllib.request.urlopen(req, timeout=15) as r: data = r.read() img = Image.open(io.BytesIO(data)) else: img = Image.open(Path(url).expanduser()) # local file path fit_square(img.convert("RGB"), size).save(out, "BMP") patch_bmp_dpi(out) ``` ```python mapping = json.loads(Path(args.images).read_text(encoding="utf-8")) ok = 0 for key, url in mapping.items(): p = fetch(key, url, args.size, args.force) ``` ### Technical Analysis The JSON object key is inserted directly into a path: ```python cache_dir() / f"{key}.bmp" ``` No validation rejects path separators, `..` components, absolute paths, drive prefixes, or platform-specific special names. `pathlib` does not guarantee that this construction remains below `cache_dir()` when the right-hand value is absolute or contains traversal components. With `--force`, an existing destination can be overwritten. The written content is constrained to a BMP generated by Pillow, but an attacker can still place or replace a `.bmp` file at an unintended writable location. Parent directories must already exist. ### Attack Path 1. An attacker supplies an image mapping with a malicious key containing traversal components or an absolute path. 2. The mapping points to any valid image the script can decode. 3. The user invokes `fetch_images.py`, optionally with `--force`. ...[truncated 694 chars]
Remediation
View remediation
Path: if not isinstance(key, str) or not key: raise ValueError("invalid product key") digest = hashlib.sha256(key.encode("utf-8")).hexdigest() root = cache_dir().resolve() destination = (root / f"{digest}.bmp").resolve() if root not in destination.parents: raise ValueError("cache path escaped cache directory") return destination ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/build_shipments.py:56
Finding

Shipment Identifiers Permit Output Path Traversal in Shipment File Generation

Content
View full analysis
Remediation
View remediation
str: if not IDENTIFIER.fullmatch(value): raise ValueError(f"invalid {field}") return value fba = safe_identifier(s["fba_shipment_id"], "fba_shipment_id") fc = safe_identifier(s["warehouse_code"], "warehouse_code") root = out_dir.resolve() path = (root / f"{fba}_{fc}.json").resolve() if path.parent != root: raise ValueError("output path escaped shipment directory") ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_invoice.py:261
Finding

Shipment Labels and Product Keys Can Escape Intended Invoice and Image Paths

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:58
Finding

Third-Party Python Dependencies Are Installed without Version Pinning or Integrity Verification

Content
View full analysis
Remediation
View remediation
--hash=sha256: xlwt== --hash=sha256: xlutils== --hash=sha256: Pillow== --hash=sha256: ``` ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The claimed behavior includes direct API retrieval and HTTP upload to a forwarder portal, but the finding says the implementation only reads local JSON and uses cached local files. Such discrepancies can bypass user expectations and security approvals, especially when a skill appears operationally capable of placing orders but may actually do something else or fail silently.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The claimed behavior includes direct API retrieval and HTTP upload to a forwarder portal, but the finding says the implementation only reads local JSON and uses cached local files. Such discrepancies can bypass user expectations and security approvals, especially when a skill appears operationally capable of placing orders but may actually do something else or fail silently.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The claimed behavior includes direct API retrieval and HTTP upload to a forwarder portal, but the finding says the implementation only reads local JSON and uses cached local files. Such discrepancies can bypass user expectations and security approvals, especially when a skill appears operationally capable of placing orders but may actually do something else or fail silently.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The claimed behavior includes direct API retrieval and HTTP upload to a forwarder portal, but the finding says the implementation only reads local JSON and uses cached local files. Such discrepancies can bypass user expectations and security approvals, especially when a skill appears operationally capable of placing orders but may actually do something else or fail silently.

Content

No source excerpt is available for this finding.

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · SKILL.md (reported line 223)May include surrounding context.

md
## 5. When the forwarder changes the template

They do, without warning, and a shifted column produces an invoice that looks
correct and is either rejected or — worse — accepted with values in the wrong
fields.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares access to environment variables, filesystem, shell, and network-capable behavior but does not define any explicit tool scope or permission boundaries. In an agent setting, this creates unnecessary ambient authority and increases the chance that the skill could read secrets, modify files, or make outbound requests beyond what a user expects.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger list is very broad and includes generic phrases like customs invoice, declaration, and upload invoice, which increases the chance of accidental activation in contexts the user did not intend. Overbroad activation is risky for a skill that may handle credentials, local files, and outbound requests because it can be invoked on ambiguous prompts with sensitive consequences.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The documentation explicitly tells the skill operator to request endpoint details, database connection information, and credentials, then connect to the user's ERP, API, database, or spreadsheet source. That expands the skill from invoice generation/upload into broad data-access and credential-handling behavior, which increases the attack surface and creates a path for unnecessary access to sensitive internal systems if the skill is over-permissioned or misused.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/catch_api.py (reported line 96)May include surrounding context.

python
user, password = os.environ.get("CATCH_USER"), os.environ.get("CATCH_PASS")
    if not (user and password):
        sys.exit("set $CATCH_USER and $CATCH_PASS")
    r = _post("/v1/user/login", {"username": user, "password": password}, auth=False)
    token = (r.get("data") or {}).get("token")
    if not token:
        sys.exit(

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file contains fixed Chinese-language strings for labels and an Excel sheet name, but provides no indication that the skill is intentionally region-specific or that users can choose another language/locale. Per the policy, forcing a specific language without opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.