Back to skill

Security audit

x402 Singularity Layer

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly transparent about its Web3/payment purpose, but it asks agents to sign high-impact blockchain transactions prepared by remote services without enough local validation or confirmation.

Install only after reviewing the specific runbook you need. Use read-only discovery without secrets where possible, prefer scoped API keys or delegated wallets, avoid long-lived high-value PRIVATE_KEY or SOLANA_SECRET_KEY values, keep API/RPC/base URL overrides pointed only at trusted hosts, and inspect any on-chain transaction before signing or staking.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/stake_sgl.py:38
Finding

Unvalidated Staking Transactions Are Signed from a Configurable Remote Host

Content
View full analysis
str: """Sign a base64 unsigned legacy transaction and return base64 signed.""" from solders.transaction import Transaction # type: ignore raw = base64.b64decode(tx_b64) tx = Transaction.from_bytes(raw) tx.sign([keypair], tx.message.recent_blockhash) return base64.b64encode(bytes(tx)).decode() ``` ```python def _post(path: str, body: dict) -> dict: r = requests.post(f"{BASE}{path}", json=body, timeout=TIMEOUT) data = r.json() if not r.ok: msg = data.get("error", data) if isinstance(msg, dict): msg = msg.get("message", msg) sys.exit(f"POST {path} failed ({r.status_code}): {msg}") return data def _prepare_sign_submit(prepare_path: str, body: dict) -> None: """POST a prepare endpoint, sign each returned tx, submit it.""" keypair = _load_keypair() print( f"⚠️ This will SIGN and SUBMIT an on-chain Solana transaction " f"({prepare_path.rsplit('/', 1)[-1]}) from wallet {keypair.pubkey()}. " f"It moves $SGL / changes your stake and cannot be undone. " f"Set SGL_STAKING_URL only to a trusted host.", file=sys.stderr, ) prep = _post(prepare_path, body) txs = prep.get("transactions", []) if not txs: sys.exit(f"No transactions returned: {json.dumps(prep)}") for t in txs: signed = _sign_tx_b64(keypair, t["transaction"]) res = _post("/api/agent/submit", {"transaction": signed}) print(f"✅ {t.get('description', prepare_path)}") print(f" signature: {res.get('signature')}") if res.get("explorer"): print(f" {re ...[truncated 2181 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/register_agent.py:292
Finding

Registry API Controls Signed Solana Transactions and Their RPC Destination

