LLMSkillHub
mirroredCopied from PlamenTSV/plamen under MIT. PlamenTSV has not claimed this page, and is not affiliated with LLM SkillHub.

plamentsv / type-safety

Trigger Pattern Always (Aptos Move) - generic type exploitation - Inject Into Breadth agents, depth-state-trace

0
SKILL.md
15.0 KB

TYPE_SAFETY Skill

Trigger Pattern: Always (Aptos Move) --- generic type exploitation
Inject Into: Breadth agents, depth-state-trace

Move's type system is its primary security mechanism. Generic type parameters allow modules to be polymorphic, but incorrect or insufficient type constraints enable attackers to substitute unexpected types, bypass access control, confuse token types, or exploit phantom type assumptions. This skill audits every generic interface for type safety violations.

STEP PRIORITY: Steps 2 (Type Parameter Substitution) and 5 (Coin/FungibleAsset Type Confusion) are where HIGH/CRITICAL severity findings most commonly hide. Do NOT rush these steps. If constrained, skip conditional sections (3, 4) before skipping 2 or 5.

1. Generic Function Inventory

Enumerate ALL public, public(friend), and entry functions with generic type parameters:

FunctionModuleType ParamsConstraintsVisibilityEntry?Who Can Call
withdraw<T>vaultTkeypublicYESAny signer
swap<X, Y>dexX, YstorepublicYESAny signer

MANDATORY GREP: Search all .move files for fun .*< to find every generic function. Include internal (fun), public(friend) fun, public fun, and public entry fun.

For each generic function, additionally note:

  • Does the function create, destroy, or transfer instances of the generic type?
  • Does the function make assumptions about the generic type beyond its constraints? (e.g., assuming T is a coin type when the constraint is only store)
  • Is the generic parameter used as a phantom/tag or does the function operate on actual instances of T?

2. Type Parameter Substitution Analysis

For each generic function identified in Step 1, analyze what happens when an attacker substitutes an unexpected type:

2a. Substitution Attack Table

FunctionType ParamExpected TypeAttacker SubstitutesGuard Against Wrong Type?Impact
withdraw<T>(store)TRealCoinFakeCoin (attacker-defined)YES --- {mechanism} / NO{impact}

Attack methodology per function:

  1. Identify expected type: What type does the protocol developer intend callers to use? This is often documented but NOT enforced at the type level.
  2. Check enforcement: Is there an on-chain mechanism that restricts T to the expected type? Common mechanisms:

    • Registered type list (module stores TypeInfo and checks against it)
    • Type witness parameter (function also requires &TypeWitness<T>)
    • Module-level resource check (assert!(exists<Pool<T>>(@protocol), E_INVALID_TYPE))
    • coin::is_coin_initialized<T>() check
    • Signer-of-defining-module pattern (only the module that defines T can call)
  3. If NO enforcement: What happens if attacker creates module attacker::fake { struct FakeCoin has store {} } and calls withdraw<FakeCoin>()?

2b. Cross-Pool / Cross-Market Type Confusion

For protocols with pools, markets, or vaults parameterized by type:

Pool/MarketType ParameterCan Attacker Create Pool With Arbitrary Type?Impact If Confusion
Pool<T>TYES --- anyone can call create_pool<T>() / NO{drain, mispricing, accounting error}

Check: If Pool<RealCoin> and Pool<FakeCoin> exist, can operations on one affect the other? Common issues:

  • Shared global state accessed by both pools
  • Price oracle shared between pools (attacker manipulates FakeCoin price, affects RealCoin pool)
  • Reward distribution computed across all pools regardless of type

Tag: [TRACE:substitute T=FakeCoin → {function} → {bypass/confusion} → impact: {X}]

3. Phantom Type Audit

For structs with phantom type parameters (phantom T):

3a. Phantom Type Inventory

StructPhantom ParamPurposeRuntime Impact of TCan T Be Forged?
Pool<phantom CoinType>CoinTypeType-tag discriminationNone (phantom){analysis}

Phantom type rules in Move:

  • Phantom type parameters do NOT affect runtime representation --- two structs with different phantom types have the same memory layout.
  • Phantom types are used for type-level tagging: Pool<USDC> vs Pool<WETH> are different types at the Move level but identical at the bytecode level.
  • The compiler enforces that phantom types are not used in non-phantom positions.

