Back to skill

Security audit

Cl Lp Rebalancer

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real DeFi LP automation tool, but it can move funds unattended and has under-scoped wallet, approval, credential, and dry-run safety controls.

Review this carefully before installing. Use only a dedicated wallet with limited funds, revoke allowances after testing, do not rely on DRY_RUN=true for safety, avoid unattended cron execution until transaction validation and confirmation gates are fixed, and provide only dedicated read-only or least-privilege credentials.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/cl_lp.py:574
Finding

Unscoped residual-position cleanup can redeem unrelated LP positions

Content
View full analysis
list[dict]: """Query all LP positions for this pool. Returns list of {tokenId, assets, value}.""" if not WALLET_ADDR: return [] data = onchainos_cmd( [ "defi", "position-detail", "--address", WALLET_ADDR, "--chain", POOL_CHAIN, "--platform-id", PLATFORM_ID, ], timeout=20, ) if not data or not data.get("ok") or not data.get("data"): return [] result = [] try: for platform in data["data"]: for wallet in platform.get("walletIdPlatformDetailList", []): for network in wallet.get("networkHoldVoList", []): for invest in network.get("investTokenBalanceVoList", []): for pos in invest.get("positionList", []): tid = str(pos.get("tokenId", "")) assets = pos.get("assetsTokenList", []) value = sum( float(a.get("currencyAmount", 0)) for a in assets ) result.append( {"tokenId": tid, "assets": assets, "value": value} ) except (KeyError, ValueError, TypeError): pass return result def cleanup_residual_positions(keep_token_id: str) -> int: """Redeem all positions except keep_token_id. Returns count of cleaned positions.""" positions = _query_all_positions() cleaned = 0 for pos in positions: tid = pos["tokenId"] if tid == keep_token_id or not tid: continue val = pos["value"] log(f" Cleaning resid ...[truncated 2195 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/cl_lp.py:1344
Finding

Unlimited token approval is granted to an unvalidated API-provided router

Content
View full analysis
bool: state = load_state() approved_routers = state.get("approved_routers", []) key = f"{token_addr}:{spender}".lower() if key in [r.lower() for r in approved_routers]: return True log(f"Approval needed for {token_addr[:10]}... to {spender[:10]}...") max_approval = ( "115792089237316195423570985008687907853269984665640564039457584007913129639935" ) data = onchainos_cmd( [ "swap", "approve", "--token", token_addr, "--amount", max_approval, "--chain", POOL_CHAIN, ] ) if not data or not data.get("ok") or not data.get("data"): log(f"Approve API failed: {json.dumps(data)[:200] if data else 'no response'}") return False approve_tx = data["data"][0] approve_tx["to"] = token_addr tx_hash, fail = _wallet_contract_call(approve_tx) if not tx_hash: log(f"Approval failed: {fail}") return False log(f"Approval TX: {tx_hash}") time.sleep(5) approved_routers.append(key) state["approved_routers"] = approved_routers save_state(state) return True ``` ```python tx = swap_data["data"][0]["tx"] # Approve if selling a token (not native ETH) if from_token.lower() != ETH_ADDR.lower(): router_addr = tx["to"] if not ensure_approval(from_token, router_addr, amount): return None, { "reason": "approval_failed", "detail": f"router {router_addr}", "retriable": True, } ``` ### Technical Analysis The spender address is derived directly from `tx["to"]` in a remote swap API response. The code does not verify th ...[truncated 1826 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
references/cl_lp.py:1066
Finding

Unvalidated remote transaction calldata is signed and broadcast

Content
View full analysis
bool: tx_list = _extract_tx_list(data) if not tx_list: log(f" {label}: empty tx list") return False for i, tx in enumerate(tx_list): tx_type = tx.get("callDataType", f"tx_{i}") to_addr = tx.get("to", "") calldata = tx.get("serializedData", "") or tx.get("data", "0x") value = tx.get("value", "0x0") if not to_addr or not calldata: log(f" {label} [{tx_type}]: missing to/data, skip") continue # Convert hex value to decimal wei value_wei = str(int(value, 16) if value.startswith("0x") else int(value)) broadcast_data = onchainos_cmd( [ "wallet", "contract-call", "--to", to_addr, "--chain", CHAIN_ID, "--input-data", calldata, "--amt", value_wei, ], timeout=60, ) if broadcast_data and broadcast_data.get("ok") and broadcast_data.get("data"): r = broadcast_data["data"] if isinstance(r, list): r = r[0] if r else {} tx_hash = r.get("txHash") or r.get("hash") or r.get("orderId") log(f" {label} [{tx_type}] broadcast OK: {tx_hash}") # Verify on-chain execution if tx_hash: confirmed = _verify_tx_receipt(tx_hash) if not confirmed: log(f" {label} [{tx_type}] TX REVERTED on-chain: {tx_hash}") return False else: detail = ( json.dumps(broadcast_data)[:200] if broadcast_data else "no response" ...[truncated 2735 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:398
Finding

Documented DRY_RUN mode is not implemented and can execute real transactions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
references/cl_lp.py:1904
Finding

Financial strategy state is explicitly stored with world-readable permissions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (37)

Tainted flow: 'req' from os.environ.get (line 2250, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · references/cl_lp.py (reported line 196)May include surrounding context.

python
data=payload,
            headers={"Content-Type": "application/json"},
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read())
        value = float(data.get("marginSummary", {}).get("accountValue", 0))
        # Also check spot balances

Tainted flow: 'spot_req' from os.environ.get (line 204, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · references/cl_lp.py (reported line 209)May include surrounding context.

python
data=spot_payload,
                headers={"Content-Type": "application/json"},
            )
            with urllib.request.urlopen(spot_req, timeout=10) as resp2:
                spot_data = json.loads(resp2.read())
            for bal in spot_data.get("balances", []):
                if bal.get("coin") == "USDC":

Tainted flow: 'req' from os.environ.get (line 2250, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

This code performs a signed Binance API request using locally sourced API credentials to query external account balances. Even though the destination is Binance, the skill expands beyond CL-LP management into access of unrelated exchange accounts, increasing blast radius and exposing sensitive cross-platform financial data within the agent workflow.

Content

Scanner excerpt · references/cl_lp.py (reported line 237)May include surrounding context.

python
).hexdigest()
        url = f"https://fapi.binance.com/fapi/v2/balance?{query}&signature={sig}"
        req = urllib.request.Request(url, headers={"X-MBX-APIKEY": BINANCE_API_KEY})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read())
        for asset in data:
            if asset.get("asset") == "USDT":

