Back to skill

Security audit

privy-integration

Security checks for vulnerabilities and agentic risk

Overview

This documentation-only Privy integration skill is on-topic, but it needs review because several wallet, payment, and signing examples could lead to real fund movement without enough safeguards.

Install only if you are comfortable reviewing and adapting the examples before use. Treat all transaction, fee sponsorship, private-key export/import, x402, and MPP snippets as high-risk starting points: use testnets or low-balance wallets, pin package and repo versions, add spend caps and recipient allowlists, validate payment challenges and Solana transaction contents, require human approval for meaningful value, and keep Privy app secrets out of any untrusted install or runtime process.

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

T09 · Insecure Skill Coding Practices

Error
Location
references/solana.md:336
Finding
Unvalidated Solana Transactions Are Signed and Broadcast by the Fee-Payer Service<![CDATA[ ## Vulnerability Details **File Location**: `references/solana.md:336-354` **Vulnerability Type**: Blind signing of attacker-controlled serialized transactions **Risk Level**: Critical ### Vulnerable Code ```tsx const response = await fetch('/api/sponsor', { method: 'POST', body: JSON.stringify({transaction: signed.serialize().toString('base64')}) }); ``` ```ts // API route: /api/sponsor const {transaction: serialized} = req.body; const transaction = Transaction.from(Buffer.from(serialized, 'base64')); // Sign with fee payer transaction.partialSign(feePayerKeypair); // Broadcast const connection = new Connection('https://api.mainnet-beta.solana.com'); const signature = await connection.sendRawTransaction(transaction.serialize()); ``` ### Technical Analysis The sponsorship endpoint deserializes a transaction supplied entirely by the client, adds the server fee-payer signature, and broadcasts it without validating its contents. Base64 decoding is not itself code execution or obfuscation in this case. The decoded value is a Solana transaction. However, applying `partialSign(feePayerKeypair)` authorizes every instruction for which the fee-payer key is declared as a required signer. The example does not demonstrate: - Authentication or authorization of the requesting user. - Verification that the declared fee payer equals the expected server wallet. - Validation of program IDs, instructions, accounts, recipients, or amounts. - Rejection of instructions that transfer assets owned by the fee-payer account. - Limits on compute budget, transaction fees, request frequency, or cumulative sponsored expenditure. - Transaction simulation before signing. - Request-size restrictions or replay protections. Signing opaque client-generated transactions violates least privilege because the endpoint grants general fee-payer signing authority rather than authorizing only a narrowly defined sponsored operation. ### Attack Path 1. The attacker obtains the p ...[truncated 1400 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not sign arbitrary serialized transactions received from clients. Prefer having the client submit a structured transaction intent and reconstruct the transaction on the server from validated fields. At minimum: 1. Require authenticated requests and verify that the caller is authorized to receive sponsorship. 2. Parse and validate every transaction instruction before signing. 3. Require the transaction fee payer to exactly match the configured sponsorship wallet. 4. Allowlist permitted Solana program IDs and instruction types. 5. Validate every writable account, signer, source, destination, amount, and token mint. 6. Explicitly reject instructions that transfer, close, assign, delegate, or otherwise modify assets owned by the fee-payer account. 7. Reject unexpected address lookup tables and unsupported transaction versions. 8. Enforce recent blockhash validity and prevent replay. 9. Apply strict request-body and serialized-transaction size limits. 10. Add per-user, per-session, per-IP, and global rate limits. 11. Enforce per-transaction and cumulative sponsorship budgets. 12. Simulate the transaction and inspect balance changes before signing. 13. Use a dedicated low-balance fee-payer wallet with no unrelated assets. 14. Log all sponsorship decisions and alert on unusual recipients, programs, or spending patterns. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/agent-payments.md:23
Finding
Automatic Machine Payments Lack Mandatory Spend and Destination Constraints<![CDATA[ ## Vulnerability Details **File Locations**: - `references/agent-payments.md:23-35` - `references/agent-payments.md:73-84` - `references/agent-payments.md:179-190` - `references/agent-payments.md:197-208` - `references/agent-payments.md:257-266` - `SKILL.md:258-278` **Vulnerability Type**: Unconstrained automatic wallet payments **Risk Level**: High ### Vulnerable Code The React example enables automatic payment handling without setting the documented `maxValue` protection: ```tsx import {useX402Fetch, useWallets} from '@privy-io/react-auth'; function PremiumContent() { const {wallets} = useWallets(); const {wrapFetchWithPayment} = useX402Fetch(); async function fetchContent() { const fetchWithPayment = wrapFetchWithPayment({ walletAddress: wallets[0]?.address, fetch }); const response = await fetchWithPayment('https://api.example.com/premium'); return response.json(); } return <button onClick={fetchContent}>Fetch Premium Content</button>; } ``` The server-side x402 example similarly wraps fetch without an explicit payment ceiling or destination policy: ```ts // Wrap fetch for automatic 402 handling const fetchWithPayment = wrapFetchWithPayment(fetch, x402client); // Use like normal fetch - 402 responses are handled transparently const response = await fetchWithPayment('https://api.example.com/premium'); const data = await response.json(); ``` The MPP helper accepts an arbitrary URL and automatically signs the resulting payment challenge: ```ts import {Mppx, tempo} from 'mppx/client'; async function makePayment(walletId: string, address: `0x${string}`, url: string) { const account = createPrivyAccount(walletId, address); const mppx = Mppx.create({ polyfill: false, methods: [tempo({account})] }); const response = await mppx.fetch(url); return response.json(); } ``` Global polyfill mode expands automatic payment behavior to ordinary fetch calls: ```ts import {Mppx, tempo} from 'mppx/ ...[truncated 3012 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Make payment validation mandatory in every example, especially autonomous and server-side examples. 1. Enforce a low per-request maximum payment amount. 2. Add per-session, daily, and lifetime wallet spending limits. 3. Allowlist trusted request origins and reject arbitrary user- or agent-supplied URLs. 4. Allowlist payment recipients independently of the server-provided 402 challenge. 5. Restrict permitted chains, currencies, token contracts, payment methods, and facilitators. 6. Disable cross-origin redirects or revalidate all payment constraints after every redirect. 7. Parse and validate the complete 402 payment challenge before requesting a signature. 8. Require explicit human approval above a conservative threshold. 9. Attach restrictive Privy wallet policies covering recipients, chains, contracts, calldata, and values. 10. Disable global `polyfill: true` by default; use a dedicated payment client only at narrowly scoped call sites. 11. Maintain a separate low-balance wallet for autonomous payments. 12. Record payment intent, challenge details, final recipient, amount, and settlement result in an audit log. 13. Add anomaly detection and automatically suspend payment signing after unusual activity. 14. Update the primary x402 example to include `maxValue`, rather than presenting it only as an optional follow-up. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:43
Finding
Unpinned Remote Packages and Skill Content Can Be Installed or Executed<![CDATA[ ## Vulnerability Details **File Locations**: - `SKILL.md:43` - `references/agent-auth.md:124-130` - `references/agent-auth.md:228-233` **Vulnerability Type**: Mutable remote dependency installation and execution **Risk Level**: Medium ### Vulnerable Code ```text Docs: Privy ships an official agent skill - `npx skills add https://docs.privy.io` (or fetch `https://docs.privy.io/skill.md`). ``` ```bash git clone https://github.com/privy-io/privy-agentic-wallets-skill.git ~/.openclaw/workspace/skills/privy ``` ```bash claude mcp add auth-agent -- npx @auth/agent-cli mcp --url https://api.example.com ``` ### Technical Analysis The documented commands install or execute mutable remote content without pinning it to an immutable version or verifying its integrity. `npx @auth/agent-cli` can download and immediately execute the current package release. The Git command clones the repository’s current default branch into a persistent OpenClaw Skill directory. The remote Skill installer similarly obtains content from a URL whose response can change after this project has been reviewed. No evidence indicates that the current upstream projects are malicious. The vulnerability is a supply-chain trust weakness: future behavior depends on mutable external resources controlled outside this repository. Potential compromise sources include: - Package registry account takeover. - Malicious or compromised future package releases. - GitHub repository or maintainer account compromise. - DNS, hosting, or remote documentation compromise. - Unexpected changes to a repository’s default branch. - Transitive dependency compromise. ### Attack Path 1. An attacker compromises an upstream package, repository, maintainer account, or remote Skill hosting endpoint. 2. The attacker publishes a modified package release, changes the default branch, or replaces the remotely served Skill content. 3. A user follows one of the documented unpinned installation commands. 4. The curren ...[truncated 1088 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin npm packages to exact reviewed versions, for example `@auth/agent-cli@<exact-version>`. 2. Pin Git installations to an immutable reviewed commit hash or signed release tag. 3. Publish and verify SHA-256 or stronger integrity hashes for downloaded Skill files. 4. Use package lockfiles and enforce lockfile integrity in deployment workflows. 5. Prefer `npm install --ignore-scripts` followed by manual review when lifecycle scripts are unnecessary. 6. Avoid allowing `npx` to automatically download and execute previously unavailable packages. 7. Download remote Skill content to a staging location, review it, and only then activate it. 8. Verify repository and release signatures where available. 9. Run third-party CLIs and Skills in a sandbox with restricted filesystem, environment, wallet, and network access. 10. Do not expose `PRIVY_APP_SECRET` or wallet authorization credentials to installation processes. 11. Monitor upstream advisories and establish an explicit dependency-update review process. 12. Document the exact audited package versions and commit hashes alongside every installation command. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (35)

Credential Access

High
Category
Privilege Escalation
Content
appSecret: process.env.PRIVY_APP_SECRET!
});

// Verify access token from Authorization header
// (top-level privy.verifyAuthToken is deprecated - use utils().auth())
const {userId} = await privy.utils().auth().verifyAccessToken(accessToken);
```
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
appSecret: process.env.PRIVY_APP_SECRET!
});

// Verify access token from Authorization header
// (top-level privy.verifyAuthToken is deprecated - use utils().auth())
const {userId} = await privy.utils().auth().verifyAccessToken(accessToken);
```
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- **MCP server** = OAuth 2.1 resource server (accepts Bearer tokens)
- **MCP client** = OAuth 2.1 client (makes protected requests)
- **Authorization server** = issues access tokens (may be co-located or separate)

### Discovery Flow
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
96% confidence
Finding
The document explicitly demonstrates `importWallet({privateKey: '0x...'})` without any warning that private keys are highly sensitive secrets that must never be hardcoded, logged, copied into frontend code, or handled in insecure environments. In a wallet/auth SDK reference, this pattern is especially dangerous because readers may cargo-cult the example into production, leading to key theft and irreversible loss of funds or account compromise.

Credential Access

High
Category
Privilege Escalation
Content
## Token Verification

### Access Token Verification

```ts
// In an API route or middleware
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Token Verification

### Access Token Verification

```ts
// In an API route or middleware
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Token Types

### Access Tokens
- **Lifetime**: 1 hour (configurable in Dashboard)
- **Format**: ES256-signed JWT
- **Claims**: `sid` (session ID), `sub` (user DID), `aud` (app ID), `iss` (privy.io), `iat`, `exp`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- **Lifetime**: 30 days (configurable)
- **Format**: Opaque string (not a JWT)
- **Management**: Handled entirely by Privy SDK, never expose to app code
- **Use**: Auto-refresh access tokens

### Identity Tokens
- **Lifetime**: 10 hours (configurable)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- Rust SDK: https://docs.privy.io/basics/rust/quickstart
- Java SDK: https://docs.privy.io/basics/java/quickstart
- REST API quickstart: https://docs.privy.io/basics/rest-api/quickstart
- Access tokens: https://docs.privy.io/authentication/user-authentication/access-tokens
- Identity tokens: https://docs.privy.io/user-management/users/identity-tokens
- Webhooks overview: https://docs.privy.io/wallets/actions/webhooks
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The transaction example demonstrates sending an on-chain transfer without an explicit warning that it can move real assets if connected to a funded wallet. In a wallet integration skill, this omission is risky because developers may paste the code into production-like environments and trigger unintended transfers.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill provides payment-enabled x402 and MPP fetch examples that can cause real blockchain or protocol-mediated charges, but it does not place an explicit safety warning adjacent to those examples. In an agentic context, users may copy these snippets into automation flows and unintentionally authorize recurring or opaque monetary spend against live wallets.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The natural-language examples show an agent creating wallet policies and sending ETH without an immediate, prominent warning that these commands can trigger real on-chain transactions with irreversible financial consequences. In an agentic-wallet context, users may treat examples as harmless demos and unintentionally authorize live fund movement.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The documentation instructs users to execute an unpinned package via `npx @auth/agent-cli`, which fetches the latest published version at runtime. If the package is compromised, typosquatted, or a breaking release is published, users may execute attacker-controlled code or unexpectedly altered behavior during setup.

External Transmission

Medium
Category
Data Exfiltration
Content
walletAddress: wallets[0]?.address,
      fetch
    });
    const response = await fetchWithPayment('https://api.example.com/premium');
    return response.json();
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
walletAddress: wallets[0]?.address,
      fetch
    });
    const response = await fetchWithPayment('https://api.example.com/premium');
    return response.json();
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
walletAddress: wallets[0]?.address,
      fetch
    });
    const response = await fetchWithPayment('https://api.example.com/premium');
    return response.json();
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
walletAddress: wallets[0]?.address,
      fetch
    });
    const response = await fetchWithPayment('https://api.example.com/premium');
    return response.json();
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
walletAddress: wallets[0]?.address,
      fetch
    });
    const response = await fetchWithPayment('https://api.example.com/premium');
    return response.json();
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
walletAddress: wallets[0]?.address,
      fetch
    });
    const response = await fetchWithPayment('https://api.example.com/premium');
    return response.json();
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
|------|-----|------|
| Pay AI | `https://facilitator.payai.network/` | https://docs.payai.network/x402/reference |
| Corbits | `https://facilitator.corbits.dev/` | https://docs.corbits.dev/ |
| Coinbase | `https://api.cdp.coinbase.com/platform/v2/x402` | https://docs.cdp.coinbase.com/api-reference/v2/rest-api/x402-facilitator/x402-facilitator |

