From aptos-agent-skills
Audits Move contracts for security vulnerabilities using 7-category checklist: access control, input validation, object safety, reference safety, arithmetic safety, generic type safety, testing. Use before deployment.
npx claudepluginhub aptos-labs/aptos-agent-skills --plugin aptos-agent-skillsThis skill uses the workspace's default tool permissions.
This skill performs systematic security audits of Move contracts using a comprehensive checklist. Every item must pass
Orchestrates interactive Solidity smart contract security audits using Map-Hunt-Attack methodology: static analysis (Slither, Aderyn), fuzzing (Echidna, Medusa, Halmos), verification, and reporting.
Generates comprehensive test suites for Move contracts ensuring 100% line coverage. Tests happy paths, access control, input validation, and edge cases.
Applies Solidity security best practices to prevent vulnerabilities like reentrancy, overflows, and access control issues when writing or auditing smart contracts.
Share bugs, ideas, or general feedback.
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.
Review ALL categories in order:
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<Config>(@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<Config>(@my_addr);
config.value = value; // Anyone can call!
}
For objects:
// ✅ CORRECT: Ownership verification
public entry fun transfer_item(
owner: &signer,
item: Object<Item>,
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<Item>,
to: address
) acquires Item {
// Anyone can transfer any item!
}
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<Account>(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<Account>(signer::address_of(user));
account.balance = account.balance + amount; // Can overflow!
}
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<T>
public fun create_item(creator: &signer): Object<Item> {
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<Item>(&constructor_ref)
}
Verify:
&mut references exposed in public function signaturesmem::swapCheck for:
// ❌ DANGEROUS: Exposing mutable reference
public fun get_item_mut(item: Object<Item>): &mut Item acquires Item {
borrow_global_mut<Item>(object::object_address(&item))
// Caller can mem::swap fields!
}
// ✅ CORRECT: Controlled mutations
public entry fun update_item_name(
owner: &signer,
item: Object<Item>,
new_name: String
) acquires Item {
assert!(object::owner(item) == signer::address_of(owner), E_NOT_OWNER);
let item_data = borrow_global_mut<Item>(object::object_address(&item));
item_data.name = new_name;
}
Verify:
Check for:
// ✅ CORRECT: Overflow protection
public entry fun deposit(user: &signer, amount: u64) acquires Account {
let account = borrow_global_mut<Account>(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<Account>(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<Account>(signer::address_of(user));
account.balance = account.balance + amount; // Can overflow!
}
Verify:
struct Vault<phantom CoinType><T: copy + drop>Check for:
// ✅ CORRECT: Phantom type for safety
struct Vault<phantom CoinType> has key {
balance: u64,
// CoinType only for type safety, not stored
}
public fun deposit<CoinType>(vault: Object<Vault<CoinType>>, amount: u64) {
// Type-safe: can't deposit BTC into USDC vault
}
// ❌ WRONG: No phantom (won't compile if CoinType not in fields)
struct Vault<CoinType> has key {
balance: u64,
}
Verify:
aptos move test --coverage#[expected_failure]Run:
aptos move test --coverage
aptos move coverage source --module <module_name>
Verify output shows 100% coverage.
Generate report in this format:
# Security Audit Report
**Module:** 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.
| 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<T> 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 |
Run these commands as part of audit:
# Compile (check for errors)
aptos move compile
# Run tests
aptos move test
# Check coverage
aptos move test --coverage
aptos move coverage summary
# Expected: 100.0% coverage
Review code for:
Access Control:
entry fun → verify each has signer checksborrow_global_mut → verify authorization before useInput Validation:
amount, length, address params → verify checksObject Safety:
ConstructorRef → verify never returnedcreate_object → verify refs generated properlyArithmetic:
+ → verify overflow checks- → verify underflow checks/ → verify division by zero checks~/.aptos/config.yaml or .env files during audits (contain private keys)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.