Back to skill

Security audit

Blink

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed wallet/payment skill, but it needs Review because it combines real spend authority with unpinned seed-handling dependencies and network/file hardening gaps.

Install only if you are comfortable giving this skill wallet access. Use read/receive-only Blink keys unless sending is required, prefer staging first, set tight L402 budgets and allowlists, avoid --include-preimage unless exporting proof-of-payment secrets intentionally, and pin/review Spark dependencies before using SPARK_MNEMONIC with self-custodial funds.

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

Warning
Location
scripts/_lnurl.js:49
Finding

Hostname-based SSRF protection does not validate resolved IP addresses

Content
View full analysis
= 16 && b <= 31) return true; if (a === 192 && b === 168) return true; if (a === 169 && b === 254) return true; if (a === 100 && b >= 64 && b <= 127) return true; return false; } if (host === '::' || host === '::1') return true; if (/^f[cd][0-9a-f]{2}:/.test(host)) return true; if (/^fe[89ab][0-9a-f]:/.test(host)) return true; const mappedHex = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/); if (mappedHex) { const hi = parseInt(mappedHex[1], 16); const lo = parseInt(mappedHex[2], 16); return isPrivateAddress([hi >> 8, hi & 0xff, lo >> 8, lo & 0xff].join('.')); } if (/^::ffff:/.test(host)) return isPrivateAddress(host.replace(/^::ffff:/, '')); return false; } ``` ```js if (isPrivateAddress(host) && !explicitlyAllowed) { throw policyError(`Refusing to fetch ${what} from private address '${host}'.`); } if (opts.strictLocal && LOCAL_HOSTS.has(host) && !explicitlyAllowed) { throw policyError(`Refusing to fetch ${what} from local host '${host}' (not allowlisted).`); } ``` ```js let current = assertAllowedUrl(url, allowedHosts, what, { portsUnrestricted, strictLocal, }); res = await fetch(current.toString(), { method: reqMethod, body: reqBody, signal: controller.signal, redirect: 'manual', headers: { ...[truncated 3184 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/qr_invoice.js:773
Finding

Predictable shared temporary filename permits symlink-based file overwrite

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_l402_macaroon.js:60
Finding

L402 root-key persistence bypasses hardened secret-file protections

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/_spark_sdk.js:62
Finding

Security-sensitive optional dependencies are installed without version pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (80)

YARA rule 'ransomware_behavior': Ransomware-like patterns (mass encryption, ransom notes) [malware]

Critical
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

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

md
lanceUsdFormatted`, `usdBalanceFormatted`).
- Do not perform manual BTC-to-USD conversion math — use `blink price <sats>` or the `btcBalanceUsd` field from `balance` output instead.
- For swap amounts, the `--unit` flag controls interpretation: `sats` for BTC, `cents` for USD.

## Workflow

1. Pick the operation path first:

- Receive payments (invoice creation, QR codes, payment monitoring).
- Send payments (invoice pay, Lightning Address, LNURL, BTC or USD wallet).
- Swap between wallets (BTC <-> USD internal conversion).
- Read-only queries (balance, transactions, price, account info).
- Non-custodial (Spark) account operations (receive via LNURL, seed-gated balance/send/history/events).

2. Configure API access from [blink-api-and-auth](references/blink-api-and-auth.md):

- Set `BLINK_API_KEY` with the correct scopes for your operation.
- Optionally set `BLINK_API_URL` for staging/testnet.
- Verify connectivity with `blink balance`.

3. For sending payments, follow [payment-opera

YARA rule 'ransomware_behavior': Ransomware-like patterns (mass encryption, ransom notes) [malware]

Critical
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/non-custodial.md (reported line 138)May include surrounding context.

md
(fees) then `sdk.lnurlPay` (signs)
  - BOLT-11 invoice / Spark address → `sdk.prepareSendPayment` (fees) then `sdk.sendPayment` (signs)
  - A Lightning Address (e.g. `alice@blink.sv`) is an LNURL-pay destination and
    MUST use the LNURL path — `prepareSendPayment` does not accept it.
  - The classification is an **exhaustive allowlist**: the SDK's `parse()`
    recognizes more types than we pay to (on-chain Bitcoin addresses, BOLT-12
    offers, cross-chain destinations, receive-side methods like
    `sparkInvoice`). Everything outside the four supported types is rejected
    with `UNSUPPORTED_DESTINATION` instead of falling into a payment path it
    was never validated for. Widening the list is a deliberate decision, not
    a default.
- `spark-transactions` → `sdk.listPayments`
- `spark-subscribe` → `sdk.addEventListener`

`spark-send` exits **non-zero** when the SDK reports a `failed` payment status,
so automation cannot read a failed payment as success. `pending` exits z

YARA rule 'ransomware_behavior': Ransomware-like patterns (mass encryption, ransom notes) [malware]

Critical
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/payment-operations.md (reported line 106)May include surrounding context.

llet) source ~/.profile node balance.js node fee_probe.js lnbc5000n1... node pay_invoice.js lnbc5000n1... node transactions.js --first 1

text

```bash
# Full workflow example (USD wallet)
source ~/.profile
node balance.js
node fee_probe.js lnbc5000n1... --wallet USD
node pay_invoice.js lnbc5000n1... --wallet USD
node transactions.js --first 1 --wallet USD

Lightning Address Sends

bash
# Send 1000 sats to a Lightning Address (BTC wallet)
node pay_lnaddress.js user@blink.sv 1000

# Send 1000 sats to a Lightning Address (USD wallet)
# Amount is in sats; Blink debits USD equivalent
node pay_lnaddress.js user@blink.sv 1000 --wallet USD

Lightning Address sends do not require a fee probe — the Blink API resolves the address, fetches an invoice, and pays it in one step.

LNURL Sends

bash
# Send 1000 sats via LNURL (BTC wallet)
node pay_lnurl.js lnurl1... 1000

# Send 1000 sats via LNURL (USD wallet)
node pay_lnurl.js lnurl1... 1000 --wallet USD

For Lightning Addres

YARA rule 'ransomware_behavior': Ransomware-like patterns (mass encryption, ransom notes) [malware]

Critical
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · scripts/spark_send.js (reported line 108)May include surrounding context.

js
N(amountSats) || amountSats <= 0) throw new Error('amount_sats must be a positive integer');
      }
    }
  }
  return { destination, amountSats, dryRun, force, network, token, baseUnits, fromBtc, fromToken, slippageBps };
}

