Back to skill

Security audit

DEX Agent

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real on-chain trading wallet tool, but it handles private keys and automated trades in ways users should review carefully before installing.

Only install this after treating it as a high-risk mainnet trading tool. Do not fund its generated wallet with meaningful assets unless you accept plaintext local key storage, unlimited token approvals, no pre-broadcast confirmations, and incomplete documented risk controls. Prefer a test wallet with tiny amounts, review and pin dependencies, and consider modifying the code to use encrypted key storage, exact approvals, explicit confirmations, and enforced slippage/risk limits.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wallet.py:18
Finding
Trading Wallet Private Key Is Stored in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wallet.py:18-45` **Vulnerability Type**: Plaintext storage of sensitive credentials **Risk Level**: High ### Vulnerable Code ```python def generate_wallet(): """Generate a new trading wallet.""" account = Account.create() wallet_data = { "address": account.address, "private_key": account.key.hex(), "note": "DEX Agent trading wallet. NEVER share this key. Generated locally." } WALLET_DIR.mkdir(exist_ok=True) with open(WALLET_FILE, "w") as f: json.dump(wallet_data, f, indent=2) os.chmod(WALLET_FILE, 0o600) print(f"✅ Wallet generated: {account.address}") print(f" Saved to: {WALLET_FILE}") print(f" ⚠️ Fund this wallet with ETH (for gas) and USDC (for trading)") return account.address def load_wallet(): """Load the trading wallet.""" if not WALLET_FILE.exists(): print("❌ No wallet found. Run: python3 wallet.py generate") return None, None with open(WALLET_FILE) as f: data = json.load(f) return data["address"], data["private_key"] ``` ### Technical Analysis The raw private key is serialized directly into `wallets/trading-wallet.json`. Although the file is assigned mode `0600`, this is an access-control measure rather than encryption. It does not protect the key from processes running as the same operating-system user, compromised backups, accidental archive publication, filesystem disclosure vulnerabilities, or malicious local dependencies. This also contradicts the module statement that private keys are stored in encrypted form. ### Attack Path 1. The user runs the wallet-generation command. 2. The Skill writes the raw hexadecimal private key to `scripts/wallets/trading-wallet.json`. 3. An attacker obtains same-user filesystem access, compromises a backup, or exploits another local file-read weakness. 4. The attacker reads and imports the private key into another wallet. 5. ...[truncated 429 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace raw-key JSON storage with an encrypted Web3 keystore using a strong user-supplied passphrase. - Prefer an operating-system keychain, hardware wallet, HSM, or external signer for production funds. - Keep decrypted key material in memory only for the shortest practical duration. - Ensure wallet and parent-directory permissions are restrictive before writing sensitive data. - Use atomic file creation with exclusive-create semantics. - Update documentation so that it accurately describes the implemented key-protection model. - Warn existing users to migrate funds to a newly generated, securely managed wallet if the plaintext file may have been exposed. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wallet.py:18
Finding
Wallet Generation Silently Overwrites an Existing Private Key<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wallet.py:18-29` **Vulnerability Type**: Destructive credential overwrite **Risk Level**: High ### Vulnerable Code ```python def generate_wallet(): """Generate a new trading wallet.""" account = Account.create() wallet_data = { "address": account.address, "private_key": account.key.hex(), "note": "DEX Agent trading wallet. NEVER share this key. Generated locally." } WALLET_DIR.mkdir(exist_ok=True) with open(WALLET_FILE, "w") as f: json.dump(wallet_data, f, indent=2) os.chmod(WALLET_FILE, 0o600) ``` ### Technical Analysis The wallet-generation function opens the fixed wallet path in write mode without first checking whether a wallet already exists. Write mode truncates the old file before storing the newly generated key. Because the documented command can be run repeatedly and no confirmation, backup, or recovery mechanism is provided, accidental invocation can destroy the only local copy of the key for a funded wallet. ### Attack Path 1. A funded wallet already exists at `scripts/wallets/trading-wallet.json`. 2. The user, an automation process, or another caller invokes `wallet generate` again. 3. The function creates a new account and truncates the existing wallet file. 4. The previous private key is removed and replaced by the new key. 5. If no independent backup exists, the user permanently loses access to assets associated with the previous address. ### Impact Assessment The vulnerability can cause irreversible loss of access to all ETH and tokens held by the overwritten wallet. It does not grant an external attacker direct control by itself, but its financial impact may be equivalent to complete wallet loss. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Refuse to generate a wallet when the target file already exists. - Require an explicit destructive flag and interactive confirmation before replacement. - Display the existing address and require the user to confirm that its key has been backed up. - Write new data to a temporary file and atomically rename it only after successful validation. - Use exclusive file creation, such as mode `x`, to prevent unintended truncation. - Provide an explicit, separately named wallet-rotation workflow rather than overloading wallet generation. - Store versioned, encrypted backups where appropriate. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/swap.py:145
Finding
Swaps Grant a Permanent Unlimited Token Allowance<![CDATA[ ## Vulnerability Details **File Location**: `scripts/swap.py:145-174` **Vulnerability Type**: Excessive token-spending authorization **Risk Level**: High ### Vulnerable Code ```python def ensure_approval(self, token_address, amount): """Approve the router to spend tokens if needed.""" token = self.w3.eth.contract( address=Web3.to_checksum_address(token_address), abi=ERC20_ABI ) router_addr = Web3.to_checksum_address(UNISWAP["swap_router_02"]) current_allowance = token.functions.allowance(self.address, router_addr).call() if current_allowance >= amount: return True print(f" 📝 Approving router to spend tokens...") max_approval = 2**256 - 1 # Max approval tx = token.functions.approve(router_addr, max_approval).build_transaction({ "from": self.address, "chainId": CHAIN_ID, "gas": 100_000, "nonce": self.w3.eth.get_transaction_count(self.address), "maxFeePerGas": self.w3.eth.gas_price * 2, "maxPriorityFeePerGas": self.w3.to_wei(0.001, "gwei"), }) signed = self.w3.eth.account.sign_transaction(tx, self.private_key) tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction) receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60) if receipt.status == 1: print(f" ✅ Approved! Tx: {tx_hash.hex()}") return True else: print(f" ❌ Approval failed!") return False ``` ### Technical Analysis A swap requiring approval grants the configured router the maximum possible ERC-20 allowance rather than limiting authorization to the current trade amount. The allowance persists after the transaction and applies to tokens subsequently deposited into the wallet. The configured address appears intended to be Uniswap SwapRouter02, but permanent unlimited authorization still violates least privilege and expands the impact of contract compromise, incorrect configuration, chain mismatch, ...[truncated 787 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Approve only the exact amount required for the pending trade. - Revoke or reset residual allowances after execution when token behavior permits it. - Support Permit2 or token permits with bounded amounts, expirations, and nonces. - Verify the chain ID and expected router bytecode before granting approval. - Clearly disclose approval amount and duration before asking the user to authorize a transaction. - Provide a command that lists and revokes existing allowances. - Account for ERC-20 tokens that require setting allowance to zero before assigning a new amount. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/swap.py:179
Finding
Advertised Risk Controls and Maximum Slippage Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:45-62`, `scripts/agent.py:42-52`, `scripts/swap.py:179-220` **Vulnerability Type**: Missing validation and ineffective security configuration **Risk Level**: High ### Vulnerable Code ```python # Trading Parameters DEFAULT_SLIPPAGE_BPS = 100 # 1% default slippage MAX_SLIPPAGE_BPS = 500 # 5% max slippage GAS_LIMIT = 350_000 OUR_FEE_BPS = 30 # 0.3% our fee (vs Bankr's 0.65%) # Risk Management Defaults # These can be overridden via trading-config.json at runtime RISK_DEFAULTS = { "max_daily_trades": 8, # Max new trades per 24h period "max_active_positions": 8, # Max concurrent open positions "trade_size_usd": 20, # Default trade size in USD "take_profit_pct": 5.0, # Take profit trigger (%) "stop_loss_pct": 8.0, # Stop loss trigger (%) "max_drawdown_pct": 20.0, # Max portfolio drawdown before halt "cooldown_minutes": 60, # Min time between trades on same token "min_liquidity": 50000, # Min pool liquidity (USD) "min_volume_24h": 100000, # Min 24h volume (USD) } ``` ```python swapper = DexSwapper() token_in = sys.argv[2] token_out = sys.argv[3] amount = float(sys.argv[4]) slippage = int(sys.argv[5]) if len(sys.argv) > 5 else 100 fee = int(sys.argv[6]) if len(sys.argv) > 6 else 3000 if token_in.upper() == "ETH": result = swapper.swap_eth_for_token(token_out, amount, slippage, fee) else: result = swapper.swap(token_in, token_out, amount, slippage, fee) ``` ```python # Apply slippage min_out = int(quote * (10000 - slippage_bps) / 10000) min_out_human = min_out / (10 ** out_info["decimals"]) print(f" Min out (with slippage): {min_out_human:.6f} {out_info['symbol']}") ``` ### Technical Analysis `MAX_SLIPPAGE_BPS` is declared but never checked during transaction execution. User-controlled basis points are passed directly into the minimum-output calculation. A value close to 10,000 p ...[truncated 1457 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject slippage outside the inclusive range from zero to `MAX_SLIPPAGE_BPS`. - Validate that amounts are finite, positive, and within configured trade-size limits. - Restrict fee tiers to explicitly supported Uniswap V3 values. - Load `trading-config.json` through a schema-validated configuration layer. - Enforce daily trade count, active-position count, cooldown, drawdown, liquidity, and volume controls before signing any transaction. - Persist trade and portfolio state atomically so limits survive process restarts. - Fail closed when required price, liquidity, volume, or portfolio information is unavailable. - Add unit and integration tests proving that every documented limit blocks noncompliant transactions. - Remove or revise documentation for any protection that is not implemented. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/price_monitor.py:151
Finding
Triggered Protective Orders Are Disabled Before Successful Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/price_monitor.py:151-176`, `scripts/agent.py:121-133` **Vulnerability Type**: Unsafe conditional-order state transition **Risk Level**: High ### Vulnerable Code ```python if order["type"] == "stop_loss" and current_price <= order["trigger_price"]: print(f"🚨 STOP-LOSS TRIGGERED: {token} at ${current_price:.6f} (trigger: ${order['trigger_price']:.6f})") order["status"] = "triggered" order["triggered_price"] = current_price order["triggered_at"] = time.time() triggered.append(order) elif order["type"] == "take_profit" and current_price >= order["trigger_price"]: print(f"🎉 TAKE-PROFIT TRIGGERED: {token} at ${current_price:.6f} (trigger: ${order['trigger_price']:.6f})") order["status"] = "triggered" order["triggered_price"] = current_price order["triggered_at"] = time.time() triggered.append(order) else: pct_from_trigger = ((current_price - order["trigger_price"]) / order["trigger_price"]) * 100 direction = "above" if pct_from_trigger > 0 else "below" print(f" {order['type'].upper()} {token}: ${current_price:.6f} ({abs(pct_from_trigger):.1f}% {direction} trigger)") self.save_orders() return triggered ``` ```python elif cmd == "monitor": from price_monitor import PriceMonitor monitor = PriceMonitor() triggered = monitor.check_orders() if triggered: # Auto-execute triggered orders from swap import DexSwapper swapper = DexSwapper() for order in triggered: print(f"\n⚡ Executing {order['type']}: selling {order['amount']} {order['token']}") result = swapper.swap(order['token'], "USDC", order['amount']) if result and result["status"] == "success": print(f" ✅ Sold! Tx: {result['tx_hash']}") ``` ### Technical Analysis An active order is changed to `triggered` and persisted before its swap is attempted. Future monitoring skips all orders whose status is ...[truncated 1155 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement explicit states such as `active`, `executing`, `submitted`, `completed`, and `failed`. - Mark an order `completed` only after receiving a successful on-chain transaction receipt. - Return failed orders to `active` or a retryable state according to a bounded retry policy. - Persist transaction hashes so restart recovery can determine whether a transaction succeeded. - Use atomic file writes and locking to avoid corrupt or conflicting order updates. - Add idempotency controls to prevent duplicate execution when recovering from uncertain RPC responses. - Emit prominent alerts for failed protective orders and require operator intervention after repeated failures. - Recheck balances and current prices before every retry. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:20
Finding
Installation Instructions Use Unpinned Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:20` **Vulnerability Type**: Unpinned software supply-chain dependencies **Risk Level**: Medium ### Vulnerable Code ```bash pip3 install web3 eth-abi ``` ### Technical Analysis The documented installation command resolves the latest available versions of `web3`, `eth-abi`, and their transitive dependencies at installation time. No lockfile, reviewed version constraints, package hashes, or isolated environment requirements are supplied. This makes installations non-reproducible and allows newly published, compromised, or incompatible releases to enter the runtime without repository changes. These packages execute in the same Python process that handles the plaintext wallet key and signs transactions, increasing the consequence of dependency compromise. ### Attack Path 1. The user follows the documented setup command. 2. Package resolution selects whatever versions and transitive dependencies are current at that time. 3. A compromised or malicious release is downloaded from the configured package index. 4. Malicious code executes during installation or when imported by the Skill. 5. The dependency can access the wallet file, private key in process memory, RPC communications, and transaction-signing operations. ### Impact Assessment A compromised dependency executes with the privileges of the user running the Skill. It may read wallet credentials, steal funds, modify transaction recipients or amounts, alter local files, or compromise other data available to that operating-system account. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin exact, reviewed direct and transitive dependency versions in a lockfile. - Require cryptographic hashes for downloaded distributions. - Install dependencies inside a dedicated virtual environment or container. - Use a trusted package index and disable unintended fallback indexes. - Add automated vulnerability and provenance scanning for dependency updates. - Review and test dependency upgrades before publishing a new Skill release. - Consider reproducible build tooling such as `pip-tools`, Poetry, or an equivalent locked dependency workflow. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (15)

Missing User Warnings

High
Confidence
97% confidence
Finding
The documented commands include live swap execution, but the safety language does not clearly and prominently warn that these commands can create irreversible on-chain transactions using the user’s wallet and funds. In a self-custodial DeFi context, insufficient disclosure materially increases the risk of accidental asset loss, mistaken trades, and user misunderstanding about the finality of execution.

Missing User Warnings

High
Confidence
97% confidence
Finding
When triggered orders are found, the code immediately executes swaps without a just-in-time confirmation, review step, or dry-run mode. In a DeFi wallet context, this can directly spend user assets due to bad configuration, manipulated price signals, or logic errors, making the risk substantially higher than a normal CLI convenience issue.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The module claims private keys are stored encrypted locally, but the implementation writes the raw private key directly into a JSON file. This is dangerous because users may rely on the false security claim and fund the wallet, while any local compromise, backup leak, log collection, or accidental file exposure would immediately expose the private key and allow theft of funds.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The skill’s trigger conditions are broad enough to activate on generic crypto-related requests, including cases where a user may only want information rather than transaction execution. In the context of a trading skill that can access a wallet and perform on-chain actions, overbroad routing increases the chance of unintended invocation and risky escalation from informational queries to trading workflows.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README gives direct commands for generating wallets, funding them, and executing live on-chain swaps, stop-losses, and take-profit orders, but it does not warn users that these actions are irreversible, involve real funds, and may incur slippage, smart-contract, or market risks. In a trading skill, omission of clear risk and safety disclosures can cause users to treat examples as safe defaults and unintentionally lose funds.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
monitor = PriceMonitor()
        triggered = monitor.check_orders()
        if triggered:
            # Auto-execute triggered orders
            from swap import DexSwapper
            swapper = DexSwapper()
            for order in triggered:
Confidence
95% confidence
Finding
The code performs autonomous decision making by taking triggered monitoring results and converting them into live market sales. In the context of an agent skill for DeFi trading, autonomous action is especially dangerous because external price inputs, order logic mistakes, or malicious configuration can cause real asset loss without human review.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
Wallet generation is initiated directly from the CLI without any visible warning about private key creation, storage location, backup requirements, or the consequences of insecure handling. In a crypto-trading tool, poor key handling can lead to irreversible loss of funds, so the missing warning is security-relevant rather than merely a usability issue.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The CLI help text documents trading-oriented commands but omits the scan behavior, while the implementation advertises 'auto-trade via DEX Agent'. In a financial tool that can move funds, understating or hiding automation materially increases the chance a user triggers behavior they did not fully understand or consent to.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The module advertises itself as a 'DIY stop-loss and take-profit system', but it never executes any trade when a trigger condition is met; it only prints alerts, updates local JSON state, and tells the user to 'Execute swaps manually or via swap.py'. In a trading context, this mismatch is dangerous because users may rely on it for automated loss protection and suffer avoidable financial losses during rapid market moves.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The docstring advertises 'Our fee: 0.3%' but the code contains no fee-transfer, fee accounting, or recipient logic beyond the Uniswap pool fee parameter. This is misleading financial behavior: users may believe the tool is charging or handling fees in a way it does not, which can conceal economic misrepresentation or cause unsafe assumptions during review and operation.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The approval flow grants the router an unlimited ERC-20 allowance (`2**256 - 1`) whenever current allowance is insufficient, without a specific per-swap cap or explicit user confirmation. If the configured router address is wrong, upgraded maliciously, or later exploited, all approved token balances can be drained, making this substantially riskier than approving only the needed amount.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code signs and broadcasts an irreversible swap transaction immediately after building it, with no final user confirmation step displaying the resolved token addresses, amount, minimum output, spender/router, and gas cost. In an agent skill context, that increases the chance of accidental or manipulated transactions being sent on-chain without the operator noticing parameter substitution or unsafe slippage settings.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The ETH swap path signs and submits a transaction carrying native value immediately, again without explicit confirmation. Because ETH transfers are direct and irreversible and this path sends `value=amount_raw`, any mistaken token mapping, router misconfiguration, or malicious invocation can cause immediate loss of funds with less recoverability than an approval-only mistake.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The code persists the wallet's private key to disk in plaintext, and file permission hardening alone does not provide cryptographic protection. In the context of a trading wallet skill that explicitly instructs users to fund the wallet with ETH and USDC, disclosure of this file would let an attacker fully control the wallet and drain assets.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill advertises direct on-chain trading, self-custodial wallet management, and automated order features, but the description does not clearly warn users that actions can execute real blockchain trades, consume gas, and cause irreversible financial loss. In a DeFi trading context, this omission materially increases the risk that users or downstream agents invoke the skill without understanding that it can move funds on-chain.

Static analysis

No suspicious patterns detected.