Trigger Pattern Protocol has privileged capabilities (AdminCap, OwnerCap, UpgradeCap, TreasuryCap, custom caps) - Inject Into Breadth agents (optional), depth-state-trace
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.
AdminCap|OwnerCap|UpgradeCap|TreasuryCap|GovernanceCap|OperatorCap|PauserCap|MinterCap|
Cap\b|_cap|admin|authority|privilege
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)?
assert!(sender == admin) checks -- verify these are present on ALL admin functions.AdminCap as a transaction argument without ownership.Categorize each by impact:
Sui-specific ability checks:
store ability? If YES -> anyone holding it can public_transfer it, meaning the privilege is freely transferable. Is this intentional?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.init()? If created elsewhere, can unauthorized parties mint new capabilities?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:
Sui-specific separation patterns:
init function creates ALL caps and transfers them to tx_context::sender() -- single point of failure at deployment| Package | UpgradeCap Holder | Upgrade Policy | Destroyed? | Risk Level |
|---|---|---|---|---|
| {package_id} | {address or description} | {compatible/additive/dep_only} | YES (immutable) / NO | {assessment} |
Risk levels:
make_immutable): No upgrade risk.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} |
| 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:
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::* packages via governance. Can framework changes break this protocol?Check:
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} |
| 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:
## 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 | 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 |
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).