Back to skill

Security audit

Agent Pay Client

Security checks for vulnerabilities and agentic risk

Overview

This payment skill is upfront about using user-supplied payment credentials, but its Lightning auto-pay path has safety flaws that could expose credentials or spend beyond the stated limit.

Review before installing for real funds. Keep L402 auto-pay disabled unless the ceiling validation is fixed to decode and verify the BOLT11 invoice amount, require verified HTTPS or a pinned LND certificate, use a narrowly scoped macaroon, keep only small balances available, and pin/audit dependencies. The x402/L402 purpose is legitimate, but this version should be treated as reference-grade rather than production-safe payment infrastructure.

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

Error
Location
agent_pay_client/payer.py:160
Finding

L402 Automatic Payment Can Bypass the Configured Spending Ceiling

Content
View full analysis
config.max_l402_sats): return PaymentResult( success=False, protocol=PaymentProtocol.L402, error=f"Invoice amount {req.amount_sats} sats exceeds configured max_l402_sats " f"({config.max_l402_sats}) — refusing to pay.", ) ``` ### Technical Analysis The documented safety model states that every automatic Lightning payment is subject to an explicit `max_l402_sats` ceiling. However, `amount_sats` is populated only from an optional JSON response field. It is not derived from or validated against the BOLT11 invoice that the LND node will actually pay. When `amount_sats` is absent, `req.amount_sats` remains `None`. The expression: ```python req.amount_sats and req.amount_sats > config.max_l402_sats ``` then evaluates to a false value, allowing execution to continue to the LND payment request. Consequently, the code treats an unknown amount as acceptable rather than failing closed. Even when the JSON field is present, a malicious server could potentially report an amount lower than the amount encoded in the invoice. The companion JSON field is server-controlled and is not an authoritative source for enforcing a financial limit. ### Attack Path 1. A caller deliberately enables `lightnin ...[truncated 1178 chars]
Remediation
View remediation
config.max_l402_sats: return PaymentResult( success=False, protocol=PaymentProtocol.L402, error="Invoice exceeds the configured spending ceiling.", ) ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
agent_pay_client/payer.py:171
Finding

LND Macaroon Is Transmitted Without TLS Certificate Verification

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · README.md (reported line 28)May include surrounding context.

md
# Look before you pay
client = AgentPayClient()
print(client.get_payment_options("https://api.example.com/paid"))

# Pay for real, with an explicit ceiling
config = PayerConfig(evm_private_key="0x...", max_x402_atomic="50000")

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · README.md (reported line 32)May include surrounding context.

md
# Look before you pay
client = AgentPayClient()
print(client.get_payment_options("https://api.example.com/paid"))

# Pay for real, with an explicit ceiling
config = PayerConfig(evm_private_key="0x...", max_x402_atomic="50000")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill performs network-capable actions against arbitrary URLs and can initiate financial/payment flows, but the manifest declares no explicit tool scope such as permissions or allowed-tools. In an agent environment, missing scope boundaries increases the chance the skill is invoked with broader network access than intended, enabling SSRF-like access to internal endpoints or unintended paid requests if a caller passes hostile URLs and credentials.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module-level safety claims state that spending ceilings are enforced before any signing or network call, but in the L402 path the code only rejects over-limit payments when req.amount_sats is present. If the amount is missing, the code proceeds to call the LND payment endpoint anyway, allowing a real payment attempt without a verified ceiling check. In a payment client, that mismatch is security-relevant because callers may rely on the documented invariant to prevent unintended spending.

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 · agent_pay_client/payer.py (reported line 171)May include surrounding context.

python
return PaymentResult(success=False, protocol=PaymentProtocol.L402, error="requests not installed")

    try:
        resp = requests.post(
            f"{config.lnd_rest_url}/v1/channels/transactions",
            headers={"Grpc-Metadata-macaroon": config.lnd_macaroon_hex},
            json={"payment_request": req.invoice},

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
98% confidence
Finding

Disabling TLS certificate verification for the LND REST payment request enables man-in-the-middle interception or impersonation of the LND endpoint. Because the request carries the admin macaroon header and submits live payment requests, an attacker on the network could steal credentials, induce unauthorized payments, or return forged responses such as fake preimages. In a payment-handling skill, this materially increases the risk of fund loss and secret exposure.

Content

Scanner excerpt · agent_pay_client/payer.py (reported line 175)May include surrounding context.

python
f"{config.lnd_rest_url}/v1/channels/transactions",
            headers={"Grpc-Metadata-macaroon": config.lnd_macaroon_hex},
            json={"payment_request": req.invoice},
            verify=False, timeout=30,  # LND REST commonly self-signed; caller's node, caller's risk
        )
        data = resp.json()
        if data.get("payment_error"):

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency is specified with only a lower bound, so builds may resolve to different versions over time and can unintentionally pull in vulnerable or breaking releases. In a payment client that handles HTTP authentication/payment flows, dependency drift in a core HTTP library increases supply-chain and security risk because behavior around redirects, credential handling, and TLS can materially affect safety.

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
91% confidence
Finding

requests has multiple known advisories, and because the manifest does not pin a specific version, there is no way to verify whether deployments will install a safe release. This is more concerning in a client that talks to arbitrary x402/L402 servers, since malicious or crafted URLs/HTTP interactions could exercise known weaknesses in the HTTP stack, including credential leakage or verification issues depending on the resolved version.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

eth-account is also unpinned, allowing future installs to resolve unpredictably and potentially include vulnerable releases or incompatible cryptographic behavior. Because this skill signs or settles payments with explicitly provided credentials, dependency integrity is especially important: changes in wallet/account-handling code can directly affect transaction safety and key usage.

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
89% confidence
Finding

eth-account has known advisories, and the lack of version pinning makes the installed package unverifiable from the manifest alone. In a payment/signing client, even a denial-of-service issue in account parsing or signing-related code can disrupt payment operations or be triggered by attacker-controlled inputs, making this more than a purely theoretical supply-chain concern.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.insecure_tls_verification

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
agent_pay_client/payer.py:175