## 6. Vulnerability Checklist (11 Patterns)

### 6.1 REKEYING ATTACK ⚠️ CRITICAL

**Description**: Missing validation of the `RekeyTo` transaction field allows attackers to change account authorization and bypass contract restrictions.

**Detection Patterns**:
```python
# VULNERABLE: No RekeyTo check
If(Txn.type_enum() == TxnType.Payment)
    # Missing: Assert(Txn.rekey_to() == Global.zero_address())

# VULNERABLE: Inner transactions with user-controlled RekeyTo
InnerTxnBuilder.SetField(TxnField.rekey_to, Txn.accounts[1])  # User controlled
```

**What to Check**:
- [ ] All transaction approval logic validates `Txn.rekey_to() == Global.zero_address()`
- [ ] Inner transactions in Teal v6+ do not use user-controlled RekeyTo
- [ ] Group transactions verify RekeyTo for all relevant txns

**Mitigation**:
```python
# SECURE: Validate RekeyTo field
Assert(Txn.rekey_to() == Global.zero_address())

# OR: Explicitly allow specific rekey target
Assert(Txn.rekey_to() == intended_address)
```

**Tool Detection**: Tealer detector `unprotected-rekey` available

**References**: building-secure-contracts/not-so-smart-contracts/algorand/rekeying

---

### 4.2 UNCHECKED TRANSACTION FEE ⚠️ HIGH

**Description**: Smart signatures without fee validation allow users to set excessive fees, draining the sender's account balance.

**Detection Patterns**:
```python
# VULNERABLE: No fee check in smart signature
def approval_program():
    return If(Txn.type_enum() == TxnType.Payment, Int(1), Int(0))
    # Missing fee validation

# VULNERABLE: Unbounded fee
If(Txn.fee() <= some_large_value)  # Still vulnerable
```

**What to Check**:
- [ ] Smart signatures enforce `Txn.fee() == Global.min_txn_fee()`
- [ ] OR fee is explicitly set to 0 with fee pooling enabled
- [ ] No user control over transaction fee amounts

**Mitigation**:
```python
# SECURE: Force fee to zero (with fee pooling)
Assert(Txn.fee() == Int(0))

# OR: Enforce minimum fee only
Assert(Txn.fee() == Global.min_txn_fee())
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/unchecked_transaction_fee

---

### 4.3 CLOSING ACCOUNT (CloseRemainderTo) ⚠️ CRITICAL

**Description**: Missing validation of `CloseRemainderTo` field allows attackers to drain entire account balance to arbitrary address.

**Detection Patterns**:
```python
# VULNERABLE: Payment without CloseRemainderTo check
If(Txn.type_enum() == TxnType.Payment)
    # Missing: Assert(Txn.close_remainder_to() == Global.zero_address())

# VULNERABLE: Inner transaction with close field
InnerTxnBuilder.SetFields({
    TxnField.type_enum: TxnType.Payment,
    # Missing CloseRemainderTo validation
})
```

**What to Check**:
- [ ] All payment transactions validate `Txn.close_remainder_to() == Global.zero_address()`
- [ ] OR explicitly allow specific close address
- [ ] Inner transactions do not set CloseRemainderTo unless intended

**Mitigation**:
```python
# SECURE: Validate CloseRemainderTo
Assert(Txn.close_remainder_to() == Global.zero_address())

# OR: Allow specific close target
Assert(Txn.close_remainder_to() == authorized_address)
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/closing_account

---

### 4.4 CLOSING ASSET (AssetCloseTo) ⚠️ CRITICAL

**Description**: Missing validation of `AssetCloseTo` field enables transferring entire asset balance to arbitrary address.

**Detection Patterns**:
```python
# VULNERABLE: Asset transfer without AssetCloseTo check
If(Txn.type_enum() == TxnType.AssetTransfer)
    # Missing: Assert(Txn.asset_close_to() == Global.zero_address())
```

**What to Check**:
- [ ] All asset transfer transactions validate `Txn.asset_close_to() == Global.zero_address()`
- [ ] OR explicitly specify allowed close target
- [ ] Inner asset transfers validate AssetCloseTo field

**Mitigation**:
```python
# SECURE: Validate AssetCloseTo
Assert(Txn.asset_close_to() == Global.zero_address())
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/closing_asset

---

### 4.5 GROUP SIZE CHECK ⚠️ HIGH

**Description**: Missing validation of `Global.group_size()` allows attackers to include multiple application calls in atomic group, executing operations multiple times.

**Detection Patterns**:
```python
# VULNERABLE: No group size validation
# Attacker can repeat call 10 times in single group
If(Gtxn[0].type_enum() == TxnType.Payment)

