
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'.
SKILL.md
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:
- Access Control - Who can call functions?
- Input Validation - Are inputs checked?
- Object Safety - Object model used correctly?
- Reference Safety - No dangerous references exposed?
- Arithmetic Safety - Overflow/underflow prevented?
- Generic Type Safety - Phantom types used correctly?
- Testing - 100% coverage achieved?
Step 2: Access Control Audit
Verify:
- All
entryfunctions verify signer authority - Object ownership checked with
object::owner() - Admin functions check caller is admin
- Function visibility uses least-privilege
- No public functions modify state without checks
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!
}
Step 3: Input Validation Audit
Verify:
- Numeric inputs checked for zero:
assert!(amount > 0, E_ZERO_AMOUNT) - Numeric inputs within max limits:
assert!(amount <= MAX, E_AMOUNT_TOO_HIGH) - Vector lengths validated:
assert!(vector::length(&v) > 0, E_EMPTY_VECTOR) - String lengths checked:
assert!(string::length(&s) <= MAX_LENGTH, E_NAME_TOO_LONG) - Addresses validated:
assert!(addr != @0x0, E_ZERO_ADDRESS) - Enum-like values in range:
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!
}
Step 4: Object Safety Audit
Verify:
- ConstructorRef never returned from public functions
- All refs (TransferRef, DeleteRef, ExtendRef) generated in constructor
- Object signer only used during construction or with ExtendRef
- Ungated transfers disabled unless explicitly needed
- DeleteRef only generated for truly burnable objects
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)
}
Step 5: Reference Safety Audit
Verify:
- No
&mutreferences exposed in public function signatures - Critical fields protected from
mem::swap - Mutable borrows minimized in scope
Check 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;
}
Step 6: Arithmetic Safety Audit
Verify:
- Additions checked for overflow
- Subtractions checked for underflow
- Division by zero prevented
- Multiplication checked for overflow
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!
}
Step 7: Generic Type Safety Audit
Verify:
- Phantom types used for type witnesses:
struct Vault<phantom CoinType> - Generic constraints appropriate:
<T: copy + drop> - No type confusion possible
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,
}
Step 8: Testing Audit
Verify:
- 100% line coverage achieved:
aptos move test --coverage - All error paths tested with
#[expected_failure] - Access control tested with multiple signers
- Input validation tested with invalid inputs
- Edge cases covered (max values, empty vectors, etc.)
Run:
aptos move test --coverage
aptos move coverage source --module <module_name>
Verify output shows 100% coverage.
Security Audit Report Template
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.
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<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 |
Automated Checks
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
Manual Checks
Review code for:
- Access Control:
- Search for
entry fun→ verify each has signer checks - Search for
borrow_global_mut→ verify authorization before use
- Search for
- Input Validation:
- Search for function parameters → verify validation
- Look for
amount,length,addressparams → verify checks
- Object Safety:
- Search for
ConstructorRef→ verify never returned - Search for
create_object→ verify refs generated properly
- Search for
- Arithmetic:
- Search for
+→ verify overflow checks - Search for
-→ verify underflow checks - Search for
/→ verify division by zero checks
- Search for
ALWAYS Rules
- ✅ ALWAYS run full security checklist before deployment
- ✅ ALWAYS verify 100% test coverage
- ✅ ALWAYS check access control in entry functions
- ✅ ALWAYS validate all inputs
- ✅ ALWAYS protect against overflow/underflow
- ✅ ALWAYS generate audit report
- ✅ ALWAYS fix critical issues before deployment
NEVER Rules
- ❌ NEVER skip security audit before deployment
- ❌ NEVER ignore failing security checks
- ❌ NEVER deploy with < 100% test coverage
- ❌ NEVER approve code with critical vulnerabilities
- ❌ NEVER rush security review
- ❌ NEVER read
~/.aptos/config.yamlor.envfiles during audits (contain private keys) - ❌ NEVER display or repeat private key values found during audit
References
Pattern Documentation:
../../../patterns/move/SECURITY.md- Comprehensive security guide../../../patterns/move/OBJECTS.md- Object safety patterns
Official Documentation:
Related Skills:
generate-tests- Ensure tests existwrite-contracts- Apply security patternsdeploy-contracts- Final check before deployment
Remember: Security is non-negotiable. Every checklist item must pass. User funds depend on it.
More skills from the aptos-agent-skills repository
View all 17 skillsanalyze-gas-optimization
optimize Aptos Move contracts for gas
Jul 12BlockchainPerformanceSmart Contractscreate-aptos-project
scaffold new Aptos dApp projects
Jul 12Next.jsTypeScriptViteWeb3deploy-contracts
deploy Move contracts to Aptos networks
Jul 12DeploymentEngineeringSmart Contractsgenerate-tests
generate test suites for Move contracts
Jul 12EngineeringQATestingmodernize-move
modernize Move V1 contracts to V2
Jul 12Code AnalysisEngineeringMigrationsearch-aptos-examples
search Aptos reference implementations
Jul 12BlockchainDocumentationSearch
More from Aptos Labs
View publishersmoothsend-gasless
sponsor gas fees for Aptos dApps
aptos-agent-skills
Jul 12API DevelopmentPaymentsWeb3ts-sdk-account
manage Aptos accounts with TS SDK
aptos-agent-skills
Jul 12AuthBlockchainSDKTypeScriptts-sdk-address
manage AccountAddress in Aptos TypeScript SDK
aptos-agent-skills
Jul 12BlockchainSDKTypeScriptts-sdk-client
configure Aptos SDK clients
aptos-agent-skills
Jul 12API DevelopmentSDKTypeScriptts-sdk-transactions
manage Aptos blockchain transactions
aptos-agent-skills
Jul 12API DevelopmentTypeScriptWeb3