Back to skill

Security audit

Credex Protocol

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-built for Credex lending, liquidity, and bridging, but it handles wallet signing authority in ways that need review before installation.

Install only if you are comfortable reviewing and constraining DeFi transaction tooling. Use an isolated low-value test wallet, avoid exposing production private keys, prefer the locked npm scripts over documented npx ts-node commands, verify the exact pool contract and chain before any approval/deposit/repay/bridge action, and require explicit user confirmation for every fund-moving command.

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

T08 · Insecure Dependencies

Error
Location
SKILL.md:14
Finding
Documented commands execute an undeclared package through npx<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:14-18`, `README.md:28-46`, `package.json:22-28` **Vulnerability Type**: Unsafe runtime dependency resolution **Risk Level**: High ### Vulnerable Code ```bash cd {baseDir} npx ts-node scripts/client.ts <command> [args] # Borrower commands npx ts-node scripts/lp.ts <command> [args] # LP commands ``` The README repeats this pattern for all financial operations: ```bash npx ts-node scripts/client.ts status npx ts-node scripts/client.ts borrow 5 npx ts-node scripts/client.ts repay all npx ts-node scripts/client.ts bridge 10 arc base npx ts-node scripts/lp.ts pool-status npx ts-node scripts/lp.ts deposit 100 npx ts-node scripts/lp.ts withdraw all ``` However, `ts-node` is not declared in the package manifest: ```json "dependencies": { "@circle-fin/adapter-viem-v2": "^1.4.0", "@circle-fin/bridge-kit": "^1.5.0", "dotenv": "^17.2.4", "ethers": "^6.16.0" }, "devDependencies": { "tsx": "^4.21.0", "typescript": "^5.9.3" } ``` ### Technical Analysis The Skill instructs users and agents to execute `npx ts-node`, but `ts-node` is neither a direct dependency nor represented as the intended local executable in the reviewed manifest. When no local binary is available, `npx` may resolve, download, and execute a package from the configured npm registry at invocation time. This bypasses the reviewed lockfile as the authoritative source for the command runner. It creates a mutable supply-chain execution path inside a process that is explicitly expected to have access to `WALLET_PRIVATE_KEY`. The project already defines local scripts using the locked `tsx` dependency: ```json "scripts": { "client": "npx tsx scripts/client.ts", "lp": "npx tsx scripts/lp.ts" } ``` Therefore, retrieving a separate runner at invocation time is not necessary for the declared functionality. ### Attack Path 1. A user exports `WALLET_PRIVATE_KEY` as required by the Skill. 2. The user or AI ag ...[truncated 1381 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace all documented `npx ts-node` commands with scripts that execute the installed, lockfile-controlled runner: ```bash npm run client -- status npm run client -- borrow 5 npm run lp -- pool-status ``` 2. Avoid invoking `npx` from within package scripts. Define the scripts as: ```json "scripts": { "client": "tsx scripts/client.ts", "lp": "tsx scripts/lp.ts" } ``` npm automatically resolves binaries from `node_modules/.bin`. 3. Install dependencies using `npm ci` so versions and integrity hashes come from `package-lock.json`. 4. Pin security-sensitive dependencies to exact versions instead of permissive caret ranges where operationally feasible. 5. If `ts-node` is intentionally required, add an exact version to `devDependencies`, regenerate the lockfile, and execute only the local binary. 6. Run transaction tooling in a constrained environment with minimal filesystem and network access. Do not expose production-value keys to package installation or dependency-resolution processes. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/client.ts:69
Finding
Read-only commands unnecessarily load a signing private key<![CDATA[ ## Vulnerability Details **File Location**: `scripts/client.ts:69-79`, `scripts/client.ts:104-106`, `scripts/client.ts:248-252`, `scripts/lp.ts:72-82`, `scripts/lp.ts:94-96`, `scripts/lp.ts:203-205` **Vulnerability Type**: Excessive credential access and violation of least privilege **Risk Level**: Medium ### Vulnerable Code Both command-line tools construct a signer whenever `getWallet()` is called: ```ts function getWallet(): Wallet { const pk = process.env.WALLET_PRIVATE_KEY; if (!pk) { error("WALLET_PRIVATE_KEY required", { hint: "Set WALLET_PRIVATE_KEY environment variable before running commands", }); } const provider = new JsonRpcProvider(CONFIG.RPC_URL); return new Wallet(pk!, provider); } ``` The signer is then used for read-only status queries: ```ts async function checkStatus(address: string): Promise<void> { const wallet = getWallet(); const pool = new Contract(CONFIG.POOL_ADDRESS, POOL_ABI, wallet); try { const [ debtRaw, principalRaw, creditLimitRaw, , lastRepayment, frozen, active, ] = await pool.getAgentState(address); const availableRaw = await pool.availableCredit(address); ``` Balance checks also unnecessarily instantiate the wallet signer: ```ts async function checkBalance(): Promise<void> { const wallet = getWallet(); try { const arcUsdc = new Contract(CONFIG.USDC_ARC, ERC20_ABI, wallet); const arcBalanceRaw = await arcUsdc.balanceOf(wallet.address); ``` The LP script follows the same pattern for read-only pool queries: ```ts async function poolStatus(): Promise<void> { const wallet = getWallet(); const pool = new Contract(CONFIG.POOL_ADDRESS, POOL_ABI, wallet); try { const [liquidityRaw, assetsRaw, sharesRaw] = await Promise.all([ pool.totalLiquidity(), pool.totalAssets(), pool.totalShares(), ]); ``` ### Technical Analysis Blockchain view calls only require an RPC provider and a public accou ...[truncated 2009 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Separate provider-only and signer-enabled initialization: ```ts function getProvider(): JsonRpcProvider { return new JsonRpcProvider(CONFIG.RPC_URL); } function getSigner(): Wallet { const pk = process.env.WALLET_PRIVATE_KEY; if (!pk) { throw new Error("WALLET_PRIVATE_KEY required for write operations"); } return new Wallet(pk, getProvider()); } ``` 2. Use `getProvider()` for `status`, `pool-status`, `lp-balance`, and all public balance queries. 3. Accept a public wallet address through a command argument or a separate non-secret variable such as `WALLET_ADDRESS`. 4. Only read `WALLET_PRIVATE_KEY` immediately before a transaction that genuinely requires a signature. 5. Split read-only and write-capable commands into separate processes or entry points where practical. 6. Document that private keys should use isolated, low-value wallets and should never be reused across production and test environments. 7. Prefer external signers, hardware wallets, or narrowly scoped signing services over raw private keys in process environment variables. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
README.md:18
Finding
Conflicting pool addresses can redirect approvals and financial transactions<![CDATA[ ## Vulnerability Details **File Location**: `README.md:18-23`, `SKILL.md:38-42`, `scripts/client.ts:36-43`, `scripts/lp.ts:36-43` **Vulnerability Type**: Unsafe and inconsistent transaction-target configuration **Risk Level**: High ### Vulnerable Code The README instructs users to override the pool address with: ```bash export WALLET_PRIVATE_KEY=0x... export CREDEX_POOL_ADDRESS=0x60C04c09ee252C4e99C1B56580F7A0D3c65a3b36 export RPC_URL=https://rpc.testnet.arc.network ``` The Skill documentation identifies a different default address: ```md | `CREDEX_POOL_ADDRESS` | Pool contract address | `0x32239e52534c0b7e525fb37ed7b8d1912f263ad3` | ``` The scripts accept the environment override without validating the deployed contract or network: ```ts const CONFIG = { RPC_URL: process.env.RPC_URL || "https://rpc.testnet.arc.network", BASE_RPC_URL: "https://sepolia.base.org", POOL_ADDRESS: getAddress( process.env.CREDEX_POOL_ADDRESS || "0x32239e52534c0b7e525fb37ed7b8d1912f263ad3", ), USDC_ARC: "0x3600000000000000000000000000000000000000", USDC_BASE: getAddress("0x036CbD53842c5426634e7929541eC2318f3dCF7e"), }; ``` The selected address receives token approval during repayment: ```ts const approveTx = await usdc.approve( CONFIG.POOL_ADDRESS, parseUsdc(repayAmount), ); await approveTx.wait(); ``` It also receives approval and deposit calls from LP operations: ```ts const approveTx = await usdc.approve(CONFIG.POOL_ADDRESS, amountWei); await approveTx.wait(); const depositTx = await pool.deposit(amountWei); const receipt = await depositTx.wait(); ``` ### Technical Analysis The README and the rest of the project identify different Credex pool addresses. A user following the README overrides the address consistently documented and embedded elsewhere. `getAddress()` only normalizes and validates address syntax. It does not establish that the address is the intended Credex contract, belongs to Arc Testnet, has expected bytecode, or e ...[truncated 2023 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Establish a single authoritative, verified pool address and use it consistently in `README.md`, `SKILL.md`, scripts, and contract references. 2. Verify the connected network before every state-changing operation: ```ts const network = await provider.getNetwork(); if (network.chainId !== 1328n) { throw new Error(`Unexpected chain ID: ${network.chainId}`); } ``` 3. Retrieve contract bytecode using `provider.getCode(address)` and reject addresses with no deployed code. 4. Where possible, compare the deployed bytecode hash or a protocol-specific immutable identifier against an expected value. 5. Display the chain ID, contract address, token address, approval amount, and operation before requesting a signature. 6. Require explicit user confirmation for deposits, repayments, withdrawals, and bridges when used interactively. 7. Reject arbitrary pool-address overrides by default. If overrides are needed for development, require an explicit unsafe-development flag and clearly label the resulting output. 8. Inspect the current allowance after an operation and provide a command to revoke residual allowances. Consider resetting allowance to zero after failed coordinated operations. 9. Add automated tests that assert all documentation and source files reference the same verified deployment address. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (82)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code substantially matches several core declared functions: check credit status, borrow, repay, and bridge USDC cross-chain on Arc/Base. However, the description explicitly claims liquidity-provider and pool-deposit capabilities ('providing liquidity as an LP', 'deposit to pool', 'provide liquidity'), and no such command or contract interaction exists in the supplied code. The code is strictly a borrower/client CLI plus balance checking. That makes the declared description materially broader than the actual implemented behavior. The extra balance-check feature is minor but undeclared; the main mismatch is the missing liquidity/deposit capability.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The code is narrowly scoped to LP operations and balance/bridge utilities. It interacts with a pool contract for deposits, withdrawals, pool metrics, and LP share balances, and uses Circle BridgeKit to move USDC between Arc testnet and Base Sepolia. There is no code for opening unsecured credit lines, borrowing USDC, repaying debt, or querying credit/reputation status. Because the declared description presents borrowing/repayment/credit-management as core supported behaviors, while the implementation only supports liquidity provision plus limited bridging/balance checks, the description does not accurately represent the actual behavior.

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
npx ts-node scripts/client.ts <command> [args] # Borrower commands
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Known Vulnerable Dependency: bigint-buffer==1.1.5 — 1 advisory(ies): CVE-2025-3194 (bigint-buffer Vulnerable to Buffer Overflow via toBigIntLE() Function)

High
Category
Supply Chain
Confidence
92% confidence
Finding
The lockfile pins bigint-buffer 1.1.5, which is reported as vulnerable to a buffer overflow in toBigIntLE(). Even though this package is transitive, memory-safety flaws in native or buffer-handling code are real supply-chain risk, and this skill operates in a financial/crypto context where malformed external data may be processed from chains or RPC responses. The issue appears reachable through Solana-related dependencies, so it should be treated as a true dependency vulnerability.

Known Vulnerable Dependency: ws==7.5.10 — 1 advisory(ies): CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
84% confidence
Finding
ws 7.5.10 is flagged for memory exhaustion DoS from fragmented frames. This instance is pulled in transitively via jayson, and while not every deployment will expose a WebSocket server, blockchain/RPC tooling commonly maintains persistent socket connections, making availability issues more relevant. In an agent skill handling financial operations, a DoS can disrupt repayment, borrowing, or bridge monitoring workflows.

Known Vulnerable Dependency: toml==3.0.0 — 2 advisory(ies): CVE-2026-77465 (toml-node: Uncontrolled Recursion); CVE-2026-63376 (toml-node: Prototype Pollution Leads to `Object.prototype` Corruption via `__pro)

High
Category
Supply Chain
Confidence
87% confidence
Finding
toml 3.0.0 is flagged for uncontrolled recursion and prototype pollution. Although transitive via Anchor tooling, parser bugs of this type are meaningful when configuration or metadata files may come from external or semi-trusted sources; prototype pollution can have broad and unpredictable effects in JavaScript runtimes. In a crypto agent context, corrupted object state or parser-triggered DoS could interfere with transaction preparation or environment handling.

Known Vulnerable Dependency: ws==8.18.3 — 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
89% confidence
Finding
ws 8.18.3 is flagged for uninitialized memory disclosure and memory exhaustion DoS. This version appears under viem, a core blockchain interaction library likely relevant to the skill's runtime behavior, so the affected WebSocket paths may be materially reachable if the skill uses websocket transports for chain or bridge monitoring. Exposure is especially concerning in a lending/bridging skill because leaked process memory or degraded availability can affect keys, session data, or critical transaction timing.

Known Vulnerable Dependency: ws==8.17.1 — 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
90% confidence
Finding
ws 8.17.1 is also flagged for uninitialized memory disclosure and memory exhaustion DoS, here through ethers. Since ethers is a direct dependency of the skill and may use WebSocket providers in production blockchain workflows, this instance is more plausibly reachable than a dormant transitive package. In a finance-oriented agent, memory disclosure and service interruption raise the stakes because secrets, provider state, or transaction operations may be affected.

Missing User Warnings

High
Confidence
97% confidence
Finding
The bridge example demonstrates constructing an adapter directly from a private key and initiating cross-chain USDC transfers, but it does not warn about secret exposure, signer compromise, or the fact that bridging moves real assets across networks and may be irreversible or operationally complex. In this skill's context, which encourages autonomous borrowing, repayment, liquidity provision, and cross-chain fund management, such omission materially increases the risk of unsafe key handling and unintended asset transfer by users or agents.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README tells users to export a wallet private key and then immediately run borrowing, repayment, and bridge commands, but it provides no warning that the key is highly sensitive or that these actions can move real funds and incur irreversible on-chain transactions. In a financial skill centered on unsecured credit and cross-chain transfers, this omission materially increases the chance of credential exposure, accidental fund movement, and unsafe operation by users or downstream agents.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill instructs use of environment variables and networked transaction scripts, but it does not declare any explicit tool scope such as allowed tools or permissions. In an agent setting, that increases the chance the skill is granted broader-than-necessary access to secrets and network operations, which is especially sensitive because it handles a wallet private key and can submit blockchain transactions.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger condition includes broad phrasing such as activation for 'any credit/lending task on Arc', which can cause the skill to run in contexts the user did not specifically intend. Because the skill can invoke networked scripts and potentially submit transactions using a configured private key, overbroad activation materially raises the risk of accidental fund movement or unnecessary exposure of sensitive wallet context.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/client.ts:35