# VULNERABLE: Absolute indices without size check
Assert(Gtxn[2].sender() == Gtxn[0].sender())  # No group size validation
```

**What to Check**:
- [ ] Atomic transaction logic validates `Global.group_size()` matches expected size
- [ ] Using absolute indices is paired with group size verification
- [ ] OR use relative indexing with ABI methods (Teal v6+)

**Mitigation**:
```python
# SECURE: Validate group size
Assert(Global.group_size() == Int(3))  # Exact size
# OR
Assert(Global.group_size() <= Int(3))  # Maximum size

# BETTER: Use ABI with relative indexing (Teal v6+)
@router.method
def method():
    # Automatically handles group indexing
```

**Tool Detection**: Tealer detector `group-size-check` available

**References**: building-secure-contracts/not-so-smart-contracts/algorand/group_size_check

---

### 4.6 TIME-BASED REPLAY ATTACK ⚠️ MEDIUM

**Description**: Transactions with same `FirstValid`/`LastValid` but different hashes can be submitted multiple times without `Lease` field protection.

**Detection Patterns**:
```python
# VULNERABLE: Periodic payments without lease
def recurring_payment():
    return Seq([
        Assert(Global.latest_timestamp() >= next_payment_time),
        # Missing Lease validation for replay protection
        InnerTxnBuilder.Submit()
    ])
```

**What to Check**:
- [ ] Recurring/periodic transactions validate `Txn.lease()` field
- [ ] Lease field set to unique value per logical transaction
- [ ] Time-dependent operations have replay protection

**Mitigation**:
```python
# SECURE: Validate Lease field
Assert(Txn.lease() == expected_lease_value)

# OR: Use Lease for mutual exclusion
lease = Sha256(Concat(Bytes("prefix"), Txn.sender(), Itob(counter)))
Assert(Txn.lease() == lease)
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/time_based_replay_attack

---

### 4.7 ACCESS CONTROLS ⚠️ CRITICAL

**Description**: Missing access control checks on `UpdateApplication` and `DeleteApplication` operations allow unauthorized contract modifications.

**Detection Patterns**:
```python
# VULNERABLE: No access control on updates
program = Cond(
    [Txn.application_id() == Int(0), on_creation],
    [Txn.on_completion() == OnComplete.UpdateApplication, Int(1)],  # Anyone can update!
    [Txn.on_completion() == OnComplete.DeleteApplication, Int(1)],  # Anyone can delete!
)

# VULNERABLE: Weak access control
If(Txn.on_completion() == OnComplete.UpdateApplication,
    Int(1))  # Missing sender validation
```

**What to Check**:
- [ ] `UpdateApplication` checks `Txn.sender() == creator/admin`
- [ ] `DeleteApplication` checks `Txn.sender() == creator/admin`
- [ ] OR explicitly disable updates/deletes: `Return(Int(0))`
- [ ] OnComplete field validated for all application calls

**Mitigation**:
```python
# SECURE: Proper access control
is_creator = Txn.sender() == Global.creator_address()

program = Cond(
    [Txn.application_id() == Int(0), on_creation],
    [Txn.on_completion() == OnComplete.UpdateApplication, is_creator],
    [Txn.on_completion() == OnComplete.DeleteApplication, is_creator],
)

# OR: Disable updates entirely
[Txn.on_completion() == OnComplete.UpdateApplication, Return(Int(0))],
```

**Tool Detection**: Tealer detector `update-application-check` available

**References**: building-secure-contracts/not-so-smart-contracts/algorand/access_controls

---

### 4.8 ASSET ID VERIFICATION ⚠️ HIGH

**Description**: Missing validation of `Txn.xfer_asset()` allows attackers to transfer wrong/worthless assets instead of expected tokens.

**Detection Patterns**:
```python
# VULNERABLE: No asset ID check
If(And(
    Txn.type_enum() == TxnType.AssetTransfer,
    Txn.asset_amount() >= required_amount,
    # Missing: Txn.xfer_asset() == expected_asset_id
))

# VULNERABLE: User-provided asset ID
def swap(asset_id):  # User controlled!
    return If(Txn.xfer_asset() == asset_id, ...)  # No validation
```

**What to Check**:
- [ ] All asset transfer validations include `Txn.xfer_asset() == expected_asset_id`
- [ ] Asset IDs stored in global state or hardcoded
- [ ] No user control over which asset ID is considered valid

