Back to skill

Security audit

Grand Bazaar Swap

Security checks for vulnerabilities and agentic risk

Overview

This skill is for real Base mainnet swaps, but its scripts can approve and move assets with raw private keys while trusting order metadata for the contract target.

Review carefully before installing. Use only dedicated low-value wallets, avoid pasting production private keys into shell environments, verify every token and swap contract address independently, and do not run sender_execute_order.js on orders from others unless the contract address is allowlisted and the transaction details are confirmed. Dependency updates and a dry-run/confirmation gate should be added before production use.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/sender_execute_order.js:66
Finding
Untrusted order metadata controls the token spender and swap execution target<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sender_execute_order.js:66-71, 83-90, 143-159, 210-214` **Vulnerability Type**: Arbitrary contract approval and invocation through untrusted metadata **Risk Level**: High ### Vulnerable Code ```js function resolveSwapAddress(payload, order) { if (process.env.SWAP_ADDRESS) return process.env.SWAP_ADDRESS; const fromMeta = payload?.meta?.verifyingContract || payload?.meta?.swapContract; if (fromMeta) return fromMeta; const senderKind = String(order?.sender?.kind || '').toLowerCase(); if (senderKind === KIND_ERC721.toLowerCase()) return SWAP_ERC721; if (senderKind === KIND_ERC1155.toLowerCase()) return SWAP_ERC1155; return SWAP_DEFAULT; } ``` ```js const payload = JSON.parse(fs.readFileSync(path.resolve(IN), 'utf8')); const order = payload.order; const signature = payload.signature; const provider = new ethers.providers.JsonRpcProvider(RPC); const sender = new ethers.Wallet(SENDER_PRIVATE_KEY, provider); const swapAddress = resolveSwapAddress(payload, order); const swap = new ethers.Contract(swapAddress, SWAP_ABI, provider); ``` ```js if (allowance.lt(total)) { const tx = await senderToken.connect(sender).approve(swapAddress, total, { maxPriorityFeePerGas: feeOverrides.maxPriorityFeePerGas, maxFeePerGas: feeOverrides.maxFeePerGas, }); console.log('approveSenderAssetTx', tx.hash); await tx.wait(); } ``` ```js const tx = await swap.connect(sender).swap(recipient, maxRoyalty, orderForCall, { gasLimit, maxPriorityFeePerGas: feeOverrides.maxPriorityFeePerGas, maxFeePerGas: feeOverrides.maxFeePerGas, }); ``` ### Technical Analysis The sender script treats `meta.verifyingContract` or `meta.swapContract` from the input order file as an authoritative swap address. It does not verify that this value is one of the three documented Base mainnet AirSwap deployments. The EIP-712 signature check does not establish that the contract is trusted. An attacker can create an or ...[truncated 2115 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define an immutable allowlist mapping each supported sender kind to its approved Base mainnet deployment. 2. Derive the swap address from the validated chain ID and sender kind rather than trusting order metadata. 3. Reject the order if `meta.verifyingContract`, `meta.swapContract`, or the compressed order's contract differs from the derived allowlisted address. 4. Verify that the connected network has chain ID `8453` before signing, approving, or broadcasting. 5. Optionally verify deployed runtime bytecode hashes against known deployment hashes. 6. Remove `SWAP_ADDRESS` overrides from production execution. If retained for development, require an explicit unsafe-development flag and prevent use with funded production wallets. 7. Perform all contract-address validation before querying token balances or issuing approvals. 8. Consider simulating the exact token balance changes through a trusted contract before granting approval. 9. Revoke any residual allowance after a failed execution where practical. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sender_execute_order.js:195
Finding
Gas-limit safety rejection is swallowed by the estimation fallback<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sender_execute_order.js:195-208` **Vulnerability Type**: Safety-control bypass through overly broad exception handling **Risk Level**: Medium ### Vulnerable Code ```js const maxGasLimit = getMaxGasLimit(); let estimatedGas = null; let gasLimit = maxGasLimit; let usedManualGasFallback = false; try { estimatedGas = await swap.connect(sender).estimateGas.swap(recipient, maxRoyalty, orderForCall, { maxPriorityFeePerGas: feeOverrides.maxPriorityFeePerGas, maxFeePerGas: feeOverrides.maxFeePerGas, }); if (estimatedGas.gt(maxGasLimit)) { throw new Error(`Preflight failed: estimated gas ${estimatedGas.toString()} exceeds MAX_GAS_LIMIT ${maxGasLimit.toString()}`); } gasLimit = estimatedGas.mul(120).div(100); if (gasLimit.gt(maxGasLimit)) { gasLimit = maxGasLimit; } } catch (e) { usedManualGasFallback = true; console.log(`estimateGas failed, using manual gas limit ${maxGasLimit.toString()}: ${e.message || e}`); } ``` The transaction is subsequently broadcast despite the caught rejection: ```js const tx = await swap.connect(sender).swap(recipient, maxRoyalty, orderForCall, { gasLimit, maxPriorityFeePerGas: feeOverrides.maxPriorityFeePerGas, maxFeePerGas: feeOverrides.maxFeePerGas, }); ``` ### Technical Analysis The `try` block covers both the RPC gas-estimation operation and the script's explicit policy check. If estimation succeeds but returns a value above `MAX_GAS_LIMIT`, the script deliberately throws a preflight error. The broad `catch` immediately catches that same error, treats it as an estimation failure, and continues with the manual maximum gas limit. This defeats the documented requirement to abort when the estimate exceeds the configured ceiling. The fallback is therefore used not only for RPC simulation failures but also for transactions already known to require more gas than the permitted limit. ### Attack Path 1. A swap call is constructed whose e ...[truncated 970 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Separate RPC estimation failures from policy validation: ```js let estimatedGas; try { estimatedGas = await swap.connect(sender).estimateGas.swap( recipient, maxRoyalty, orderForCall, feeOptions ); } catch (error) { // Apply a narrowly controlled fallback only for explicitly accepted errors. estimatedGas = null; } if (estimatedGas) { if (estimatedGas.gt(maxGasLimit)) { throw new Error( `Preflight failed: estimated gas ${estimatedGas} exceeds MAX_GAS_LIMIT ${maxGasLimit}` ); } gasLimit = estimatedGas.mul(120).div(100); if (gasLimit.gt(maxGasLimit)) { gasLimit = maxGasLimit; } } else { gasLimit = maxGasLimit; } ``` Additional hardening: 1. Permit manual fallback only for explicitly recognized simulation errors from allowlisted contracts. 2. Never use fallback after an application-generated safety exception. 3. Log a machine-readable reason code distinguishing RPC failure, contract revert, and policy rejection. 4. Limit fallback to one attempt and require operator confirmation outside narrowly scoped test-wallet automation. 5. Add a regression test asserting that an estimate above `MAX_GAS_LIMIT` produces no broadcast. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/signer_make_order.js:6
Finding
Executable ERC20 workflow does not use the deployment, signature schema, or fee semantics declared by the Skill<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:10-12, 185-192`; `scripts/signer_make_order.js:6-8, 178-185`; `scripts/sender_execute_order.js:7-9, 105-111` **Vulnerability Type**: Security-critical documentation and implementation mismatch **Risk Level**: Medium ### Conflicting Skill Declaration `SKILL.md` declares the following ERC20 route: ```md Sender-side token standard is routed to the matching Swap contract: - ERC20<>ERC20 -> `SwapERC20` on Base `0x95D598D839dE1B030848664960F0A20b848193F4` ``` It also declares the v4.3 typed-data domain and schema: ```md SwapERC20 v4.3 - Domain: - name: `SWAP_ERC20` - version: `4.3` - chainId: `8453` - verifyingContract: `0x95D598D839dE1B030848664960F0A20b848193F4` - Types: - `OrderERC20(uint256 nonce,uint256 expiry,address signerWallet,address signerToken,uint256 signerAmount,uint256 protocolFee,address senderWallet,address senderToken,uint256 senderAmount)` ``` ### Conflicting Implementation `scripts/signer_make_order.js` instead selects the legacy deployment: ```js const RPC = process.env.RPC_URL || 'https://mainnet.base.org'; const SWAP_DEFAULT = '0x8a9969ed0A9bb3cDA7521DDaA614aE86e72e0A57'; const SWAP_ERC721 = '0x2aa29F096257bc6B253bfA9F6404B20Ae0ef9C4d'; const SWAP_ERC1155 = '0xD19783B48b11AFE1544b001c6d807A513e5A95cf'; ``` It signs the legacy v4.2 domain: ```js const domain = { name: 'SWAP', version: '4.2', chainId: 8453, verifyingContract: swapAddress, }; const sig = await signer._signTypedData(domain, ORDER_TYPES, orderToSign); ``` The sender script also verifies the legacy domain: ```js const domain = { name: 'SWAP', version: '4.2', chainId: 8453, verifyingContract: swapAddress, }; const recovered = ethers.utils.verifyTypedData(domain, ORDER_TYPES, order, signature); ``` ### Technical Analysis The Skill states that ERC20-to-ERC20 swaps use `SwapERC20` v4.3 at `0x95D598...`, with the `SWAP_ERC20` domain and `OrderERC20` schema. The executable scripts inst ...[truncated 1966 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Choose one supported workflow and make the implementation and documentation consistent. If ERC20 v4.3 is intended: 1. Change the ERC20 deployment to `0x95D598D839dE1B030848664960F0A20b848193F4`. 2. Implement the v4.3 ABI and `OrderERC20` typed-data schema. 3. Use EIP-712 domain name `SWAP_ERC20` and version `4.3`. 4. Calculate the protocol fee on the signer side. 5. Require signer balance and allowance for `signerAmount + signer protocol fee`. 6. Update compressed-order handling and execution to match the v4.3 contract. 7. Add integration tests asserting the contract address, chain ID, domain separator, schema, fee payer, and required allowances. If the legacy v4.2 route is intended: 1. Remove the contradictory v4.3 claims from `SKILL.md`. 2. Clearly identify the legacy deployment and sender-side fee model before any approval instructions. 3. Ensure all deployment references and README files present the same address and protocol version. In either case, print the selected deployment, protocol version, fee-paying side, and exact approval amount, and require explicit operator confirmation before signing or approving. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The description claims a comprehensive onchain P2P swap skill for Grand Bazaar on Base, including approvals, signing, posting/deeplinks, execution, and verification. The actual code chunk does none of that. It is a disabled script whose sole behavior is to print notices about security hardening and terminate. Its primary purpose is effectively to prevent execution of a previously existing posting tool, not to perform or document AirSwap swaps. This is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code is clearly related to the declared AirSwap/Grand Bazaar swap workflow: it uses Base RPC, deployed AirSwap swap contracts, performs EIP-712 signing, creates a deeplink-style compressed order, and documents output in JSON. However, the declared description claims broader functionality than this chunk actually implements. This script only prepares a signer-side order and approval flow. It does not post casts, execute the swap, or verify settlement. Although it detects ERC721/ERC1155 interfaces to select a swap contract, the implemented token interaction and amount parsing are ERC20-oriented ('ERC20 for now', decimals/parseUnits, ERC20 ABI), so the claimed full ERC721/ERC1155 route support is not accurately represented by this code chunk. Therefore this chunk is a partial implementation of the declared purpose, making the description materially broader than the actual behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The code is related to the declared domain: it interacts with a deployed swap contract on Base, performs ERC20 approvals, creates an EIP-712 signature, executes a swap, and verifies results via balance logging. However, the description claims a broader capability set than the code actually provides. This code chunk only supports a specific ERC20 WETH/USDC swap path and does not include any cast/deeplink posting or support for ERC721/ERC1155 routes. So while the general theme matches, the supplied code materially underdelivers relative to the declared description's breadth and workflow coverage.

Ae1

High
Category
analysis-evasion
Content
`make_cast_payload.js` writes
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
`make_cast_payload.js` writes
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
`make_cast_payload.js` writes
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Known Vulnerable Dependency: ws==8.18.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
92% confidence
Finding
The lockfile includes ws 8.18.0, which is reported as affected by uninitialized memory disclosure and memory-exhaustion denial-of-service issues. If the skill’s scripts or their dependencies open WebSocket connections to untrusted or semi-trusted peers/endpoints, these flaws could expose process memory or allow resource exhaustion. This is more concerning here because ethers providers commonly use ws for real-time blockchain connectivity, so the package may be reachable during normal networked operation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill references environment-based secret handling (`SENDER_PRIVATE_KEY`) and operational scripts, but it does not declare any explicit tool scope or permissions boundary. In an agent setting, missing scope declarations can cause the skill to be invoked with broader-than-intended capabilities, especially around secret access, which increases the chance of credential misuse or unsafe execution.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Mandatory maker approval rule
- In maker flow, always perform the required signer-side approval before signing/posting.
- Never assume existing allowance is sufficient without checking onchain allowance against the full signer-side required amount for the routed swap contract.
- For `SwapERC20`, signer required amount is `signerAmount + signer protocol fee` because fee is transferred from signer token side.
- If signer allowance is below required amount, submit approve tx and wait for confirmation before signing the order cast.
- Sender-side allowance must not block maker posting. Ignore `SenderAllowanceLow` in maker validation gates.
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill instructs operators to set `SENDER_PRIVATE_KEY` immediately before running an execution script, but it does not place a strong warning and safe-handling procedure at the point of use. In a high-value blockchain context, encouraging direct private-key injection into the runtime environment increases the risk of credential exposure through shell history, process inspection, logs, or misuse by other tools.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README instructs users to export raw private keys as environment variables but does not warn about the sensitivity of those secrets or safer handling practices. In agent, CI, shell-history, or shared workstation contexts, this can lead to credential exposure and direct theft of on-chain assets if keys are logged, inspected by other processes, or reused incorrectly.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README tells users to execute signed on-chain swap orders with a sender private key but does not prominently warn that this performs live blockchain transactions that can move assets irreversibly, incur gas costs, and approve token spending. In a swap skill handling ERC20/ERC721/ERC1155 transfers, missing transaction-risk warnings materially increases the chance of accidental loss from misuse, wrong network/configuration, or misunderstanding of approvals and execution behavior.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The script requires a raw private key from an environment variable and immediately uses it for transaction signing and EIP-712 signing without any safety guidance or safer key-management option. In an agent/automation context, this increases the risk that operators paste production keys into insecure environments, logs, shell history, or shared execution contexts, leading to wallet compromise.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest says the skill includes workflows across ERC20/ERC721/ERC1155 routes, but this file hardcodes ERC20 assumptions for key steps: environment comments label both assets as ERC20, token metadata is read via ERC20 ABI, amounts are parsed with decimals/parseUnits, and approval uses ERC20 approve(). Although it detects token kind to choose a swap contract, the actual order it signs fixes token IDs to 0 and uses ERC20-like amount handling, so the implemented behavior does not match the broader multi-token-route claim.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script automatically sends an on-chain approve() transaction if allowance is insufficient, with no interactive confirmation, simulation, or explicit operator warning. In a swap skill that handles real assets on Base, silent approval execution is dangerous because approvals authorize token transfer by the swap contract and could expose funds if parameters are wrong, the contract address is misconfigured, or the environment is compromised.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
This script will automatically submit live approval and swap transactions as soon as the required private keys are present, with no dry-run mode, interactive confirmation, chain/simulation safety check, or prominent warning about fund movement. In the context of a skill that documents repeatable on-chain swap workflows, that behavior materially increases the chance of accidental token approvals and unintended asset transfers if a user runs it with real credentials or misconfigured environment variables.

Vague Triggers

Low
Confidence
78% confidence
Finding
The manifest-style description says the skill 'Perform[s] and document[s] Grand Bazaar P2P swaps on Base' and lists many workflows, but it does not define explicit trigger phrases, boundaries, or exclusion conditions. For a markdown/manifest-style file, this broad capability description may be interpreted too loosely and cause unintended invocation for general swap, approval, signing, or verification requests.

Known Vulnerable Dependency: bn.js==5.2.2 — 1 advisory(ies): CVE-2026-2739 (bn.js affected by an infinite loop)

Low
Category
Supply Chain
Confidence
84% confidence
Finding
The lockfile pins bn.js 5.2.2, which the static analysis reports as affected by an infinite-loop denial-of-service issue. Even though this file is only a dependency manifest and not executable code, shipping a known vulnerable version is still a real supply-chain risk if any code path processes attacker-controlled numeric input through bn.js. In this skill’s context, the dependency is pulled in through ethers/elliptic rather than being intentionally introduced for abuse, so the issue appears accidental rather than malicious.

Known Vulnerable Dependency: elliptic==6.6.1 — 1 advisory(ies): CVE-2025-14505 (Elliptic Uses a Cryptographic Primitive with a Risky Implementation)

Low
Category
Supply Chain
Confidence
80% confidence
Finding
The lockfile includes elliptic 6.6.1, which is flagged for using a risky cryptographic implementation. Because elliptic is commonly used in wallet/signing stacks, weaknesses here can affect signature handling, reliability, or security assumptions in cryptographic operations. In this skill, which explicitly performs EIP-712 signing for blockchain swaps, the context makes crypto-library weaknesses more relevant than they would be in a non-cryptographic tool.

Known Vulnerable Dependency: bn.js==4.12.2 — 1 advisory(ies): CVE-2026-2739 (bn.js affected by an infinite loop)

Low
Category
Supply Chain
Confidence
85% confidence
Finding
A second vulnerable bn.js version, 4.12.2, is present as a nested dependency under elliptic. This creates the same potential denial-of-service exposure from pathological input, and nested vulnerable packages remain exploitable if reachable through application behavior. Given the skill’s blockchain tooling context, the main concern is service disruption or unreliable cryptographic processing rather than direct code execution.

Unpinned Dependencies

Low
Category
Supply Chain
Content
{
  "dependencies": {
    "ethers": "^5.8.0",
    "lz-string": "^1.5.0"
  }
}
Confidence
90% confidence
Finding
The dependency uses a caret range, which allows newer minor/patch releases of ethers to be installed over time. This creates supply-chain and reproducibility risk because builds may silently change and consume a compromised or breaking upstream release, which is relevant for a skill that prepares and signs blockchain swap transactions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
{
  "dependencies": {
    "ethers": "^5.8.0",
    "lz-string": "^1.5.0"
  }
}
Confidence
90% confidence
Finding
The dependency uses a caret range, so future installs may resolve to different lz-string versions than originally tested. Even though this is a lower-level utility package, unpinned versions increase supply-chain exposure and reduce reproducibility, which is undesirable in a workflow handling encoded swap data and transaction-related artifacts.

Intent-Code Divergence

Low
Confidence
82% confidence
Finding
The comments say `SIGNER_TOKEN` is 'ERC20 for now' and `SENDER_TOKEN` is 'ERC20 required by this Swap deployment', implying a limited constraint description. In practice, the code later treats both signer and sender tokens strictly as ERC20 by calling decimals(), symbol(), allowance(), approve(), and parseUnits() on both, which would not work for ERC721/ERC1155 assets.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The script writes a JSON order artifact to OUT_FILE/order.json, which is a file write affecting the local filesystem. The action is only disclosed after the write occurs, so there is no advance warning or comment at the output definition and write site describing that the script will persist generated order data locally.

Static analysis

No suspicious patterns detected.