Security Audit
by @iskysun96
Audits Move contracts for security vulnerabilities before deployment using 7-category checklist. Triggers on: 'audit contract', 'security check', 'review sec...
clawhub install aptos-security-auditπ About This Skill
name: security-audit description: "Audits Move contracts for security vulnerabilities before deployment using 7-category checklist. Triggers on: 'audit contract', 'security check', 'review security', 'check for vulnerabilities', 'security audit', 'is this secure', 'find security issues'." license: MIT metadata: author: aptos-labs version: "1.0" category: move tags: ["security", "audit", "vulnerabilities", "best-practices"] priority: critical
Security Audit Skill
Overview
This skill performs systematic security audits of Move contracts using a comprehensive checklist. Every item must pass before deployment.
Critical: Security is non-negotiable. User funds depend on correct implementation.
Core Workflow
Step 1: Run Security Checklist
Review ALL categories in order:
1. Access Control - Who can call functions? 2. Input Validation - Are inputs checked? 3. Object Safety - Object model used correctly? 4. Reference Safety - No dangerous references exposed? 5. Arithmetic Safety - Overflow/underflow prevented? 6. Generic Type Safety - Phantom types used correctly? 7. Testing - 100% coverage achieved?
Step 2: Access Control Audit
Verify:
entry functions verify signer authorityobject::owner()Check for:
// β
CORRECT: Signer verification
public entry fun update_config(admin: &signer, value: u64) acquires Config {
let config = borrow_global(@my_addr);
assert!(signer::address_of(admin) == config.admin, E_NOT_ADMIN);
// Safe to proceed
}// β WRONG: No verification
public entry fun update_config(admin: &signer, value: u64) acquires Config {
let config = borrow_global_mut(@my_addr);
config.value = value; // Anyone can call!
}
For objects:
// β
CORRECT: Ownership verification
public entry fun transfer_item(
owner: &signer,
item: Object- ,
to: address
) acquires Item {
assert!(object::owner(item) == signer::address_of(owner), E_NOT_OWNER);
// Safe to transfer
}
// β WRONG: No ownership check
public entry fun transfer_item(
owner: &signer,
item: Object- ,
to: address
) acquires Item {
// Anyone can transfer any item!
}
Step 3: Input Validation Audit
Verify:
assert!(amount > 0, E_ZERO_AMOUNT)assert!(amount <= MAX, E_AMOUNT_TOO_HIGH)assert!(vector::length(&v) > 0, E_EMPTY_VECTOR)assert!(string::length(&s) <= MAX_LENGTH, E_NAME_TOO_LONG)assert!(addr != @0x0, E_ZERO_ADDRESS)assert!(type_id < MAX_TYPES, E_INVALID_TYPE)Check for:
// β
CORRECT: Comprehensive validation
public entry fun deposit(user: &signer, amount: u64) acquires Account {
assert!(amount > 0, E_ZERO_AMOUNT);
assert!(amount <= MAX_DEPOSIT_AMOUNT, E_AMOUNT_TOO_HIGH); let account = borrow_global_mut(signer::address_of(user));
assert!(account.balance <= MAX_U64 - amount, E_OVERFLOW);
account.balance = account.balance + amount;
}
// β WRONG: No validation
public entry fun deposit(user: &signer, amount: u64) acquires Account {
let account = borrow_global_mut(signer::address_of(user));
account.balance = account.balance + amount; // Can overflow!
}
Step 4: Object Safety Audit
Verify:
Check for:
// β DANGEROUS: Returning ConstructorRef
public fun create_item(): ConstructorRef {
let constructor_ref = object::create_object(@my_addr);
constructor_ref // Caller can destroy object!
}// β
CORRECT: Return Object
public fun create_item(creator: &signer): Object- {
let constructor_ref = object::create_object(signer::address_of(creator));
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
let delete_ref = object::generate_delete_ref(&constructor_ref);
let object_signer = object::generate_signer(&constructor_ref);
move_to(&object_signer, Item { transfer_ref, delete_ref });
object::object_from_constructor_ref- (&constructor_ref)
}
Step 5: Reference Safety Audit
Verify:
&mut references exposed in public function signaturesmem::swapCheck for:
// β DANGEROUS: Exposing mutable reference
public fun get_item_mut(item: Object- ): &mut Item acquires Item {
borrow_global_mut
- (object::object_address(&item))
// Caller can mem::swap fields!
}
// β
CORRECT: Controlled mutations
public entry fun update_item_name(
owner: &signer,
item: Object- ,
new_name: String
) acquires Item {
assert!(object::owner(item) == signer::address_of(owner), E_NOT_OWNER);
let item_data = borrow_global_mut- (object::object_address(&item));
item_data.name = new_name;
}
Step 6: Arithmetic Safety Audit
Verify:
Check for:
// β
CORRECT: Overflow protection
public entry fun deposit(user: &signer, amount: u64) acquires Account {
let account = borrow_global_mut(signer::address_of(user)); // Check overflow BEFORE adding
assert!(account.balance <= MAX_U64 - amount, E_OVERFLOW);
account.balance = account.balance + amount;
}
// β
CORRECT: Underflow protection
public entry fun withdraw(user: &signer, amount: u64) acquires Account {
let account = borrow_global_mut(signer::address_of(user));
// Check underflow BEFORE subtracting
assert!(account.balance >= amount, E_INSUFFICIENT_BALANCE);
account.balance = account.balance - amount;
}
// β WRONG: No overflow check
public entry fun deposit(user: &signer, amount: u64) acquires Account {
let account = borrow_global_mut(signer::address_of(user));
account.balance = account.balance + amount; // Can overflow!
}
Step 7: Generic Type Safety Audit
Verify:
struct VaultCheck for:
// β
CORRECT: Phantom type for safety
struct Vault has key {
balance: u64,
// CoinType only for type safety, not stored
}public fun deposit(vault: Object>, amount: u64) {
// Type-safe: can't deposit BTC into USDC vault
}
// β WRONG: No phantom (won't compile if CoinType not in fields)
struct Vault has key {
balance: u64,
}
Step 8: Testing Audit
Verify:
aptos move test --coverage#[expected_failure]Run:
aptos move test --coverage
aptos move coverage source --module
Verify output shows 100% coverage.
Security Audit Report Template
Generate report in this format:
# Security Audit ReportModule: my_module Date: 2026-01-23 Auditor: AI Assistant
Summary
β
PASS: All security checks passed
β οΈ WARNINGS: 2 minor issues found
β CRITICAL: 0 critical vulnerabilities Access Control
β
All entry functions verify signer authority
β
Object ownership checked in all operations
β
Admin functions properly restricted Input Validation
β
All numeric inputs validated
β οΈ WARNING: String length validation missing in function X
β
Address validation present Object Safety
β
No ConstructorRef returned
β
All refs generated in constructor
β
Object signer used correctly Reference Safety
β
No public &mut references
β
Critical fields protected Arithmetic Safety
β
Overflow checks present
β
Underflow checks present
β
Division by zero prevented Generic Type Safety
β
Phantom types used correctly
β
Constraints appropriate Testing
β
100% line coverage achieved
β
All error paths tested
β
Access control tested
β
Edge cases covered Recommendations
1. Add string length validation to function X (line 42)
2. Consider adding event emissions for important state changes
Conclusion
β
Safe to deploy after addressing warnings.
Common Vulnerabilities
| Vulnerability | Detection | Impact | Fix |
| ------------------------ | ------------------------------------------ | --------------------------------------- | ----------------------------------------- |
| Missing access control | No assert!(signer...) in entry functions | Critical - anyone can call | Add signer verification |
| Missing ownership check | No assert!(object::owner...) | Critical - anyone can modify any object | Add ownership check |
| Integer overflow | No check before addition | Critical - balance wraps to 0 | Check assert!(a <= MAX - b, E_OVERFLOW) |
| Integer underflow | No check before subtraction | Critical - balance wraps to MAX | Check assert!(a >= b, E_UNDERFLOW) |
| Returning ConstructorRef | Function returns ConstructorRef | Critical - caller can destroy object | Return Object instead |
| Exposing &mut | Public function returns &mut T | High - mem::swap attacks | Expose specific operations only |
| No input validation | Accept any value | Medium - zero amounts, overflow | Validate all inputs |
| Low test coverage | Coverage < 100% | Medium - bugs in production | Write more tests |
Automated Checks
Run these commands as part of audit:
# Compile (check for errors)
aptos move compileRun tests
aptos move testCheck coverage
aptos move test --coverage
aptos move coverage summaryExpected: 100.0% coverage
Manual Checks
Review code for:
1. Access Control:
- Search for entry fun β verify each has signer checks
- Search for borrow_global_mut β verify authorization before use
2. Input Validation:
- Search for function parameters β verify validation
- Look for amount, length, address params β verify checks
3. Object Safety:
- Search for ConstructorRef β verify never returned
- Search for create_object β verify refs generated properly
4. Arithmetic:
- Search for + β verify overflow checks
- Search for - β verify underflow checks
- Search for / β verify division by zero checks
ALWAYS Rules
NEVER Rules
~/.aptos/config.yaml or .env files during audits (contain private keys)References
Pattern Documentation:
../../../patterns/move/SECURITY.md - Comprehensive security guide../../../patterns/move/OBJECTS.md - Object safety patternsOfficial Documentation:
Related Skills:
generate-tests - Ensure tests existwrite-contracts - Apply security patternsdeploy-contracts - Final check before deploymentRemember: Security is non-negotiable. Every checklist item must pass. User funds depend on it.