Agent skill

centralization-risk

Trigger Pattern Protocol has privileged capabilities (AdminCap, OwnerCap, UpgradeCap, TreasuryCap, custom caps) - Inject Into Breadth agents (optional), depth-state-trace

Stars 215
Forks 33

Install this agent skill to your Project

npx add-skill https://github.com/PlamenTSV/plamen/tree/main/agents/skills/sui/centralization-risk

SKILL.md

Skill: CENTRALIZATION_RISK (Sui)

Trigger Pattern: Protocol has privileged capabilities (AdminCap, OwnerCap, UpgradeCap, TreasuryCap, custom caps) Inject Into: Breadth agents (optional), depth-state-trace Finding prefix: [CR-N] Rules referenced: R2, R6, R9, R10, R13 Required: NO (recommended when protocol has 3+ distinct privileged capability types)

Covers: single points of failure, privilege escalation, capability object management, external governance dependencies, emergency powers. On Sui, centralization risk has unique dimensions: capability objects (AdminCap) are first-class owned objects that can be transferred, UpgradeCap controls full package replacement, TreasuryCap controls token supply, and shared objects can be the target of admin-gated mutations. The ownership and lifecycle of capability objects IS the access control model.


Trigger Patterns

AdminCap|OwnerCap|UpgradeCap|TreasuryCap|GovernanceCap|OperatorCap|PauserCap|MinterCap|
Cap\b|_cap|admin|authority|privilege

Step 1: Capability Inventory

Enumerate ALL capability objects and ALL functions requiring capabilities:

# Capability Type Module Abilities Created Where Holder What It Controls Impact If Lost/Stolen
1 {CapType} {module} {key, store, ...} {init or func} {address/shared} {list functions} {worst case}

Key question for each capability: Is it OWNED (by a single address) or SHARED (accessible via reference in any transaction)?

  • Owned AdminCap: Only the owner can pass it as a tx argument. Strongest access control, but single point of failure.
  • Shared config with admin field: Admin address stored in shared object. More flexible but requires assert!(sender == admin) checks -- verify these are present on ALL admin functions.
  • Shared capability object: DANGEROUS -- anyone can pass a shared AdminCap as a transaction argument without ownership.

Categorize each by impact:

  • FUND_CONTROL: Can move, lock, or destroy user funds (e.g., emergency withdraw, treasury drain)
  • PARAMETER_CONTROL: Can change fees, rates, thresholds, delays (e.g., set_fee, set_max_leverage)
  • OPERATIONAL_CONTROL: Can pause, unpause, add/remove pools, whitelist/blacklist
  • UPGRADE_CONTROL: UpgradeCap -- controls package upgrades, policy changes
  • MINT_CONTROL: TreasuryCap -- controls token supply (mint/burn)

Sui-specific ability checks:

  • Does the capability have store ability? If YES -> anyone holding it can public_transfer it, meaning the privilege is freely transferable. Is this intentional?
  • Does the capability have drop ability? If YES -> it can be silently discarded. For AdminCap, dropping it means admin functions become permanently uncallable. For UpgradeCap, dropping makes the package permanently immutable.
  • Is the capability created ONLY in init()? If created elsewhere, can unauthorized parties mint new capabilities?

Step 2: Capability Hierarchy and Separation

Map the capability hierarchy:

Capability Created By Can Create Other Caps? Transferable (has store)? Destructible (has drop)? Timelock/Delay?
{cap} {init / admin_func} YES/NO YES/NO YES/NO YES/NO

Check:

  • Are FUND_CONTROL and UPGRADE_CONTROL separated into different capability types?
  • Does any single capability type grant both PARAMETER_CONTROL and FUND_CONTROL?
  • Are capability transfers behind timelocks or governance mechanisms?
  • Can capabilities be destroyed, and what happens when they are?
  • Is there a master capability that can create all other capabilities?

Sui-specific separation patterns:

  • Best practice: UpgradeCap held by governance multisig, AdminCap held by operations team, TreasuryCap held by treasury
  • Anti-pattern: single init function creates ALL caps and transfers them to tx_context::sender() -- single point of failure at deployment
  • Two-step transfer: propose new admin -> new admin accepts. Prevents accidental transfer to wrong address.

UpgradeCap Analysis (CRITICAL)

Package UpgradeCap Holder Upgrade Policy Destroyed? Risk Level
{package_id} {address or description} {compatible/additive/dep_only} YES (immutable) / NO {assessment}

Risk levels:

  • UpgradeCap destroyed (make_immutable): No upgrade risk.
  • UpgradeCap held by governance multisig with timelock: Low risk.
  • UpgradeCap held by multisig (no timelock): Low-Medium risk.
  • UpgradeCap held by single address: CRITICAL risk -- one compromised key replaces entire package. All shared objects now interact with attacker code.
  • UpgradeCap stored in shared object: Check access control carefully. If extraction is possible -> same as single address risk.

Step 3: Single Points of Failure

For each capability type:

Capability Key Compromise Impact Current Protection Residual Risk
{cap} {what attacker can do with it} {multisig holder? timelock wrapper?} {what remains}

Sui-Specific SPOF Analysis