### x402 Payment Flow
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This section recommends `polyfill: true`, causing ordinary `fetch` calls to automatically handle HTTP 402 payment flows. That can turn previously non-paying network requests into spending actions without an explicit per-request consent or a prominent warning, increasing the risk of unintended fund loss if developers enable it broadly or point it at untrusted endpoints.

External Transmission

Medium
Category
Data Exfiltration
Content
});

// All fetch calls now handle 402 responses automatically
const response = await fetch('https://api.example.com/weather');
```

**mppx version footguns** (pin mppx - this skill tracks `0.6.30`):
Confidence
88% confidence
Finding
This example shows global fetch polyfilling so that all `fetch` calls may transparently satisfy payment challenges. In practice, that can cause unexpected spending on cross-origin or insufficiently reviewed requests, especially in larger apps where many libraries or components issue network calls.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The transaction examples demonstrate live on-chain sends without warning that transactions are irreversible, chain-specific, and can move real funds. In a payments/wallet SDK reference, developers may copy examples directly into production or testing against mainnet-like environments without adding confirmation, simulation, or value/recipient validation.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The documentation shows direct private key export with no surrounding warning, restriction guidance, or recommendation to avoid export except for break-glass recovery flows. In a wallet/auth integration skill, normalizing private key extraction can lead developers to build unsafe server logs, persistence, or transmission paths that fully compromise wallet control.

Static analysis

No suspicious patterns detected.