Tainted flow: 'req' from os.environ.get (line 2250, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The code sends notifications to Telegram using credentials harvested from environment or local config files, with message contents including portfolio values, prices, triggers, and operational state. This exports sensitive financial telemetry to an external third party without explicit in-file consent or scoping, increasing privacy and metadata leakage risk.

Content

Scanner excerpt · references/cl_lp.py (reported line 2231)May include surrounding context.

python
headers={"Content-Type": "application/json"},
    )
    try:
        urllib.request.urlopen(req, timeout=10)
        return True
    except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError) as exc:
        log(f"Telegram send error: {exc}")

Tainted flow: 'req' from os.environ.get (line 2250, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The code sends Discord notifications using a bot token loaded from env or local agent config, exporting strategy and portfolio state to an external service. Pulling credentials from shared local config and transmitting operational data without explicit consent makes this a real data-exposure issue in an agent setting.

Content

Scanner excerpt · references/cl_lp.py (reported line 2260)May include surrounding context.

python
},
        )
        try:
            urllib.request.urlopen(req, timeout=10)
            discord_ok = True
        except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError) as exc:
            log(f"Discord embed error: {exc}")

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 67)May include surrounding context.

md
# 2. 配置
cd ~/.openclaw/skills/cl-lp-rebalancer/references
cp ../.env.example .env    # 填入 API keys + 钱包地址
vi config.json             # 调整池参数

# 3. 测试(只读,安全)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 75)May include surrounding context.

md
# 2. 配置
cd ~/.openclaw/skills/cl-lp-rebalancer/references
cp ../.env.example .env    # 填入 API keys + 钱包地址
vi config.json             # 调整池参数

# 3. 测试(只读,安全)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 554)May include surrounding context.

md
# 2. 配置
cd ~/.openclaw/skills/cl-lp-rebalancer/references
cp ../.env.example .env    # 填入 API keys + 钱包地址
vi config.json             # 调整池参数

# 3. 测试(只读,安全)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 558)May include surrounding context.

md
# 2. 配置
cd ~/.openclaw/skills/cl-lp-rebalancer/references
cp ../.env.example .env    # 填入 API keys + 钱包地址
vi config.json             # 调整池参数

# 3. 测试(只读,安全)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The main declared purpose is largely accurate: the code clearly targets concentrated-liquidity LP management with adaptive ranges, trend-aware asymmetry, MTF analysis, and automated rebalance execution. The cached status behavior is also present. However, the description omits several materially relevant capabilities. Most notably, the code discovers and uses Discord/Telegram credentials to send alerts, aggregates balances from unrelated external platforms (Hyperliquid and Binance), and auto-switches/manages wallet accounts. It also contains a substantial risk-control subsystem (stop-loss, trailing stop, circuit breaker, auto-resume) that changes operational behavior beyond pure range optimization. These are not just internal implementation details; they are meaningful externally observable capabilities and resource accesses not represented in the declared description. Therefore this is a description/behavior mismatch, though the core LP rebalancer description itself is mostly correct.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The description presents a production LP management skill with broad autonomous functionality and analytics. The actual code chunk is a test file whose effective behavior is limited to validating a single rebalance condition: trigger only when price leaves the tick range. Even the comments/tests indicate prior volatility/time-decay triggers were removed, which directly conflicts with the declared emphasis on volatility-adaptive rebalancing. There is no chain interaction, no pool management workflow, no status subcommand, and no performance reporting. This is a material description-versus-behavior mismatch, not just an incomplete excerpt of a declared feature.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The execution path includes fee claims, full liquidity redemption, swaps, and redeposit, all of which are irreversible and financially sensitive, but standard rebalances do not require explicit user confirmation. In the context of an autonomous DeFi agent with shell/network access and wallet integration, this creates a substantial risk of unintended fund movement or loss from malformed inputs, stale prices, or misclassification.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