Content
View full analysis
str: solders = _import_solders() VersionedTransaction = solders["VersionedTransaction"] keypair = _load_keypair(solde ...[truncated 2701 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/register_agent.py:118
Finding

Registry API Can Select Arbitrary EVM Contracts and Calldata for Wallet Signing

Content
View full analysis
str: from web3 import Web3 private_key = os.getenv("PRIVATE_KEY") wallet_address = load_wallet_address(required=True, allow_awal_fallback=False) if not private_key or not wallet_address: raise ValueError("Set PRIVATE_KEY and WALLET_ADDRESS for wallet-first EVM registration") rpc_url = EVM_RPC_URLS.get(network) if not rpc_url: raise ValueError(f"Unsupported EVM network for wallet-first registration: {network}") w3 = Web3(Web3.HTTPProvider(rpc_url)) if not w3.is_connected(): raise ValueError(f"Failed to connect to EVM RPC for {network}") checksum_wallet = Web3.to_checksum_address(wallet_address) checksum_contract = Web3.to_checksum_address(contract_address) contract = w3.eth.contract(address=checksum_contract, abi=abi) abi_functions = {f.get("name") for f in abi if isinstance(f, dict) and f.get("type") == "function"} if not function_name.isidentifier() or function_name.startswith("_") or function_name not in abi_functions: raise ValueError(f"Refusing dynamic call to non-ABI contract function: {function_name!r}") contract_fn = getattr(contract.functions, function_name)(*args) nonce = w3.eth.get_transaction_count(checksum_wallet) tx: Dict[str, Any] = { "from": checksum_wallet, "nonce": nonce, "chainId": int(w3.eth.chain_id), } latest_block = w3.eth.get_block("latest") base_fee = latest_block.get("baseFeePerGas") if base_fee is not None: priority_fee = w3.to_wei(1, "gwei") ...[truncated 3250 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:5
Finding

Dependency Installation Uses Unpinned Mutable Versions and Sources

Content
View full analysis
=0.10.0 web3>=6.0.0 # HTTP requests requests>=2.28.0 # Receipt JWT verification (RS256/JWKS) pyjwt[crypto]>=2.8.0 cryptography>=42.0.0 # Solana payments / wallet-first Solana agent registration solders>=0.20.0 ``` Related installation commands: ```bash pip install -r {baseDir}/requirements.txt npx skills add coinbase/agentic-wallet-skills npm install -g @open-wallet-standard/core ``` ### Technical Analysis All Python requirements use open-ended minimum-version constraints. A future version satisfying these constraints can therefore be installed even though it was not part of this audit. The npm and npx commands similarly do not specify immutable versions, package integrity values, or repository commits. This project operates in environments that may contain `PRIVATE_KEY`, `SOLANA_SECRET_KEY`, API keys, PATs, and support tokens. Consequently, dependency compromise has higher impact than in a read-only application. No currently malicious package was identified. The issue is that the documented installation process permits the effective dependency payload to change after review. ### Attack Path 1. A dependency maintainer account, package registry, package release, or transitive dependency is compromised. 2. A malicious version is published while still satisfying the open-ended version constraint. 3. A user follows the documented `pip`, `npm`, or `npx` installation command. 4. The mutable package version is downloaded and installed. 5. Malicious installation or runtime code executes with the user's local privileges. 6. If the environment already contains wallet or platform credentials, that code may access those credentials or alter transactions. ### Impact Assessment Exploitation ca ...[truncated 435 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (131)

Tainted flow: 'wallet' from os.getenv (line 31, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/check_credits.py (reported line 44)May include surrounding context.

python
print(f"Checking credits for: {endpoint_slug}")
    print(f"Wallet: {wallet}")

    response = requests.get(
        url,
        params={"action": "balance", "wallet": wallet},
        headers={"Accept": "application/json"},

Tainted flow: 'wallet' from os.getenv (line 31, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/consume_credits.py (reported line 50)May include surrounding context.

python
print(f"Consuming: {credits_url}")
    
    try:
        response = requests.get(
            credits_url,
            headers={
                "x-wallet-address": wallet,

Tainted flow: 'payment_payload' from os.getenv (line 253, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/consume_product.py (reported line 273)May include surrounding context.

python
# Step 4: Make request with payment
    print("\nStep 3: Submitting payment and requesting download...")
    
    response = requests.get(
        api_url,
        headers={
            "X-Payment": json.dumps(payment_payload),

Tainted flow: 'headers' from os.getenv (line 96, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/list_on_marketplace.py (reported line 80)May include surrounding context.

python
if banner_url:
        print(f"  Banner: {banner_url}")

    response = requests.post(url, json=data, headers=headers)

    if response.status_code in [200, 201]:
        result = response.json()

Tainted flow: 'headers' from os.getenv (line 96, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/list_on_marketplace.py (reported line 104)May include surrounding context.

python
if banner_url:
        print(f"  Banner: {banner_url}")

    response = requests.post(url, json=data, headers=headers)

    if response.status_code in [200, 201]:
        result = response.json()

Tainted flow: 'api_key' from os.getenv (line 30, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/manage_endpoint.py (reported line 45)May include surrounding context.

python
def get_endpoint_info(slug: str, api_key: Optional[str] = None) -> dict:
    response = requests.get(
        f"{API_BASE}/agent/endpoints",
        params={"slug": slug},
        headers=_build_headers(api_key),

Tainted flow: 'endpoint_id' from os.getenv (line 88, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/manage_endpoint.py (reported line 99)May include surrounding context.

python
"details": details,
        }

    response = requests.get(
        f"{API_BASE}/agent/endpoints",
        params={"action": "stats", "endpoint_ids": endpoint_id},
        headers={"Accept": "application/json"},

Tainted flow: 'api_key' from os.getenv (line 30, credential/environment) → requests.patch (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/manage_endpoint.py (reported line 165)May include surrounding context.

python
if not api_key:
        return {"error": "Update requires X_API_KEY or API_KEY"}

    response = requests.patch(
        f"{API_BASE}/agent/endpoints",
        params={"slug": slug},
        headers={**_build_headers(api_key), "Content-Type": "application/json"},

Tainted flow: 'api_key' from os.getenv (line 44, credential/environment) → requests.patch (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/manage_webhook.py (reported line 53)May include surrounding context.

python
def set_webhook(slug: str, webhook_url: str) -> dict:
    """Set or update the webhook URL for an endpoint."""
    api_key = _load_api_key()
    response = requests.patch(
        f"{API_BASE}/agent/endpoints",
        params={"slug": slug},
        json={"webhook_url": webhook_url},

Tainted flow: 'api_key' from os.getenv (line 44, credential/environment) → requests.patch (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/manage_webhook.py (reported line 74)May include surrounding context.

python
def remove_webhook(slug: str) -> dict:
    """Remove the webhook URL from an endpoint."""
    api_key = _load_api_key()
    response = requests.patch(
        f"{API_BASE}/agent/endpoints",
        params={"slug": slug},
        json={"webhook_url": None},

Tainted flow: 'api_key' from os.getenv (line 44, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/manage_webhook.py (reported line 89)May include surrounding context.

python
def get_webhook_info(slug: str) -> dict:
    """Check if an endpoint has a webhook configured."""
    api_key = _load_api_key()
    response = requests.get(
        f"{API_BASE}/agent/endpoints",
        params={"slug": slug},
        headers={"X-API-Key": api_key, "Accept": "application/json"},

Tainted flow: 'BASE' from os.getenv (line 38, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/stake_sgl.py (reported line 87)May include surrounding context.

python
# ─── API helpers ─────────────────────────────────────────────────────────────

def _get(path: str) -> dict:
    r = requests.get(f"{BASE}{path}", timeout=TIMEOUT)
    data = r.json()
    if not r.ok:
        sys.exit(f"GET {path} failed ({r.status_code}): {data.get('error', data)}")

Tainted flow: 'BASE' from os.getenv (line 38, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The script posts wallet-specific action parameters to a server chosen by SGL_STAKING_URL and then later signs transactions returned by that same service. In this staking context, a malicious or misconfigured endpoint could prepare deceptive unsigned transactions for local signing, turning endpoint override into a high-risk transaction-confusion vector even though the private key is not directly transmitted.

Content

Scanner excerpt · scripts/stake_sgl.py (reported line 95)May include surrounding context.

python
def _post(path: str, body: dict) -> dict:
    r = requests.post(f"{BASE}{path}", json=body, timeout=TIMEOUT)
    data = r.json()
    if not r.ok:
        msg = data.get("error", data)

Tainted flow: 'url' from os.getenv (line 44, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The request destination is derived from the X402_API_BASE environment variable and is used to send an authenticated POST containing the X-API-Key header. If an attacker can influence the process environment or deployment configuration, they can redirect this request to an attacker-controlled host and exfiltrate the worker API key, making this an SSRF/credential-leakage issue rather than a harmless configurability feature. In this skill context, the script handles privileged worker credentials and performs on-chain writes, which increases the sensitivity of outbound requests.

Content

Scanner excerpt · scripts/submit_feedback.py (reported line 59)May include surrounding context.

python
else:
        body["agentId"] = agent_id

    response = requests.post(
        url,
        json=body,
        headers={

Tainted flow: 'STUDIO_BASE' from os.getenv (line 25, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/support_threads.py (reported line 68)May include surrounding context.

python
def _resolve_listing(listing_type: str, listing_ref: str) -> Dict[str, str]:
    if listing_type == "endpoint" and not listing_ref.startswith("http") and len(listing_ref) < 64:
        response = requests.get(f"{STUDIO_BASE}/api/public/endpoints/{listing_ref}", timeout=30)
        if response.status_code == 200:
            data = response.json()
            return {"listing_id": data["id"], "listing_type": "endpoint", "name": data.get("name") or listing_ref}

Tainted flow: 'STUDIO_BASE' from os.getenv (line 25, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/support_threads.py (reported line 76)May include surrounding context.

python
if listing_ref.startswith("http"):
        slug = listing_ref.rstrip("/").split("/")[-1]
        if listing_type == "endpoint":
            response = requests.get(f"{STUDIO_BASE}/api/public/endpoints/{slug}", timeout=30)
            if response.status_code == 200:
                data = response.json()
                return {"listing_id": data["id"], "listing_type": "endpoint", "name": data.get("name") or slug}

Tainted flow: 'API_BASE' from os.getenv (line 24, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/support_threads.py (reported line 82)May include surrounding context.

python
return {"listing_id": data["id"], "listing_type": "endpoint", "name": data.get("name") or slug}

    # fallback to marketplace search by id/slug
    response = requests.get(f"{API_BASE}/api/marketplace", params={"search": listing_ref, "limit": 50}, timeout=30)
    response.raise_for_status()
    data = response.json()
    listings = data.get("listings", [])

Tainted flow: 'api_key' from os.getenv (line 36, credential/environment) → requests.put (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/topup_endpoint.py (reported line 68)May include surrounding context.

python
headers=headers,
        )

    response = requests.put(
        url,
        params=params,
        json=data,

Tainted flow: 'api_key' from os.getenv (line 36, credential/environment) → requests.put (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/topup_endpoint.py (reported line 100)May include surrounding context.

python
except Exception as exc:
        return {"error": f"Failed to build {selected_network} payment: {exc}"}

    response = requests.put(
        url,
        params=params,
        json=data,

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This is a material description-to-behavior mismatch. The declared description presents a wide-ranging x402-layer skill centered on monetized API endpoints, USDC API payments, credits, webhooks, marketplace listings, ERC-8004 agent registration/discovery/reputation, wallet integrations, and staking. In contrast, the supplied code only manages fundraiser campaigns through an owner-scoped API endpoint. Its concrete actions are limited to listing owned campaigns, retrieving campaign details, creating a campaign, and updating campaign metadata. Those fundraiser/campaign capabilities are not mentioned in the declaration, and the code does not implement the core advertised functions such as paying for APIs, deploying endpoints, handling webhooks, marketplace browsing/listing, ERC-8004 operations, or staking. While the use of PAT/API key auth is broadly consistent with optional credentialed management flows mentioned in the description, that overlap is incidental and does not resolve the primary-purpose mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This code does interact with x402 resources and one phrase in the description mentions 'open support chat', so the behavior is not wholly unrelated. However, the declared purpose presents the skill primarily as a comprehensive payments, endpoint, marketplace, wallet, and staking toolkit. The actual code chunk only manages support threads in Studio: resolve listings, check support eligibility, open/reuse a thread, list/show threads, and update thread status. That is a materially narrower and different primary purpose than the declared description. Because the code's concrete functionality is support-thread operations rather than the major declared capabilities, this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/mcp-control-plane.md (reported line 47)May include surrounding context.

PAT access is optional.

If the user provides a dashboard MCP personal access token, it should look like:

text
sgl_pat_...

Lp1

High
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The script reads the AWAL_BIN environment variable to choose which executable to run, but the finding indicates this environment capability is not declared in permissions. In an agent skill that may later handle wallet operations and privileged credentials, undeclared environment access weakens auditability and can enable execution of an attacker-controlled binary if the environment is manipulated.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/solana_signing.py (reported line 328)May include surrounding context.

python
AccountMeta(dest, False, True),
            AccountMeta(owner, True, False),
        ]
        return Instruction(token_program_id, data, keys)

    blockhash = _get_recent_blockhash(Hash)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script exposes a revoke-others command that calls client.revokeAllOtherInstallations(), which can invalidate all other XMTP installations for the wallet. That is a high-impact account-management action unrelated to normal support-chat messaging, and if triggered by an agent or user misunderstanding it could disrupt account access across devices and sessions.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/xmtp_support.mjs:11