**Mitigation**:
```python
# SECURE: Validate asset ID
expected_asset_id = Int(12345678)  # Or from global state
Assert(And(
    Txn.type_enum() == TxnType.AssetTransfer,
    Txn.xfer_asset() == expected_asset_id,
    Txn.asset_amount() >= required_amount
))
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/asset_id_verification

---

### 4.9 DENIAL OF SERVICE (Asset Opt-In) ⚠️ MEDIUM

**Description**: Transferring assets to non-opted-in accounts causes transaction failure, enabling DoS attacks when using push pattern.

**Detection Patterns**:
```python
# VULNERABLE: Push pattern for asset distribution
For(i IN users).Do(
    InnerTxnBuilder.SetFields({
        TxnField.type_enum: TxnType.AssetTransfer,
        TxnField.receiver: users[i],
        TxnField.asset_amount: rewards[i]
    })
)  # Fails if any user not opted-in, DoS all users

# VULNERABLE: Batch operations with asset transfers
# Single failure blocks entire batch
```

**What to Check**:
- [ ] Asset distributions use pull pattern (users claim) instead of push
- [ ] OR batch operations handle opt-in failures gracefully
- [ ] Critical operations not blocked by asset transfer failures

**Mitigation**:
```python
# SECURE: Pull pattern
@router.method
def claim_reward():
    # User initiates, must be opted-in
    amount = App.localGet(Txn.sender(), Bytes("reward"))
    Assert(amount > Int(0))
    # Transfer asset to opted-in user

# BETTER: Users trigger their own transfers
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/dos

---

### 4.10 INNER TRANSACTION FEE ⚠️ MEDIUM

**Description**: Inner transactions with unset or non-zero fees drain application balance when fee pooling is used.

**Detection Patterns**:
```python
# VULNERABLE: Missing fee field in inner transaction
InnerTxnBuilder.Begin()
InnerTxnBuilder.SetFields({
    TxnField.type_enum: TxnType.Payment,
    TxnField.receiver: receiver,
    # Missing: TxnField.fee: Int(0)
})
InnerTxnBuilder.Submit()  # Drains app balance for fees!

# VULNERABLE: Non-zero inner transaction fee
InnerTxnBuilder.SetField(TxnField.fee, Int(1000))  # Drains balance
```

**What to Check**:
- [ ] All inner transactions explicitly set `TxnField.fee: Int(0)`
- [ ] Fee pooling strategy documented and validated
- [ ] Internal bookkeeping accounts for any non-zero fees

**Mitigation**:
```python
# SECURE: Explicitly set fee to zero
InnerTxnBuilder.Begin()
InnerTxnBuilder.SetFields({
    TxnField.type_enum: TxnType.Payment,
    TxnField.receiver: receiver,
    TxnField.amount: amount,
    TxnField.fee: Int(0),  # Explicit zero fee
})
InnerTxnBuilder.Submit()
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/inner_transaction_fee

---

### 4.11 CLEAR STATE TRANSACTION ⚠️ HIGH

**Description**: Missing `OnComplete` field validation allows attackers to invoke clear state program instead of approval program, bypassing logic.

**Detection Patterns**:
```python
# VULNERABLE: Only checks transaction type, not OnComplete
def validate_group():
    return And(
        Gtxn[0].type_enum() == TxnType.Payment,
        Gtxn[1].type_enum() == TxnType.ApplicationCall,  # Could be ClearState!
        # Missing: Gtxn[1].on_completion() == OnComplete.NoOp
    )

# VULNERABLE: Assumes ApplicationCall is approval
If(Gtxn[i].type_enum() == TxnType.ApplicationCall,
    validate_app_call())  # May be ClearStateProgram
```

**What to Check**:
- [ ] Group transaction validation checks `Gtxn[i].on_completion() == OnComplete.NoOp`
- [ ] OR explicitly allows specific OnComplete values
- [ ] Not just checking `TxnType.ApplicationCall` without OnComplete validation

**Mitigation**:
```python
# SECURE: Validate OnComplete field
def validate_group():
    return And(
        Gtxn[0].type_enum() == TxnType.Payment,
        Gtxn[1].type_enum() == TxnType.ApplicationCall,
        Gtxn[1].on_completion() == OnComplete.NoOp,  # Explicit check
    )
```

**References**: building-secure-contracts/not-so-smart-contracts/algorand/clear_state_transaction

---