Automatically loading a local .env file causes the skill to ingest secrets from disk without any permission boundary or provenance check. In agent environments, this widens secret exposure because all loaded variables become available to the process and, later, many are forwarded to subprocesses.

Content

Scanner excerpt · references/cl_lp.py (reported line 24)May include surrounding context.

python
from datetime import datetime, timedelta, timezone
from pathlib import Path

# ── Load .env if present ────────────────────────────────────────────────────


def _load_env():

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The explicit read of a colocated .env file is part of a real credential-access path, not just a benign config read, because the file commonly contains API and messaging secrets used later by the skill. In this codebase, that becomes more dangerous due to subsequent use in subprocesses and external notifications.

Content

Scanner excerpt · references/cl_lp.py (reported line 28)May include surrounding context.

python
def _load_env():
    env_file = Path(__file__).parent / ".env"
    if env_file.exists():
        for line in env_file.read_text().splitlines():
            line = line.strip()

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
98% confidence
Finding

Copying the full process environment into subprocesses is a classic environment-harvesting problem in agent systems because it forwards all ambient secrets, not just the few intended API keys, to an external executable. If the CLI is compromised, misconfigured, or replaced, it gains access to every inherited secret and can combine that with wallet privileges.

Content

Scanner excerpt · references/cl_lp.py (reported line 359)May include surrounding context.

python
def onchainos_cmd(args: list[str], timeout: int = 30) -> dict | None:
    """Run onchainos CLI command, return parsed JSON."""
    env = os.environ.copy()
    env.setdefault("OKX_API_KEY", OKX_API_KEY)
    env.setdefault("OKX_SECRET_KEY", OKX_SECRET)
    env.setdefault("OKX_PASSPHRASE", OKX_PASSPHRASE)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · references/cl_lp.py (reported line 3332)May include surrounding context.

python
def reset():
    """Reset state, close position if active."""
    state = load_state()
    position = state.get("position")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README describes a strategy that can automatically claim fees, remove liquidity, swap assets, and redeploy capital on-chain, but it does not prominently warn that these are live fund-moving operations with trading, slippage, impermanent loss, and smart-contract risk. Users may infer from the presentation that this is a routine optimization workflow and underestimate that enabling the skill can materially change holdings and incur irreversible losses.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The cron example schedules unattended execution every 5 minutes, effectively enabling continuous automated trading and liquidity changes without a prominent warning that it will submit recurring live transactions. This is dangerous because users may copy-paste the setup and unknowingly authorize repeated fund-moving behavior, compounding losses from bugs, bad parameters, volatile markets, or compromised environment configuration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Listing a close command without warning obscures that it may fully exit an LP position, remove liquidity, claim fees, and leave the user with materially different token balances than before. A user may invoke it casually for troubleshooting or cleanup and trigger irreversible portfolio changes, taxable events, or loss of market exposure.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares broad operational behavior including shell execution, file writes, network/API access, and environment-variable use, but does not declare any explicit tool scope or permissions boundary. In an agent setting, this increases the chance that the skill is invoked with more capability than users expect, enabling unintended command execution, wallet operations, or local state modification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description is entirely in Chinese and does not indicate language choice or multilingual support. This can violate language/locale policy where skills should not force a specific language absent user opt-in or documented regional justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill is designed to perform irreversible on-chain financial actions, yet it does not present a general warning for normal operation beyond the meme-coin case. Users may trigger value-moving transactions without understanding that claim/redeem/swap/deposit actions are final, can incur slippage/gas costs, and can realize losses.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill reads Discord and Telegram tokens from unrelated local agent config directories and auto-uses whichever it finds. Harvesting credentials from shared config outside the skill's own permission boundary is dangerous because it can silently inherit messaging authority and exfiltrate sensitive runtime data.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill broadens from Uniswap LP automation into aggregating Hyperliquid and Binance balances, which are unrelated platforms requiring additional identifiers and credentials. This unnecessary scope expansion increases the amount of sensitive financial data touched by the skill and raises the consequences of compromise or misconfiguration.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/cl_lp.py (reported line 192)May include surrounding context.

python
{"type": "clearinghouseState", "user": HL_WALLET_ADDR}
        ).encode()
        req = urllib.request.Request(
            "https://api.hyperliquid.xyz/info",
            data=payload,
            headers={"Content-Type": "application/json"},
        )

Static analysis

No suspicious patterns detected.