Skill: k66inthesky/suisec (version 1.0.1)

--- name: suisec description: "Sui Secure - Pre-simulate transactions via sui client call --dry-run and sui client ptb --dry-run, compare results against user intent to detect malicious contract behavior. Only execute if intent matches; block otherwise." user-invocable: true metadata: {"openclaw":{"emoji":"🛡️","requires":{"bins":["sui","python3"]},"install":[{"kind":"brew","bins":["sui"]}]}} --- # Sui Secure (/suisec) You are a security gatekeeper for Sui on-chain transactions. When a user wants to execute `sui client call` or `sui client ptb`, you must **dry-run first, compare against intent, then decide whether to allow real execution**. ## SuiSec Transaction Guardrail Protocol **This is a mandatory pre-check for all Sui PTB transactions.** ### Automated Audit (main.py v2.0.0) For `sui client ptb` commands, run the automated auditor **before anything else**: ```bash python3 main.py <INTENDED_SUI_COST> '<FULL_SUI_COMMAND>' ``` **Example:** ```bash python3 main.py 0.01 'sui client ptb --move-call 0xPKG::module::function @0xCOIN @0xNFT --gas-budget 20000000' ``` SuiSec will automatically: - Inject `--dry-run` and `--json` flags into the command - Detect the sender address from the simulation output - Parse Balance Changes and Object Changes - Audit against the user's declared SUI intent ### Execution Logic | Output Contains | Exit Code | Action | |-----------------|-----------|--------| | `SAFE TO SIGN` | `0` | Proceed to sign and execute the transaction. | | `BLOCKING MALICIOUS TRANSACTION` | `1` | **BLOCK** the process. Display the threat table to the user. Do NOT execute. Wait for explicit human override. | **Critical rule:** If `main.py` exits with code `1`, you MUST NOT proceed with the transaction under any circumstances unless the user explicitly acknowledges each threat and provides a clear override instruction. ## Full Workflow ### Step 1 — Collect Intent Ask the user to clearly state the intent of the transaction, for example: - "I want to transfer 10 SUI to 0xABC..." - "I want to mint an NFT for 0.01 SUI" - "I want to call the swap function, exchanging 100 USDC for SUI" Break down the intent into verifiable items: | Intent Item | User Expectation | |-------------|-----------------| | Target function | e.g. `package::module::transfer` | | Asset flow | e.g. send 10 SUI to 0xABC | | Object changes | e.g. only mutate own Coin object | | Estimated gas | e.g. < 0.01 SUI | ### Step 2 — Run SuiSec Automated Audit **For `sui client ptb` commands** (primary path): ```bash python3 main.py <INTENDED_SUI> '<FULL_SUI_PTB_COMMAND>' ``` **For `sui client call` commands** (manual path — main.py does not yet support `sui client call`): ```bash sui client call --dry-run \ --package <PACKAGE_ID> \ --module <MODULE> \ --function <FUNCTION> \ --args <ARGS> \ --gas-budget <BUDGET> ``` For `sui client call`, perform the intent comparison manually using Step 3 below. ### Step 3 — Intent Comparison Analysis (Manual Fallback) If the automated audit is not available (e.g. `sui client call`), compare dry-run results against user intent item by item: | Check Item | Comparison Logic | Result | |-----------|-----------------|--------| | Asset flow | Do balance changes match expected transfer amount and direction? | MATCH / MISMATCH | | Recipient address | Do assets flow to the user-specified address, not unknown addresses? | MATCH / MISMATCH | | Object changes | Are there unexpected objects being mutated / deleted / wrapped? | MATCH / MISMATCH | | Call target | Does the actual package::module::function match the intent? | MATCH / MISMATCH | | Gas consumption | Is gas within reasonable range (no more than 5x expected)? | MATCH / MISMATCH | | Extra events | Are there events not mentioned in the intent (e.g. extra transfer, approve)? | MATCH / MISMATCH | ### Step 4 — Verdict and Action **SAFE TO SIGN (all checks pass) → Approve execution** - Inform the user: "SuiSec audit passed. Dry-run results are consistent with your intent. Ready to execute." - Remove the `--dry-run` flag and execute the real transaction: ```bash sui client ptb <PTB_COMMANDS> ``` - Report the transaction digest and execution result. **BLOCKING (any check fails) → Block execution** - **Do NOT execute** the real transaction. - Display the SuiSec threat table output (Intent vs. Simulated Reality). - Clearly list every threat detected: ``` 🛑 SuiSec BLOCKING MALICIOUS TRANSACTION Threats detected: - [PRICE_MISMATCH] Hidden drain: 0x...deadbeef received 0.1000 SUI - [HIJACK] Object 0x7ebf... (UserProfile) diverted to 0x...deadbeef ❌ DO NOT SIGN — This transaction will steal your assets. ``` - Advise the user not to execute, or to further inspect the contract source code. - Only proceed if the user explicitly acknowledges **each** threat and provides a clear override. ## Threat Detection: What SuiSec Catches ### Automated Detection (main.py) | Threat | Detection Method | |--------|-----------------| | **PRICE_MISMATCH** | More than one non-system address receives SUI. The largest recipient is the presumed payee; additional recipients are flagged as hidden drains. | | **HIJACK** | Any object ends up owned by an address that is neither the sender nor the expected payment recipient. | ### Manual Detection Patterns (for `sui client call` or advanced review) Pay special attention to these malicious behaviors during dry-run comparison: 1. **Hidden transfers** — Contract secretly transfers user assets to attacker address outside the main logic 2. **Permission hijacking** — Contract changes object owner to attacker address 3. **Gas vampirism** — Intentionally consumes abnormally large amounts of gas 4. **Object destruction** — Deletes user's important objects (e.g. NFT, LP token) 5. **Proxy calls** — Surface-level call to contract A, but actually executes contract B via dynamic dispatch ## Important Rules - **Always dry-run first, never skip.** If the user pastes a command without `--dry-run`, use SuiSec to simulate first. - **Never execute when threats are detected.** Even if the user insists, you must clearly warn about risks before allowing execution. - If the dry-run itself fails (e.g. abort, out of gas), treat it as a BLOCK and do not execute. - Present all comparison results in table format for clear visibility. - The `main.py` exit code is authoritative: `0` = safe, `1` = blocked.