Check for each phantom type:

  1. Is the phantom parameter used ONLY for type discrimination (correct use)?
  2. Does any function extract or operate on the phantom type at runtime? (should be impossible by compiler, but verify no workarounds)
  3. Can an attacker create a struct with a phantom type that aliases an existing legitimate phantom type? (e.g., creating Pool<AttackerCoin> that interacts with Pool<USDC> state)
  4. Are phantom type parameters properly propagated through nested generics? (Wrapper<phantom T> containing Inner<T> --- is T phantom in Inner too?)

3b. Phantom Type Bypass Patterns

PatternRiskCheck
Phantom used for access controlMediumCan attacker define their own type to bypass access gate?
Phantom used for pool isolationHighDoes pool isolation rely solely on phantom type discrimination?
Phantom type in event emissionLowCan attacker emit events with spoofed phantom types for off-chain confusion?

4. Type Witness Pattern

For functions that accept type witnesses:

4a. Witness Inventory

Witness StructCreating ModuleWho Can Create?Functions That Accept ItProperly Gated?
TypeWitness<T>{module}{analysis}{list}YES/NO

Type witness pattern is Move's equivalent of capability-based access control at the type level. A type witness is a struct that can only be created by the module that defines the associated type. Functions that require a witness parameter are restricted to callers authorized by that module.

Check for each witness:

  1. Is the witness struct defined in the SAME module as the type it witnesses? If not, the witness can be created by anyone who imports the witness module.
  2. Does the witness have drop? If yes, it can be created once and reused --- is this intended?
  3. Does the witness have copy? If yes, it can be duplicated --- does this break single-use assumptions?
  4. Does the witness have store? If yes, it can be persisted --- can an attacker store a witness and replay it later?
  5. Is witness creation gated by signer checks or capability pattern? Or can any function in the defining module create it?

4b. Witness Forgery Analysis

For each witness used for access control:

1. TypeWitness<T> is required by function F
2. TypeWitness<T> can be created by: {list of functions/modules}
3. Can attacker reach a creation path? {YES/NO --- trace}
4. If YES: attacker creates witness and calls F with unauthorized type T
5. Impact: {unauthorized operation}

5. Coin/FungibleAsset Type Confusion

For all functions that handle Coin<T> or FungibleAsset:

5a. Coin Type Enforcement

FunctionAcceptsType RestrictionEnforcement MechanismBypass Possible?
deposit<T>(coin: Coin<T>)Coin<T>T must be registeredassert!(is_registered<T>()){analysis}

Check for each coin-handling function:

  1. Does the function verify that T is the expected coin type? Or does it accept ANY Coin<T>?
  2. If the function interacts with a pool/vault typed by T, does it verify the coin type matches the pool type?
  3. Can an attacker deposit Coin<FakeCoin> and withdraw Coin<RealCoin>?
  4. For multi-coin functions (swap<X, Y>): are X and Y validated to be a supported pair? Can attacker swap between arbitrary types?

5b. FungibleAsset Metadata Confusion

For protocols using the Aptos Fungible Asset standard:

FunctionMetadata CheckObject Address Validated?Impact If Wrong Metadata
{function}YES --- assert!(metadata == expected) / NOYES/NO{wrong asset deposited/withdrawn}

Aptos FA-specific checks:

  1. FungibleAsset is NOT parameterized by type --- it uses a metadata object address for discrimination. This means type-level enforcement does NOT apply. All FungibleAssets have the same Move type.
  2. Does the function verify the metadata object matches the expected asset? If not, any FungibleAsset can be passed.
  3. Can an attacker create a FungibleAsset with spoofed metadata (same name/symbol as a legitimate asset)?
  4. Are FungibleStore addresses properly validated when reading balances?

Tag: [TRACE:deposit FakeCoin to Pool<RealCoin> → withdraw RealCoin → drain pool]

5c. Mixed Standard Confusion

For protocols that handle BOTH Coin<T> and FungibleAsset:

OperationWhich Standard Used?Consistent?Can Attacker Force Wrong Standard?
depositCoin<T>------
withdrawFungibleAssetMISMATCH{analysis}

Check: If deposit uses Coin<T> but withdraw uses FungibleAsset (or vice versa), is the accounting consistent? The Aptos framework provides conversion between Coin<T> and FungibleAsset, but the conversion path may not be symmetric or may bypass module-level accounting.

6. Module Type Authority

Only the module that defines a struct can create instances of it. This is Move's module encapsulation guarantee. Audit for violations:

6a. Instance Creation Audit

StructDefining ModulePublic Functions That Return New InstancesShould Creation Be Public?
{name}{module}{list or NONE}YES/NO --- {reason}