Risk Description Severity
UpgradeCap compromise Attacker publishes malicious upgrade. All shared objects now interact with attacker code. ALL user funds at risk. CRITICAL if single address, HIGH if multisig without timelock
AdminCap compromise Attacker calls admin functions: drain pools, change parameters, pause protocol. HIGH if AdminCap controls fund extraction
TreasuryCap compromise Attacker mints unlimited tokens, diluting all holders. HIGH if supply-sensitive protocol
AdminCap with store Holder (or compromised key) can transfer AdminCap to anyone via public_transfer. New holder has full admin access. Adds transfer risk to any compromise scenario
AdminCap with drop Admin can accidentally destroy the capability. Admin functions become permanently uncallable. MEDIUM -- permanent loss of admin access (Rule 9 if admin functions needed for user fund recovery)
Phantom ownership Capability transferred to an address nobody controls (e.g., @0x0). Object is permanently inaccessible -- equivalent to destroying it. Same as destruction if holding value or needed for operations

Severity assessment:

  • Single address with FUND_CONTROL or UPGRADE_CONTROL -> HIGH (minimum)
  • Multisig holds capability + timelock -> LOW (but document)
  • UpgradeCap destroyed + no admin fund extraction -> INFO

Step 4: External Governance Dependencies

Identify parameters or behaviors controlled by EXTERNAL governance:

Dependency External Entity What They Control Protocol Impact If Changed Notification?
{dep} {entity} {parameter/behavior} {impact} YES/NO

Sui-specific external governance:

  • Sui framework upgrades: Validators upgrade sui::* packages via governance. Can framework changes break this protocol?
  • Oracle provider changes: If protocol reads from oracle shared object, oracle admin can change prices, feeds, or parameters
  • DeFi protocol governance: External pools, vaults, or DEXes may change parameters
  • Bridge governance: Wormhole guardian set rotation, Sui Bridge committee changes
  • Dependency package upgrades: If a dependency has active UpgradeCap, its owner can publish new versions. Our package pins to specific version at compile time, but compatible upgrades preserve types that we import.

Check:

  • Can external governance changes break protocol invariants?
  • Does the protocol have circuit breakers for external changes?
  • Does the protocol verify external package addresses at call sites? Types from compatible-upgraded packages remain the same, but behavior may change.

Step 5: Emergency Powers

Document emergency/pause capabilities:

Emergency Function Required Capability What It Affects Recovery Path Time to Recover
{func} {cap_type} {scope: all operations / specific pool} {how to resume} {estimate}

Sui Emergency Patterns

Pattern Description Risk
Global pause field Shared config object has paused: bool. All user functions check it. Standard -- check: can users withdraw when paused?
Capability destruction Admin destroys their own capability to "renounce" control. Irreversible -- if needed later for recovery, funds stranded
Object freeze Admin calls transfer::public_freeze_object on a config. Permanent immutability. If done to wrong object, permanent loss of admin access
Package policy tightening UpgradeCap holder restricts policy (compatible -> additive -> immutable). Good for security, but irreversible. Cannot loosen policy.

Check:

  • Can pausing strand user funds permanently? (Rule 9 -- stranded asset severity floor: minimum MEDIUM)
  • Is there a maximum pause duration or automatic unpause?
  • Can users emergency-withdraw during pause?
  • What happens if the PauserCap/AdminCap is lost or destroyed?
  • Can the protocol be permanently bricked by destroying a critical capability?
  • If no exit during pause -> apply Rule 9 (minimum MEDIUM)

Output Schema

markdown
## Finding [CR-N]: Title

**Verdict**: CONFIRMED / PARTIAL / REFUTED
**Step Execution**: check1,2,3,4,5 | skip(reason) | uncertain
**Rules Applied**: [R2:___, R6:___, R9:___, R10:___, R13:___]
**Severity**: Critical/High/Medium/Low/Info
**Location**: sources/{module}.move:LineN

**Centralization Type**: FUND_CONTROL / PARAMETER_CONTROL / OPERATIONAL_CONTROL / UPGRADE_CONTROL / MINT_CONTROL
**Affected Capability**: {cap_type}
**Mitigation Present**: {multisig / timelock / UpgradeCap destroyed / governance / NONE}

**Description**: What is wrong
**Impact**: What can happen if capability is compromised, lost, or holder acts maliciously
**Recommendation**: How to mitigate (destroy UpgradeCap, use multisig, add timelock, remove `store` ability, wrap in governance)

Step Execution Checklist (MANDATORY)

Step Required Completed? Notes
1. Capability Inventory (all cap-gated functions) YES Owned vs shared, abilities checked
2. Capability Hierarchy and Separation YES store/drop analysis, UpgradeCap assessment
3. Single Points of Failure (per capability) YES
4. External Governance Dependencies YES
5. Emergency Powers and Recovery Paths YES

Cross-Reference Markers

After Step 1: Cross-reference with ABILITY_ANALYSIS Section 4 (Capability Pattern Audit) -- capabilities with store enable unrestricted transfer.

After Step 2: If UpgradeCap held by single address -> immediate finding (minimum HIGH).

After Step 3: If AdminCap has drop and is needed for fund recovery -> Rule 9 stranded asset finding.

After Step 5: If no emergency withdraw exists AND pause is possible -> Rule 9 stranded asset finding.

After Step 5: If protocol claims trustlessness but retains UpgradeCap/AdminCap -> Rule 13 anti-normalization finding.

If any step skipped, document valid reason (N/A, no external governance, no emergency functions, single capability only).

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results