/**
 * Classify an SDK parse() result into one of the routing targets this skill
 * supports. EXHAUSTIVE by design: the SDK recognizes more destination types
 * than we can pay to (on-chain Bitcoin addresses, BOLT-12 offers, cross-chain
 * addresses, URLs, ...), and a boolean LNURL-vs-everything-else check would
 * let any future SDK input type silently fall into the generic
 * prepareSendPayment path without ever having been validated for it. Only the
 * four types below are allowed; everything else is rejected by name.
 *
 * @param {object} parsed  Result of sdk.parse(destination).
 * @returns {{ type: 'lnurl'|'bolt11'|'spark', parsed: object }}
 * @throws {Error} code UNSUPPORTED_DESTINATION for any unrecognized type.
 */
function classifyDestination(parsed) {

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a broad Lightning wallet/API integration skill, with wallet operations and an L402 auto-pay client via Blink. The supplied code chunk instead is a low-level cryptographic helper for creating and verifying L402 macaroons. Its primary function is security token generation/validation, not wallet management or API interaction. While L402 is mentioned in the description, this module is specifically producer-side macaroon handling and root key persistence, which is materially different from a client wallet feature set. It also reads from environment variables and writes a persistent root key file under ~/.blink, behavior not suggested by the declared purpose. Therefore the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description presents a Lightning wallet/API client skill with financial operations such as balances, invoices, payments, swaps, QR codes, and price conversion. The supplied code does not implement any wallet, payment, API, or JSON-output functionality. Instead, it provides low-level security utilities for local state storage, including directory/file permission enforcement and secure writes. While such code could be a supporting internal component of a wallet, this chunk’s actual behavior is materially different from the declared primary purpose and introduces undeclared filesystem-management capabilities.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a broad Lightning wallet skill, but this code chunk is narrowly focused on budget administration. Its primary purpose is configuring and inspecting spending controls and an L402 allowlist through a CLI, backed by local config/log files. While L402 is mentioned in the description, this code only manages allowed domains and does not perform auto-pay client behavior. Therefore the code's actual behavior is materially different from the declared wallet functionality and includes distinct budget-management capabilities not described.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a broad Bitcoin Lightning wallet skill centered on Blink API wallet operations and related account/payment features. This code chunk instead implements a specific utility for fetching L402 service information from l402.directory and optionally purchasing a health report through an L402 payment flow. While L402 auto-pay is mentioned in the declared purpose, the primary behavior here is service-directory lookup/report retrieval, not wallet management. That makes the actual behavior materially narrower and different in purpose and external resource usage than the declared description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description presents a full-featured Lightning wallet skill centered on the Blink API, while the supplied code chunk is only a specific payment leg for paying a BOLT-11 invoice from a Spark account. The primary behavior is much narrower than the declared feature set, and one declared integration is directly contradicted by the code comments and implementation ('No Blink API involvement'). The code does support part of the description—Spark-based Lightning payment and L402-related payment handling—but not the broader wallet functionality listed. The 'all output is JSON' claim also does not fit this module, which writes plain-text messages to stderr and exits when run directly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description presents a Lightning wallet and payment capability centered on Blink API wallet functions and L402 auto-pay. The supplied code does none of those things. It is a standalone service-discovery/search utility that queries l402.directory and 402index.io over HTTP and returns listings of L402-enabled services. This is a materially different primary purpose and accesses different resources than declared. JSON output is consistent, but that alone does not make the description accurate.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The supplied code is narrowly focused on public price and exchange-rate queries. It does support one declared area—price conversion—and partially overlaps with 'transaction history' only in the sense of historical BTC price data, not wallet transaction history. However, the declared description presents a much broader wallet skill with payment, invoice, balance, account, swap, QR, and L402 capabilities, none of which appear in this code chunk. This is a material description-versus-behavior mismatch because the actual primary purpose of the code shown is price lookup/conversion, not wallet operations.

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/non-custodial.md (reported line 190)May include surrounding context.

md
## The Breez API key (`BREEZ_API_KEY`)

The Breez Spark SDK will not connect without a Breez API key. This is an
access credential for **Breez's infrastructure** — it is NOT custody: it never
touches the seed, cannot sign, and cannot spend. It is per-_application_, not
per-_wallet_, so any valid key works with any seed.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Confidence
90% confidence
Finding

Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Content

Scanner excerpt · scripts/_lnurl.js (reported line 71)May include surrounding context.

js
if (/^f[cd][0-9a-f]{2}:/.test(host)) return true; // fc00::/7 unique-local
  if (/^fe[89ab][0-9a-f]:/.test(host)) return true; // fe80::/10 link-local

  // IPv4-mapped IPv6. `new URL()` re-serialises `::ffff:169.254.169.254` into
  // the hex form `::ffff:a9fe:a9fe`, so matching only the dotted spelling would
  // never fire on anything the guard actually receives.
  const mappedHex = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/_spark_sdk.js (reported line 308)May include surrounding context.

js
* Bitcoin wallet. We therefore:
 *   1. reject a symlink (or any non-directory) at the target — a pre-placed
 *      symlink could redirect the wallet state somewhere an attacker controls;
 *   2. create with mode 0700 and chmod unconditionally so a permissively
 *      created dir from an earlier run is repaired;
 *   3. VERIFY the postcondition — no group or world bits may remain — and throw
 *      if they do. A swallowed chmod failure that left the dir at 0777 must be

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/_spark_sdk.js (reported line 391)May include surrounding context.

js
try {
    fs.chmodSync(dir, 0o700);
  } catch (err) {
    // A chmod failure is only fatal if the dir is NOT already owner-only. On
    // filesystems where chmod is unsupported (some network FS) an already-0700
    // dir must be usable; a permissive one must still fail closed.
    let mode = null;

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script can disclose Lightning payment preimages when invoked with --include-preimage, and preimages are secret bearer material that may be usable as L402 credentials or other proof-of-payment tokens. In an agent skill context, a seemingly read-only transaction-history tool returning reusable secrets materially increases exfiltration risk because downstream agents, logs, or users may treat the output as safe metadata.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

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

md
capabilities:
      - http:outbound
      - filesystem:read
      - filesystem:write
  openclaw:
    requires:
      # No env var is required skill-wide: `resolve-receiver` and

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

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

md
blink swap-quote usd-to-btc 500 --unit cents

# Probe an L402-gated URL (discover price without paying)
blink l402-discover https://api.example.com/resource

# Pay for an L402-gated resource (dry-run first)
blink l402-pay https://api.example.com/resource --dry-run

Static analysis

Detected: suspicious.env_credential_access, suspicious.insecure_tls_verification

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/_blink_client.js:22

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
scripts/create_invoice_lnaddress.js:57