agents/skills/sui/ability-analysis/SKILL.md
Trigger Pattern Always (Sui Move) -- foundational security check - Inject Into Breadth agents, depth agents
npx skillsauth add plamentsv/plamen ability-analysisInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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 have id: UID as the first field. Can be owned, shared, or frozen.store = Can be transferred freely via public_transfer / public_share_object. Can be stored inside other objects via dynamic fields or wrapping.drop = Can be implicitly discarded. Without drop, the value MUST be explicitly consumed (unpacked, transferred, or destroyed).copy = Can be duplicated. copy + key is IMPOSSIBLE in Sui -- objects cannot be copied.Consistency check: For each struct with key:
id: UID as the FIRST field? If not -> compilation error (catch misplaced UID).store intentionally included or omitted? key without store = only the defining module can transfer it (custom transfer rules).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:
transfer::transfer to reset state?transfer::share_object at creation?transfer::freeze_object?key only, no store, wrapped inside a key + store parent)?For each struct, verify ability assignments match intended behavior:
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.
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?
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.
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:
init() (module initializer)? If created elsewhere, can it be minted by unauthorized parties?store? If yes, the holder can transfer it freely -- is this intended?init, not stored.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:
object::id() of the object being repaid, settled, claimed, or mutated?store via a wrapper? (Check: is there a public wrapper that accepts arbitrary store types.)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). If a receipt enforces consumption but is not bound to the source object it came from -> FINDING (the receipt can settle the wrong order/pool/position).
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 via transfer::public_transfer.key only: Only the defining module can transfer via transfer::transfer (requires module-level access).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:
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::add requires the value type to have store.dynamic_object_field::add requires the value type to have key + store.store is added as a dynamic field, anyone who can access the parent object can potentially extract it via dynamic_field::remove. Is extraction access-controlled?vector<u8> or String.)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:
init the ONLY place critical capabilities are created?init properly consume the One-Time Witness if one is passed?init.)init? (They must be -- you cannot share an owned object after creation in Sui.)**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]
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/? | |
After Section 4 (Capability Pattern Audit):
TYPE_SAFETY.md Section on OTW analysisstore -> flag for SEMI_TRUSTED_ROLES analysisAfter Section 5 (Hot Potato Enforcement):
After Section 7 (Dynamic Field Ability Propagation):
development
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline