smart-contract-security-review

📁 scalus3/scalus 📅 12 days ago
1
总安装量
1
周安装量
#54204
全站排名
安装命令
npx skills add https://github.com/scalus3/scalus --skill smart-contract-security-review

Agent 安装分布

codex 1
claude-code 1

Skill 文档

Smart Contract Security Review

Analyze Scalus/Cardano smart contracts for security vulnerabilities.

Target Code Identification

Find on-chain code by searching for:

  1. Objects/classes with @Compile annotation
  2. Objects extending Validator, DataParameterizedValidator, or ParameterizedValidator
  3. Objects compiled with PlutusV3.compile(), PlutusV2.compile(), or PlutusV1.compile()

Search patterns:

grep -rn "@Compile" --include="*.scala" <path>
grep -rn "extends Validator" --include="*.scala" <path>
grep -rn "extends DataParameterizedValidator" --include="*.scala" <path>

Workflow

  1. Discovery: Find all @Compile annotated code in specified path
  2. Classification: Identify validator type (spend/mint/reward/certify/vote/propose)
  3. Analysis: Check each validator against vulnerability checklist
  4. False Positive Verification: For each potential issue, verify it’s not a false positive
  5. Reporting: Generate structured report with severity levels (only verified issues)
  6. Remediation: Use TodoWrite to track issues, fix one-by-one with user confirmation

Vulnerability Checklist

Based on Cardano Developer Portal security guidelines and Scalus-specific patterns. For detailed patterns and code examples, see references/vulnerabilities.md.

Critical Severity

ID Name Risk Detection
V001 Redirect Attack Funds stolen via output redirection outputs.at(idx) without address validation
V002 Token/NFT Not Verified State token excluded Missing quantityOf on outputs
V003 Inexact Burn/Mint Extra tokens minted >= instead of === for quantities
V004 Integer Overflow Arithmetic overflow Custom encoding without bounds
V005 Double Satisfaction Pay once, satisfy many outputs.exists without unique linking

High Severity

ID Name Risk Detection
V006 Index Validation Missing Invalid index access .at(idx) without bounds check
V007 Self-Dealing/Shill Bidding Price manipulation No seller/bidder separation
V008 Double Spend via Index Same UTxO processed twice Index lists without uniqueness
V009 Inexact Refund Amount Fund manipulation >= for refunds instead of ===
V010 Other Redeemer Attack Bypass via different redeemer Multiple script purposes
V011 Other Token Name Attack Unauthorized token minting Policy doesn’t check all tokens
V012 Missing UTxO Authentication Fake UTxO injection No auth token verification
V025 Oracle Data Validation Price manipulation, stale data Oracle data without signature/freshness

Medium Severity

ID Name Risk Detection
V013 Time Handling Time manipulation Incorrect interval bounds
V014 Missing Signature Unauthorized actions No isSignedBy checks
V015 Datum Mutation Unauthorized state change No field comparison
V016 Insufficient Staking Control Reward redirection No staking credential check
V017 Arbitrary Datum Unspendable UTxOs No datum validation
V024 Parameterization Verification Script substitution (varies) ParameterizedValidator with auth params, no token

Low Severity / Design Issues

ID Name Risk Detection
V018 Unbounded Value UTxO size limit Unlimited tokens in output
V019 Unbounded Datum Resource exhaustion Growing datum size
V020 Unbounded Inputs TX limit exceeded Many required UTxOs
V021 UTxO Contention / Concurrency DoS Bottleneck, DoS Shared global state, no rate limit
V022 Cheap Spam/Dust Operation obstruction No minimum amounts
V023 Locked Value Permanent lock Missing exit paths

False Positive Verification

CRITICAL: Before reporting ANY vulnerability, you MUST verify exploitability by tracing code execution with a concrete attack transaction.

Verification Method: Attack Transaction Tracing

For each potential vulnerability:

  1. Construct a concrete attack transaction

    • Define specific inputs (UTxOs with concrete values/datums)
    • Define the redeemer values
    • Define the outputs the attacker would create
    • Define signatories
  2. Execute the validator logic mentally with this transaction

    • Go line-by-line through the validator code
    • Track what each variable evaluates to with your attack tx
    • Check EVERY require() statement – does it pass or fail?
  3. If ANY require fails, the attack fails → False Positive

  4. Only report if ALL requires pass with the attack transaction

Example: V005 Double Satisfaction Verification