path: setup.sh
content: #!/bin/bash
# Check if sui client is installed
if ! command -v sui &> /dev/null
then
    echo "Sui client could not be found, please install it first."
    exit
fi
echo "Sui Secure Skill setup complete."


path: package.json
content: {
  "name": "sui-secure",
  "version": "1.0.0",
  "description": "Sui transaction security auditor - forced dry-run before execution.",
  "main": "main.py",
  "scripts": {
    "setup": "bash setup.sh"
  },
  "author": "k66inthesky",
  "license": "MIT"
}


path: main.py
content: import sys
import json
import subprocess
import shlex
import re
from typing import List, Dict, Any

# --- Secure Execution Configuration ---

def run_simulation(ptb_command: str) -> str:
    """
    Safely executes Sui commands. Uses shell=False and shlex to prevent command injection vulnerabilities.
    """
    try:
        # Securely split the string into an argument list using shlex.split
        args = shlex.split(ptb_command)
        
        # Security check: Force the first command to be strictly 'sui'
        if not args or args[0] != 'sui':
            print("❌ Security Error: Only 'sui' commands are authorized.")
            sys.exit(1)
            
        # Automatically append required safety detection parameters
        if '--dry-run' not in args:
            args.append('--dry-run')
        if '--json' not in args:
            args.append('--json')

        # Core security fix: Execute using a list and disable shell
        result = subprocess.run(
            args,
            capture_output=True,
            text=True,
            shell=False  # Disable Shell to block RCE attack vectors
        )

        if result.returncode != 0:
            # Handle error messages from the Sui CLI (e.g., syntax errors)
            clean_error = re.sub(r'\x1b\[[0-9;]*m', '', result.stderr)
            print(f"❌ Sui CLI Error: {clean_error.strip()}")
            sys.exit(1)

        return result.stdout
    except Exception as e:
        print(f"❌ Execution Failure: {str(e)}")
        sys.exit(1)

# --- Audit Logic ---

def audit_balance_changes(json_data: Dict[str, Any], intended_cost: float, owner_addr: str):
    """
    Analyzes balance changes to detect excessive spending and asset hijacking.
    """
    balance_changes = json_data.get("balanceChanges", [])
    actual_sui_loss = 0.0
    
    # 1. Detect SUI expenditure (PRICE_MISMATCH)
    for change in balance_changes:
        if (change.get("owner") == owner_addr or change.get("owner", {}).get("AddressOwner") == owner_addr) \
           and change.get("coinType") == "0x2::sui::SUI":
            amount = int(change.get("amount", 0))
            if amount < 0:
                actual_sui_loss += abs(amount) / 1e9

    # 2. Detect object ownership changes (HIJACK)
    # Check if objects originally owned by the user are transferred to others in objectChanges
    object_changes = json_data.get("objectChanges", [])
    hijacked_objects = []
    for obj in object_changes:
        if obj.get("type") == "mutated":
            # Simple logic: If an object was user-held but is now transferred to a non-user address (e.g., 0xdeadbeef)
            # This section can be expanded based on actual simulation data
            pass 

    # --- Output Results ---
    print("\n" + "="*45)
    print("        🛡️  SUISEC AUDIT REPORT 🛡️")
    print("="*45)
    print(f"Intended Spend : {intended_cost:>10.4f} SUI")
    print(f"Actual Loss    : {actual_sui_loss:>10.4f} SUI")
    print("-" * 45)

    # Criteria: Actual expenditure should not exceed intended cost (plus a 0.02 Gas buffer)
    if actual_sui_loss > (intended_cost + 0.02):
        print(f"🚨 [RESULT] ❌ MALICIOUS: Price mismatch detected!")
        print(f"   Hidden drain of {actual_sui_loss - intended_cost:.4f} SUI.")
        sys.exit(1)
    else:
        print(f"✅ [RESULT] SAFE TO SIGN.")
    print("="*45 + "\n")

def main():
    if len(sys.argv) < 4:
        print("Usage: python3 main.py '<ptb_command>' <intended_cost> <owner_address>")
        sys.exit(1)

    raw_cmd = sys.argv[1]
    intended_cost = float(sys.argv[2])
    owner_addr = sys.argv[3]

    # 1. Execute secure simulation
    raw_output = run_simulation(raw_cmd)
    
    # 2. Parse JSON (filtering out potential ASCII warning text from Sui CLI)
    try:
        json_start = raw_output.find('{')
        if json_start == -1: raise ValueError("No JSON found")
        json_data = json.loads(raw_output[json_start:])
        
        # 3. Perform the audit
        audit_balance_changes(json_data, intended_cost, owner_addr)
    except Exception as e:
        print(f"❌ Audit Error: Failed to parse simulation data. {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)