Agent skill
ability-analysis
Trigger Pattern Always (Sui Move) -- foundational security check - Inject Into Breadth agents, depth agents
Install this agent skill to your Project
npx add-skill https://github.com/PlamenTSV/plamen/tree/main/agents/skills/sui/ability-analysis
SKILL.md
ABILITY_ANALYSIS Skill
Trigger Pattern: Always (Sui Move) -- foundational security check Inject Into: Breadth agents, depth agents
For every struct defined in the protocol:
STEP PRIORITY: Steps 5 (Hot Potato Enforcement) and 7 (Dynamic Field Ability Propagation) are where HIGH/CRITICAL severity findings most commonly hide. Do NOT rush these steps. If constrained, skip conditional sections before skipping 5 or 7.
1. Struct Ability Inventory
Enumerate ALL structs across all modules:
| Module | Struct | Abilities | Has id: UID? |
Is Object? | Transferable? | Notes |
|---|---|---|---|---|---|---|
| {mod} | {name} | {key, store, drop, copy} | YES/NO | YES/NO | YES/NO | {context} |
Sui ability semantics:
key= Object type. MUST haveid: UIDas the first field. Can be owned, shared, or frozen.store= Can be transferred freely viapublic_transfer/public_share_object. Can be stored inside other objects via dynamic fields or wrapping.drop= Can be implicitly discarded. Withoutdrop, the value MUST be explicitly consumed (unpacked, transferred, or destroyed).copy= Can be duplicated.copy + keyis IMPOSSIBLE in Sui -- objects cannot be copied.- No abilities at all = Hot potato pattern. Value must be consumed within the same transaction.
Consistency check: For each struct with key:
- Does it have
id: UIDas the FIRST field? If not -> compilation error (catch misplaced UID). - Is
storeintentionally included or omitted?keywithoutstore= only the defining module can transfer it (custom transfer rules).
2. Object Model Classification
Classify each object (key ability) by ownership model:
| Object | Ownership | Created Via | Transfer Restricted? | Freeze Possible? |
|---|---|---|---|---|
| {name} | Owned / Shared / Frozen / Wrapped | {function} | YES (no store) / NO (store) |
YES/NO |
Security checks per ownership type:
2a. Owned Objects
- Can the owner transfer to themselves via
transfer::transferto reset state? - Are there time-locks or cooldowns that reset on transfer?
- Can owned objects be wrapped inside other objects to bypass module restrictions?
2b. Shared Objects
- Is the object made shared via
transfer::share_objectat creation? - Once shared, can never be un-shared -- is this intended?
- Shared objects require consensus ordering -- are there ordering-dependent operations?
- Critical: Can an attacker create a competing shared object of the same type?
2c. Frozen Objects (Immutable)
- Is the object frozen via
transfer::freeze_object? - Once frozen, can never be mutated -- is this intended?
- Are there references to the frozen object that expect mutation?
2d. Wrapped Objects
- Objects stored as fields inside other objects lose their independent existence.
- Can wrapping bypass transfer restrictions (object with
keyonly, nostore, wrapped inside akey + storeparent)? - When unwrapped, does the object retain its original ID and state?
3. Ability Mismatch Analysis
For each struct, verify ability assignments match intended behavior:
3a. Missing drop -- Intentional?
| Struct | Has drop? |
Explicit Destroy Function? | Can Leak? |
|---|---|---|---|
| {name} | NO | YES: destroy_{name}() / NO |
YES/NO |
Rule: A struct without drop that has no explicit destroy/consume path creates a resource leak. The transaction will abort if the value is not consumed. This is sometimes intentional (hot potato) but often a bug when the struct is created in error paths.
3b. Unnecessary store -- Over-Permissive?
| Struct | Has store? |
Stored in Dynamic Fields? | Freely Transferable? | Should Be Restricted? |
|---|---|---|---|---|
| {name} | YES | YES/NO | YES | {analysis} |
Check: If a struct has store but the protocol intends restricted transfers (e.g., non-transferable receipts, bound tickets), the store ability enables bypass via public_transfer. Does any security invariant depend on transfer restriction?
3c. copy Abuse Potential
| Struct | Has copy? |
Contains Balances/IDs? | Duplication Dangerous? |
|---|---|---|---|
| {name} | YES/NO | YES/NO | YES/NO |
Rule: copy on a struct containing Balance<T>, capability tokens, or unique identifiers is almost always a bug -- it enables double-spending or capability duplication. copy + key is impossible (enforced by Sui), but copy + store on inner structs is allowed and dangerous if they hold value.
4. Capability Pattern Audit
Identify all capability/admin structs:
| Capability | Abilities | Created In | Transferred To | Can Be Duplicated? | Revocable? |
|---|---|---|---|---|---|
| {name} | {abilities} | init() |
{recipient} | YES (copy) / NO |
YES/NO |
Checks:
- Is the capability created only in
init()(module initializer)? If created elsewhere, can it be minted by unauthorized parties? - Does the capability have
store? If yes, the holder can transfer it freely -- is this intended? - Is there a revocation mechanism? (Capability patterns in Sui are typically one-way -- once issued, not revocable without wrapping in a shared object with access control.)
- One-Time Witness (OTW) vs Capability: Is this struct actually an OTW being misused as a persistent capability? OTW types should be consumed in
init, not stored.
5. Hot Potato Enforcement
Identify all structs with NO abilities:
| Struct | Module | Created By | Must Be Consumed By | Enforced? |
|---|---|---|---|---|
| {name} | {mod} | {function} | {function} | YES/NO |
Hot potato security checks:
- Is the hot potato created and consumed within a single PTB (Programmable Transaction Block)?
- Can the consumption function be called by anyone, or only specific callers?
- Does the consumption function validate the hot potato's contents match expectations?
- Can an attacker create a fake hot potato of the same type from a different module? (NO -- Move type system prevents cross-module struct creation.)
- Can the hot potato be stored if someone adds
storevia a wrapper? (Check: is there a public wrapper that accepts arbitrarystoretypes.) - Transaction abort impact: If the hot potato cannot be consumed (e.g., consumption function reverts), the entire PTB aborts. Can this be used for griefing? (e.g., attacker causes the consumption precondition to fail after the hot potato is created.)
Pattern validation: Trace every hot potato from creation to consumption. Document the full lifecycle:
create: module::start_action() -> HotPotato
... intervening calls that rely on HotPotato's existence ...
consume: module::finish_action(potato: HotPotato)
If any code path creates a hot potato without a guaranteed consumption path -> FINDING (transaction will always abort on that path).
6. Transfer Restriction Analysis
For objects with key but NOT store:
| Object | Module Transfer Function | Custom Rules | Bypass Possible? |
|---|---|---|---|
| {name} | {function or NONE} | {description} | YES/NO |
Sui transfer rules:
key + store: Anyone can transfer viatransfer::public_transfer.keyonly: Only the defining module can transfer viatransfer::transfer(requires module-level access).- Bypass check: Can the restricted object be wrapped inside a
store-capable struct, then the wrapper transferred freely? If the wrapping struct is from a DIFFERENT module, this is a transfer restriction bypass.
Check each restricted object:
- Does any public function accept this object type and wrap it?
- Does any public function accept this object type and place it in a dynamic field of a freely transferable object?
- If yes to either -> the transfer restriction is bypassable -> FINDING.
7. Dynamic Field Ability Propagation
For every use of dynamic_field::add or dynamic_object_field::add:
| Parent Object | Field Key Type | Field Value Type | Value Has store? |
Parent Has store? |
|---|---|---|---|---|
| {parent} | {key_type} | {value_type} | YES/NO | YES/NO |
Rules:
dynamic_field::addrequires the value type to havestore.dynamic_object_field::addrequires the value type to havekey + store.- Security check: If a value with
storeis added as a dynamic field, anyone who can access the parent object can potentially extract it viadynamic_field::remove. Is extraction access-controlled? - Orphan check: If the parent object is destroyed, are dynamic fields cleaned up? Orphaned dynamic fields remain in storage and can never be accessed again -> permanent storage leak.
- Type confusion: Dynamic fields are keyed by type. Can an attacker add a dynamic field with a key type that collides with an expected key type? (Unlikely due to Move type system, but check for generic key types like
vector<u8>orString.)
8. Module Initializer Audit
For each module with an init function:
| Module | init Parameters |
Objects Created | Capabilities Issued | OTW Consumed? |
|---|---|---|---|---|
| {mod} | {params} | {list} | {list} | YES/NO/N/A |
Checks:
- Is
initthe ONLY place critical capabilities are created? - Does
initproperly consume the One-Time Witness if one is passed? - Can the module be re-initialized via package upgrade? (Sui package upgrades do NOT re-run
init.) - Are shared objects created in
init? (They must be -- you cannot share an owned object after creation in Sui.)
Finding Template
**ID**: [AB-N]
**Severity**: [based on ability misuse impact]
**Step Execution**: check1,2,3,4,5,6,7,8 | X(reasons) | ?(uncertain)
**Rules Applied**: [R4:Y, R5:Y, R10:Y, ...]
**Location**: module::struct_name
**Title**: [Ability issue type] in [struct] enables [attack/bypass]
**Description**: [Specific ability misconfiguration with type-level trace]
**Impact**: [What breaks: transfer restriction bypass, capability duplication, resource leak, hot potato griefing]
Step Execution Checklist (MANDATORY)
CRITICAL: You MUST report completion status for ALL sections. Findings with incomplete sections will be flagged for depth review.
| Section | Required | Completed? | Notes |
|---|---|---|---|
| 1. Struct Ability Inventory | YES | Y/X/? | |
| 2. Object Model Classification | YES | Y/X/? | |
| 2b. Shared Object Analysis | IF shared objects | Y/X(N/A)/? | |
| 3. Ability Mismatch Analysis | YES | Y/X/? | |
3b. Unnecessary store Check |
YES | Y/X/? | |
3c. copy Abuse Check |
YES | Y/X/? | |
| 4. Capability Pattern Audit | YES | Y/X/? | |
| 5. Hot Potato Enforcement | IF hot potatoes exist | Y/X(N/A)/? | HIGH PRIORITY |
| 6. Transfer Restriction Analysis | IF key-only objects |
Y/X(N/A)/? | |
| 7. Dynamic Field Ability Propagation | IF dynamic fields used | Y/X(N/A)/? | HIGH PRIORITY |
| 8. Module Initializer Audit | YES | Y/X/? |
Cross-Reference Markers
After Section 4 (Capability Pattern Audit):
- Cross-reference with
TYPE_SAFETY.mdSection on OTW analysis - IF capability has
store-> flag for SEMI_TRUSTED_ROLES analysis
After Section 5 (Hot Potato Enforcement):
- IF hot potato consumption depends on external state -> cross-reference with EXTERNAL_PRECONDITION_AUDIT
- IF hot potato abort causes shared object locking -> document consensus impact
After Section 7 (Dynamic Field Ability Propagation):
- IF dynamic field values extractable by non-owners -> FINDING (minimum Medium)
- Cross-reference with TOKEN_FLOW_TRACING for dynamic field token storage
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
integration-hazard-research
Protocol Type Trigger NAMED_EXTERNAL_PROTOCOL (detected when recon finds import/interface for an identifiable external protocol — not standard libraries). Researches known integration hazards of the target protocol.
outcome-determinism
Protocol Type Trigger outcome_determinism - detected when EITHER of these code patterns are present - - Selection from finite depletable pool with fallback behavior (while(full)...
governance-attack-vectors
Protocol Type Trigger governance (detected when Governor, Timelock, voting, proposal, quorum, delegate patterns found) - Inject Into Breadth agents, depth-external, depth-edge-case
vault-accounting
Protocol Type Trigger vault (detected in recon TASK 0 Step 1) - Inject Into Core state agent OR economic design agent (merge via M4 hierarchy)
lending-protocol-security
Protocol Type Trigger lending (detected when recon finds liquidate|borrow|repay|collateral|lend|loan|LTV|healthFactor|interestRate|debtToken) - Inject Into Breadth agents, depth...
dex-integration-security
Protocol Type Trigger dex_integration (detected when recon finds swap|addLiquidity|removeLiquidity|IUniswapV2Router|ISwapRouter|amountOutMin|amountOutMinimum|slippage - AND the...
Didn't find tool you were looking for?