Potential vulnerability detected: handlePay uses getAdaFromOutputs(sellerOutputs) without unique linking.

Construct attack transaction:

Inputs:
  - EscrowA: 12 ADA, datum={seller=S, buyer=B, escrowAmount=10, initAmount=2}
  - EscrowB: 12 ADA, datum={seller=S, buyer=B, escrowAmount=10, initAmount=2}
Outputs:
  - 12 ADA to seller S (single output for both!)
  - 1 ADA to buyer B
Signatories: [B]

Trace execution for EscrowA:

// Line 57-58:
val contractInputs = txInfo.findOwnInputsByCredential(contractAddress.credential)
// → Returns [EscrowA, EscrowB] (both have same script credential)

val contractBalance = Utils.getAdaFromInputs(contractInputs)
// → 12 + 12 = 24 ADA

// Line 118-120 in handlePay:
require(contractBalance === escrowDatum.escrowAmount + escrowDatum.initializationAmount)
// → require(24 === 10 + 2)
// → require(24 === 12)
// → FAILS! ❌

Result: Attack transaction fails at line 118-120. This is a FALSE POSITIVE.

When to Write a Test

If the attack trace is complex or you’re uncertain, write an actual test:

test("V005: Double satisfaction attack should fail") {
  // Setup: Create two escrow UTxOs with same seller
  val escrowA = createEscrowUtxo(seller = S, buyer = B, amount = 10.ada)
  val escrowB = createEscrowUtxo(seller = S, buyer = B, amount = 10.ada)

  // Attack: Try to spend both with single output to seller
  val attackTx = Transaction(
    inputs = List(escrowA, escrowB),
    outputs = List(TxOut(sellerAddress, 12.ada)),  // Only pay once!
    redeemers = Map(escrowA -> Pay, escrowB -> Pay)
  )

  // Verify: Should this pass or fail?
  // If it passes → Real vulnerability
  // If it fails → False positive
  evaluateValidator(EscrowValidator, escrowA, attackTx) shouldBe failure
}

Verification Checklist

Before reporting, answer these questions:

Question Answer Required
What is the specific attack transaction? Inputs, outputs, redeemers, signatories
Which line would the attacker exploit? File:line reference
Did you trace through EVERY require in the code path? Yes/No
Does the attack pass ALL requires? Yes (report) / No (false positive)
What value does the attacker gain? Concrete amount/asset

Do NOT Report If

  • You only found a pattern match without tracing execution
  • You haven’t constructed a specific attack transaction
  • Any require() in the code path would fail the attack
  • You’re unsure whether the attack works (investigate more or write a test)

Output Format

Use clickable file_path:line_number format for all code locations.

Finding Format

For each vulnerability found, output in this format:

### [SEVERITY] ID: Vulnerability Name

**Location:** `full/path/to/File.scala:LINE`
**Method:** methodName

**Issue:** Brief description of what's wrong

**Vulnerable code** (`full/path/to/File.scala:LINE-LINE`):
```scala
// actual code from file

Fix:

// proposed fix


### Summary Table

At the end, provide a summary with clickable locations:

Summary

ID Severity Location Issue Status
C-01 Critical path/File.scala:123 Missing mint validation Fixed
H-01 High path/File.scala:87 Token not in output Declined
M-01 Medium path/File.scala:200 Missing signature False Positive

False Positives

ID Location Reason
M-01 path/File.scala:200 Authorization is done via NFT ownership in verifyAuth helper

Security Grade: A/B/C/D/F


### Location Format Rules

1. Always use full path from project root: `scalus-examples/jvm/src/.../File.scala:123`
2. For ranges use: `File.scala:123-145`
3. For method references: `File.scala:123` (methodName)
4. Make locations clickable by using backticks

## Interactive Workflow

For each finding:
1. Display issue with location and proposed fix
2. Prompt: "Apply fix? [y/n/s/d/f]"
   - y: Apply fix, mark completed, verify with `sbtn compile`
   - n: Skip, log as "declined"
   - s: Skip without logging
   - d: Show more details (attack scenario)
   - f: Mark as false positive (prompts for reason, logged to summary)
3. After all findings: run `sbtn quick` to verify fixes
4. Generate summary report including:
   - Fixed issues
   - Declined issues
   - False positives with reasons

## Reference

For detailed vulnerability patterns and code examples, see:
- `references/vulnerabilities.md` - Full pattern documentation with Scalus-specific examples