Back to skill

Security audit

Torch Prediction Market Kit

Security checks for vulnerabilities and agentic risk

Overview

This is a real autonomous Solana market bot, but its key/linking flow, spending controls, and package provenance are too under-scoped for automatic approval.

Review this before installing or running it with real funds. Use an exact pinned package and lockfile, confirm the runtime imports the reviewed SDK, run with a low-funded dedicated vault, restrict who can edit markets.json, require manual approval or budgets for batches, and avoid providing a valuable private key.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
lib/torchsdk/tokens.js:272
Finding
Unrestricted On-Chain Metadata Fetch Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `lib/torchsdk/tokens.js:272-276`; supporting fetch implementation at `lib/torchsdk/gateway.js:30-41` **Vulnerability Type**: Server-Side Request Forgery through attacker-influenced token metadata URI **Risk Level**: High ### Vulnerable Code ```js // lib/torchsdk/tokens.js:272-276 const uri = (0, program_1.decodeString)(bondingCurve.uri); if (uri) { try { const res = await (0, gateway_1.fetchWithFallback)(uri); const data = (await res.json()); ``` ```js // lib/torchsdk/gateway.js:30-41 const fetchWithFallback = async (url, options, timeoutMs = 10000) => { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); const opts = { ...options, signal: controller.signal }; try { // If it's an Irys gateway URL, use uploader directly (gateway has SSL issues) if ((0, exports.isIrysUrl)(url)) { const uploaderUrl = (0, exports.irysToUploader)(url); return await fetch(uploaderUrl, opts); } // For non-Irys URLs, fetch normally return await fetch(url, opts); ``` ### Technical Analysis The `getToken()` implementation retrieves the metadata URI from on-chain bonding-curve state and passes it directly to `fetchWithFallback()`. The latter accepts arbitrary non-Irys URLs and calls `fetch()` without validating: - The URL scheme - The destination hostname - Resolved IP addresses - Loopback, private, link-local, or cloud metadata address ranges - Redirect destinations - Response content type - Maximum response size The bot invokes `getToken()` from `snapshotMarket()` for each active market. Consequently, metadata retrieval is part of normal continuous operation rather than an explicitly authorized metadata-fetching action. The local allowlist applied to pending market definitions does not fully mitigate this issue. Already-active market entries are not revalidated, and an allowl ...[truncated 1743 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https:` metadata URLs. 2. Apply the approved-host allowlist immediately before every metadata request, including requests for already-active markets. 3. Resolve the hostname and reject all loopback, private, link-local, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6. 4. Set `redirect: "manual"` and validate every redirect destination before following it. 5. Protect against DNS rebinding by connecting only to the validated resolved address or by using a hardened outbound proxy. 6. Enforce a strict response-size limit before parsing JSON. 7. Require an expected JSON content type and reject non-JSON responses. 8. Consider removing metadata retrieval from `getToken()` and exposing it as a separate, explicitly enabled operation. Market snapshots only require on-chain price, volume, holder, and treasury information. 9. Apply a restrictive outbound network policy so the process cannot contact localhost, private networks, or cloud metadata services. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
lib/kit/markets.js:112
Finding
Non-Atomic Market Lifecycle Can Repeat On-Chain Spending After Partial Success<![CDATA[ ## Vulnerability Details **File Location**: `lib/kit/markets.js:112-145`; state transition at `lib/kit/index.js:37-45` **Vulnerability Type**: Non-idempotent retry and incomplete transaction-state persistence **Risk Level**: High ### Vulnerable Code ```js // lib/kit/markets.js:112-145 const createMarket = async (connection, market, agentKeypair, vaultCreator) => { // create the torch token const createResult = await (0, utils_1.withTimeout)((0, torchsdk_1.buildCreateTokenTransaction)(connection, { creator: agentKeypair.publicKey.toBase58(), name: market.name, symbol: market.symbol, metadata_uri: market.metadataUri, }), SDK_TIMEOUT_MS, 'buildCreateTokenTransaction'); createResult.transaction.sign(agentKeypair); const createSig = await (0, utils_1.withTimeout)(connection.sendRawTransaction(createResult.transaction.serialize()), SDK_TIMEOUT_MS, 'sendRawTransaction(create)'); await (0, utils_1.withTimeout)((0, torchsdk_1.confirmTransaction)(connection, createSig, agentKeypair.publicKey.toBase58()), SDK_TIMEOUT_MS, 'confirmTransaction(create)'); const mintAddress = createResult.mint.toBase58(); // seed liquidity via vault buy if (market.initialLiquidityLamports > 0) { const buyResult = await (0, utils_1.withTimeout)((0, torchsdk_1.buildBuyTransaction)(connection, { mint: mintAddress, buyer: agentKeypair.publicKey.toBase58(), amount_sol: market.initialLiquidityLamports, slippage_bps: 500, vault: vaultCreator, }), SDK_TIMEOUT_MS, 'buildBuyTransaction'); buyResult.transaction.sign(agentKeypair); const buySig = await (0, utils_1.withTimeout)(connection.sendRawTransaction(buyResult.transaction.serialize()), SDK_TIMEOUT_MS, 'sendRawTransaction(buy)'); await (0, utils_1.withTimeout)((0, torchsdk_1.confirmTransaction)(connection, buySig, agentKeypair.publicKey.toBase58()), SDK_TIMEOUT_MS, 'confirm ...[truncated 3596 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the two-state creation flow with a durable staged state machine, for example: - `pending` - `create_submitted` - `token_created` - `seed_submitted` - `seeded` - `migration_submitted` - `active` 2. Persist the generated mint address before submitting the token-creation transaction where possible. 3. Persist every submitted transaction signature immediately and atomically before waiting for confirmation. 4. On timeout or restart, query the recorded signature and relevant on-chain accounts before deciding to retry. 5. Retry only the incomplete stage; never restart the full creation sequence when an earlier stage may have succeeded. 6. Use deterministic, market-specific on-chain identifiers where the protocol permits. 7. Write state through a temporary file followed by `fsync` and atomic rename. 8. Introduce a reconciliation pass at startup that compares local stages against on-chain token, purchase, and migration state. 9. Require manual intervention when transaction status remains ambiguous rather than automatically spending again. 10. Add tests for crashes and timeouts after every submitted transaction. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
lib/kit/markets.js:56
Finding
Per-Market Liquidity Limit Does Not Prevent Aggregate Vault Depletion<![CDATA[ ## Vulnerability Details **File Location**: `lib/kit/markets.js:56-78`; autonomous processing at `lib/kit/index.js:32-45` **Vulnerability Type**: Excessive controller spending authority and missing aggregate budget enforcement **Risk Level**: High ### Vulnerable Code ```js // lib/kit/markets.js:56-78 const MAX_LIQUIDITY_LAMPORTS = 10000000000; // 10 SOL const validateLiquidity = (lamports, marketId) => { if (lamports < 0) { throw new Error(`initialLiquidityLamports cannot be negative for market "${marketId}"`); } if (lamports > MAX_LIQUIDITY_LAMPORTS) { throw new Error(`initialLiquidityLamports ${lamports} exceeds max ${MAX_LIQUIDITY_LAMPORTS} (10 SOL) for market "${marketId}"`); } }; ``` ```js // lib/kit/index.js:32-45 const marketCycle = async (connection, log, marketsPath, vaultCreator, agentKeypair) => { const markets = (0, markets_1.loadMarkets)(marketsPath); let dirty = false; for (const market of markets) { try { // --- create pending markets --- if (market.status === 'pending') { log('info', `CREATING | ${market.id} — "${market.question}"`); const mint = await (0, markets_1.createMarket)(connection, market, agentKeypair, vaultCreator); market.mint = mint; market.status = 'active'; ``` ### Technical Analysis The code limits `initialLiquidityLamports` to 10 SOL for each individual market. It does not limit: - The number of pending markets - Total spending in one cycle - Total spending over a time window - Total lifetime spending - The percentage of the vault balance that may be used - A minimum reserve that must remain in the vault The controller processes every pending market autonomously. Therefore, a file containing many individually valid entries can authorize spending far beyond 10 SOL in aggregate. This exceeds minimum-privilege expectations for a disposable autonomous controller. The local 10 ...[truncated 1582 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce a configurable maximum total spend per cycle. 2. Enforce hourly, daily, and lifetime controller budgets. 3. Preserve a mandatory minimum vault reserve. 4. Limit the number of new markets that can be processed in one cycle. 5. Require explicit authority approval for every new market ID or for batches exceeding a low threshold. 6. Maintain a signed allowlist of approved market definitions rather than trusting any writable JSON file. 7. Authenticate market definitions with a signature from the vault authority and verify the signature before spending. 8. Add an on-chain controller allowance if supported, so limits cannot be bypassed by compromising the local process. 9. Re-read the vault balance before each transaction and stop when the remaining budget or reserve threshold is reached. 10. Add rate limits and alerting for unusual market counts or spending volume. 11. Run the bot under a dedicated account with read-only access to its configuration directory, and restrict who can modify `markets.json`. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:35
Finding
Runtime Dependency Resolution Does Not Match the Bundled Audited SDK<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:35-39`; duplicated installer declaration at `agent.json:44-49`; runtime imports at `lib/kit/index.js:22-25`, `lib/kit/markets.js:43-46`, and `lib/kit/utils.js:8` **Vulnerability Type**: Unpinned installation range and externally resolved runtime dependency **Risk Level**: Medium ### Vulnerable Code ```yaml # SKILL.md:35-39 install: - id: torch-prediction-market-kit kind: npm package: torch-prediction-market-kit@^2.0.2 flags: [] ``` ```json // agent.json:44-49 "install": [ { "id": "npm-torch-prediction-market-kit", "kind": "npm", "package": "torch-prediction-market-kit@^2.0.2", "flags": [], ``` ```js // lib/kit/index.js:22-25 const web3_js_1 = require("@solana/web3.js"); const torchsdk_1 = require("torchsdk"); const config_1 = require("./config"); const utils_1 = require("./utils"); ``` ```js // lib/kit/markets.js:43-46 const fs = __importStar(require("fs")); const torchsdk_1 = require("torchsdk"); const oracle_1 = require("./oracle"); const utils_1 = require("./utils"); ``` ```js // lib/kit/utils.js:8 const torchsdk_1 = require("torchsdk"); ``` ### Technical Analysis The project includes SDK source under `lib/torchsdk/` and describes it as bundled for auditability. However, executable bot modules do not import that directory. They resolve the package name `torchsdk` through Node.js package resolution. The installation declaration also uses the range `torch-prediction-market-kit@^2.0.2`, which allows later compatible releases instead of fixing the reviewed artifact exactly. No lockfile or integrity metadata was present in the audited directory structure. As a result, review of `lib/torchsdk/` does not establish that the same SDK code will execute at runtime. Runtime behavior depends on whichever package version is installed and resolved from the deployment environment. This discrepancy is particularly sensitive because package code executes in the same proc ...[truncated 1698 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `torch-prediction-market-kit@^2.0.2` with an exact reviewed version. 2. Include a lockfile with integrity hashes and deploy with a frozen-lockfile command such as `npm ci`. 3. Import the bundled SDK through an explicit relative path, or remove the bundled copy and audit the exact installed artifact instead. 4. Add a build-time and startup assertion that the loaded SDK version and integrity hash match the reviewed release. 5. Publish reproducible build metadata linking source commits, package tarballs, and generated JavaScript. 6. Disable dependency lifecycle scripts unless they are explicitly required and reviewed. 7. Use registry allowlisting and package provenance verification. 8. Run the process with minimal filesystem and network permissions to reduce the impact of a compromised dependency. 9. Avoid placing long-lived private keys in environment variables; use a restricted signer service or ephemeral key whenever possible. 10. Continuously scan exact dependency versions and require a new security review before version upgrades. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (49)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
A documented autonomous bot that does not actually implement the claimed controls, lifecycle, and linkage logic is a security problem because users may deploy it expecting bounded automated behavior and safety properties that are absent. In financial automation, false assurances around vault routing and oracle resolution can directly affect fund safety and operational decisions.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
if (!vault) throw new Error(...)

const link = await getVaultForWallet(connection, agentKeypair.publicKey.toBase58())  // index.ts:144
if (!link) { /* print instructions, exit */ }
```

The bot verifies both vault existence and agent linkage before entering the market cycle.
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The interface includes explicit asset-movement and privilege-management operations such as withdraw_vault, withdraw_tokens, link_wallet, unlink_wallet, and transfer_authority. Those capabilities are not inherently malicious in a protocol, but they are high-risk to expose through an autonomous bot skill because they enable direct custody changes, withdrawal of funds, and expansion of who can spend from a vault if an agent is tricked, misconfigured, or over-permissioned. The skill context makes this more dangerous because it claims safety through a vault model while simultaneously exposing the exact administrative operations that can drain or reassign control of that vault.

Memory Manipulation

High
Category
Memory Poisoning
Content
})))
            .instruction();
    };
    // Helper: build the swap instruction
    const buildSwapIx = async () => {
        return program.methods
            .swapFeesToSol(new anchor_1.BN(minimum_amount_out.toString()))
Confidence
85% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares environment and network requirements but does not define an explicit tool/permission scope. In an agent ecosystem, that creates ambiguity about what capabilities the skill is expected to exercise and weakens policy enforcement, especially for a skill that can initiate blockchain transactions and external HTTP requests.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
Using `npx torch-prediction-market-bot` without pinning an exact version allows whatever package version is current at execution time to be fetched and run. This introduces supply-chain risk and can result in unexpected behavior or malicious code being executed if the package is updated or compromised.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
Line L034 states the bot performs no trading, yet the same document later describes `buildBuyTransaction` usage and agent signing of buy transactions at L131 and L154-L160. A buy is a trading action, so the documentation actively contradicts the behavior it documents.

Intent-Code Divergence

Medium
Confidence
78% confidence
Finding
The audit minimizes oracle resolution as having 'no financial impact' even though the skill metadata describes markets that resolve at a deadline, making resolution a core state transition. If operators trust this claim, they may under-secure oracle inputs or manual resolution flows, which could allow incorrect market outcomes, disputes, or downstream fund/accounting errors depending on protocol behavior.

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
89% confidence
Finding
createMarket signs and submits raw blockchain transactions for token creation, liquidity purchase, and possible migration via sendRawTransaction. These are irreversible external actions, but this file provides no confirmation prompt, user-facing disclosure, or warning text around the transaction execution.

External Transmission

Medium
Category
Data Exfiltration
Content
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkOracle = exports.checkPriceFeed = void 0;
const COINGECKO_API = 'https://api.coingecko.com/api/v3/simple/price';
const ALLOWED_ORACLE_ASSETS = new Set([
    'solana',
    'bitcoin',
Confidence
60% 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkOracle = exports.checkPriceFeed = void 0;
const COINGECKO_API = 'https://api.coingecko.com/api/v3/simple/price';
const ALLOWED_ORACLE_ASSETS = new Set([
    'solana',
    'bitcoin',
Confidence
60% 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkOracle = exports.checkPriceFeed = void 0;
const COINGECKO_API = 'https://api.coingecko.com/api/v3/simple/price';
const ALLOWED_ORACLE_ASSETS = new Set([
    'solana',
    'bitcoin',
Confidence
60% 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkOracle = exports.checkPriceFeed = void 0;
const COINGECKO_API = 'https://api.coingecko.com/api/v3/simple/price';
const ALLOWED_ORACLE_ASSETS = new Set([
    'solana',
    'bitcoin',
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The comment claims the ephemeral key has 'zero risk', but the returned object exposes the raw `keypair` to any caller, which directly exposes private key material in process memory. In a vault-signing bot, misleading safety claims can cause integrators to handle the object less carefully, increasing the chance of key misuse, logging, serialization, or unintended exfiltration while the process is running.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
This index re-exports broad protocol capabilities including token creation, borrowing, repayment, liquidation, rewards claims, and general vault operations that exceed the stated prediction-market-bot scope. In an agent-skill setting, exposing unnecessary high-impact primitives increases the attack surface and raises the risk that downstream agent logic, prompts, or tool routing could invoke financially dangerous actions outside the intended use case.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest describes a Solana prediction-market bot that creates and manages Torch markets and routes funds through a vault. This file adds an external integration to query SAID Protocol for wallet reputation and trust tier, which is not mentioned in the skill description and is not an obvious implementation detail of running Torch markets.

External Transmission

Medium
Category
Data Exfiltration
Content
Object.defineProperty(exports, "__esModule", { value: true });
exports.confirmTransaction = exports.verifySaid = void 0;
const constants_1 = require("./constants");
const SAID_API_URL = 'https://api.saidprotocol.com/api';
// ============================================================================
// Verify
// ============================================================================
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.