Check for each struct:

  1. Does ANY public or public(friend) function return a newly created instance of this struct?
  2. If yes, should external callers be able to obtain new instances? For value-bearing structs (coins, shares, receipts), uncontrolled creation = minting vulnerability.
  3. For structs used as capabilities or proofs: is there a public function that creates and returns them to arbitrary callers?
  4. For friend functions that create instances: are ALL friend modules trusted to create instances responsibly?

6b. Friend Module Trust Analysis

ModuleFriend ModulesWhat Friends Can CreateTrust Justified?
{module}{friends list}{structs accessible via friend functions}{analysis}

Check: The friend declaration grants the friend module full access to public(friend) functions, including creation functions. If a friend module has a vulnerability, it can be used to mint/create unauthorized instances.

MANDATORY: For each friend relationship, verify: if the friend module is compromised (has a vulnerability), what is the maximum damage to the declaring module? If friend can create value-bearing structs → severity minimum HIGH.

Finding Template

When this skill identifies an issue:

**ID**: [TS-N]
**Severity**: [based on type confusion impact --- fund loss from wrong type = Critical]
**Step Execution**: check1,2,3,4,5,6 | X(reasons) | ?(uncertain)
**Rules Applied**: [R1:Y, R4:Y, R5:Y, R10:Y]
**Depth Evidence**: [TRACE:substitute T=X → bypass → impact], [BOUNDARY:type=FakeCoin]
**Location**: module::function (source_file.move:LineN)
**Title**: [Function] accepts arbitrary type [T] without [validation], enabling [type confusion/drain/forgery]
**Description**: [Trace from attacker type substitution through function logic to impact]
**Impact**: [Fund drain, unauthorized minting, accounting corruption, pool confusion]

Step Execution Checklist (MANDATORY)

CRITICAL: You MUST report completion status for ALL sections. Steps 2 and 5 are highest priority.

SectionRequiredCompleted?Notes
1. Generic Function InventoryYESY/X/?MANDATORY --- grep ALL .move files
2. Type Parameter SubstitutionYESY/X/?MANDATORY --- highest-severity source
2b. Cross-Pool Type ConfusionIF pools/markets parameterized by typeY/X(N/A)/?
3. Phantom Type AuditIF phantom types usedY/X(N/A)/?
4. Type Witness PatternIF witness pattern usedY/X(N/A)/?
5. Coin/FA Type ConfusionYESY/X/?MANDATORY --- fund loss vector
5b. FungibleAsset MetadataIF FA standard usedY/X(N/A)/?Metadata validation
5c. Mixed Standard ConfusionIF both Coin and FA usedY/X(N/A)/?
6. Module Type AuthorityYESY/X/?Creation function audit
6b. Friend Module TrustIF friend declarations existY/X(N/A)/?Friend compromise analysis

Cross-Reference Markers

After Section 2 (Type Parameter Substitution):

  • IF type substitution enables unauthorized access -> cross-reference with ABILITY_ANALYSIS.md Section 7 for ability constraint gaps
  • IF no enforcement mechanism found -> severity minimum HIGH for value-handling functions

After Section 5 (Coin/FA Type Confusion):

  • Cross-reference with token flow analysis for entry/exit point type validation
  • IF FungibleAsset used without metadata check -> severity minimum HIGH (any FA can be deposited)
  • IF mixed Coin + FA standards -> verify accounting consistency across standards

After Section 6 (Module Type Authority):

  • IF public creation function for value-bearing struct -> severity minimum CRITICAL (unauthorized minting)
  • IF friend module has known vulnerability -> escalate all friend-accessible creation to HIGH minimum

Mandatory Forced Output

For Sections 2 and 5, you MUST produce output even if no issues found:

Section 2 Output (always required):

### 2. Type Parameter Substitution Analysis
| Function | Type Param | Expected Type | Enforcement | Substitution Blocked? |
|----------|-----------|---------------|-------------|----------------------|
| {function} | T | {expected} | {mechanism or NONE} | YES/NO |

**If enforcement = NONE for any value-handling function**: Finding verdict minimum PARTIAL.

Section 5 Output (always required):

### 5. Coin/FungibleAsset Type Confusion
| Function | Standard | Type Restriction | Enforcement | Bypass Possible? |
|----------|---------|-----------------|-------------|-----------------|
| {function} | Coin/FA | {restriction} | {mechanism} | YES/NO |

**If ANY coin-handling function lacks type enforcement**: Verdict CONFIRMED, severity based on fund impact.