Back to skill

Security audit

Esri Workflow Smell Detector (Consumer)

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed paid API client, but its helper can sign a USDC payment for server-selected amount and recipient without enforcing the advertised price or asking for confirmation.

Review before installing. Use only a dedicated wallet with very limited USDC, verify the endpoint and payment recipient, sanitize the project snapshot before upload, and avoid running the helper until it enforces a strict maximum payment and confirms terms before signing.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/call_smells.py:43
Finding

Unbounded Server-Controlled USDC Payment Authorization

Content
View full analysis
str: opt = pick_base_option(challenge) pay_to = opt["payTo"] amount = int(opt["maxAmountRequired"]) # USDC has 6 decimals nonce = "0x" + secrets.token_hex(32) valid_after = 0 valid_before = int(time.time()) + 3600 typed_data = { "types": { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"}, ], "TransferWithAuthorization": [ {"name": "from", "type": "address"}, {"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}, {"name": "validAfter", "type": "uint256"}, {"name": "validBefore", "type": "uint256"}, {"name": "nonce", "type": "bytes32"}, ], }, "primaryType": "TransferWithAuthorization", "domain": { "name": USDC_NAME, "version": USDC_VERSION, "chainId": BASE_CHAIN_ID, "verifyingContract": USDC_ADDRESS, }, "message": { "from": wallet, "to": pay_to, "value": amount, "validAfter": valid_after, "validBefore": valid_before, "nonce": nonce, }, } account = Account.from_key(private_key) signed = account.sign_typed_data( typed_data["domain"], {"TransferWithAuthorization": typed_data["types"]["TransferWithAuthorization"]}, typed_data["message"], ) payload = { "x402Version": 1, "schem ...[truncated 3072 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Tainted flow: 'body' from open (line 127, file read) → requests.post (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/call_smells.py (reported line 133)May include surrounding context.

python
}

    # Step 1: get 402 challenge
    r1 = requests.post(args.endpoint, json=body, headers={"Accept": "application/json"}, timeout=30)
    if r1.status_code == 200:
        print(json.dumps(r1.json(), indent=2))
        return 0

Tainted flow: 'body' from open (line 127, file read) → requests.post (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/call_smells.py (reported line 145)May include surrounding context.

python
# Step 2: pay and retry
    x_payment = make_x_payment(challenge, wallet, private_key)
    r2 = requests.post(args.endpoint, json=body, headers={"X-Payment": x_payment, "Accept": "application/json"}, timeout=30)
    print(f"Response: {r2.status_code}")
    ct = (r2.headers.get("content-type") or "").lower()
    if "application/json" in ct:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill describes capabilities that involve reading files, using environment variables, and making outbound network requests, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this weakens least-privilege controls and makes it easier for the skill to be invoked with broader access than users may expect, especially given that it handles payment credentials and transmits project snapshots externally.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
83% confidence
Finding

The skill sends a project snapshot to an external paid endpoint, which is a genuine data egress path. Even if the snapshot is described as safe-by-default, it may still contain metadata, structure, identifiers, or other sensitive operational context, and the presence of a payment flow increases the trust and integrity requirements for the remote service.

Content

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

md
description: |
  Paid client skill for Esri Workflow Smell Detector via x402 (Base/USDC).
  Use when you want to run a deterministic automation preflight scan on an ArcGIS Pro project snapshot
  by calling https://api.x402layer.cc/e/esri-smells (HTTP 402 payment flow).
---

# Esri Workflow Smell Detector (Consumer Skill)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs users to export PRIVATE_KEY and WALLET_ADDRESS directly into environment variables without any warning about the sensitivity of the private key or safer handling practices. In a multi-tool agent environment, environment variables may be exposed to logs, subprocesses, crash reports, or other skills, creating a real risk of wallet compromise and unauthorized payments.

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 · SKILL.md (reported line 30)May include surrounding context.

md
"""Paid client for Esri Workflow Smell Detector via x402 (Base).

Usage:
  python call_smells.py <project_snapshot.json> [--endpoint https://api.x402layer.cc/e/esri-smells]

Env:
  PRIVATE_KEY   EVM private key (Base)

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/call_smells.py (reported line 5)May include surrounding context.

python
"""Paid client for Esri Workflow Smell Detector via x402 (Base).

Usage:
  python call_smells.py <project_snapshot.json> [--endpoint https://api.x402layer.cc/e/esri-smells]

Env:
  PRIVATE_KEY   EVM private key (Base)

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/call_smells.py (reported line 121)May include surrounding context.

python
"""Paid client for Esri Workflow Smell Detector via x402 (Base).

Usage:
  python call_smells.py <project_snapshot.json> [--endpoint https://api.x402layer.cc/e/esri-smells]

Env:
  PRIVATE_KEY   EVM private key (Base)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script uploads a project snapshot to a third-party endpoint without any runtime disclosure or confirmation to the operator. Project snapshots can contain sensitive operational, geospatial, infrastructure, or customer data, so silent transmission increases the chance of unintentional data leakage, especially because the endpoint is configurable.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/call_smells.py (reported line 133)May include surrounding context.

python
}

    # Step 1: get 402 challenge
    r1 = requests.post(args.endpoint, json=body, headers={"Accept": "application/json"}, timeout=30)
    if r1.status_code == 200:
        print(json.dumps(r1.json(), indent=2))
        return 0

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/call_smells.py (reported line 145)May include surrounding context.

python
}

    # Step 1: get 402 challenge
    r1 = requests.post(args.endpoint, json=body, headers={"Accept": "application/json"}, timeout=30)
    if r1.status_code == 200:
        print(json.dumps(r1.json(), indent=2))
        return 0

Tainted flow: 'x_payment' from requests.post (line 144, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/call_smells.py (reported line 145)May include surrounding context.

python
# Step 2: pay and retry
    x_payment = make_x_payment(challenge, wallet, private_key)
    r2 = requests.post(args.endpoint, json=body, headers={"X-Payment": x_payment, "Accept": "application/json"}, timeout=30)
    print(f"Response: {r2.status_code}")
    ct = (r2.headers.get("content-type") or "").lower()
    if "application/json" in ct:

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specifier requests>=2.28.0 is unpinned, which permits installation of many different future versions and can lead to non-reproducible builds or accidental inclusion of a vulnerable release through resolver behavior or transitive constraints. In a paid client skill that performs network calls and payment-related workflow logic, dependency drift increases supply-chain risk and makes security posture harder to verify.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.28.0
eth-account>=0.10.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding

requests has multiple published advisories, and because the manifest does not pin a specific version, there is no way to verify from this file whether the deployed environment is using a patched release. In this skill's context, requests is likely used for outbound API calls to the x402 payment/service endpoint, so an affected version could expose network-facing weaknesses such as credential leakage or TLS/request handling issues depending on the resolved release.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specifier eth-account>=0.10.0 is unpinned, so installations may resolve to different versions over time, reducing reproducibility and making it difficult to verify whether known security fixes are present. Because eth-account is involved in blockchain/payment flows, version uncertainty is more sensitive than in a purely local utility due to the potential trust and signing implications around financial operations.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.28.0
eth-account>=0.10.0

Unverifiable Dependency: eth-account has 2 known advisory(ies) (CVE-2022-1930 (Regular expression denial of service in eth-account); CVE-2022-1930 (Regular expression denial of service in eth-account)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding

eth-account has known advisories, and without an exact version pin the manifest cannot demonstrate that a safe release is installed. Given that this skill is described as using x402/Base/USDC payment flow, uncertainty around a crypto-account library is security-relevant because vulnerable parsing or signing-related code in the selected version could affect availability or trust in payment operations.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.