# Dependency Audit

> Trigger Pattern EXTERNAL_LIB flag (third-party Move dependencies detected in Move.toml beyond Sui framework) - Inject Into Breadth agents, depth-external

- Skill: `plamentsv/dependency-audit` (Agent Skill)
- Install (CLI): `npx skillmds@latest add plamentsv/dependency-audit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/plamentsv/dependency-audit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: plamentsv (https://skillmd.com/u/plamentsv)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/plamentsv/dependency-audit

---


# Skill: DEPENDENCY_AUDIT (Sui/Move)

> **Trigger Pattern**: EXTERNAL_LIB flag (third-party Move dependencies detected in Move.toml beyond Sui framework)
> **Inject Into**: Breadth agents, depth-external
> **Finding prefix**: `[DEP-N]`
> **Rules referenced**: R1, R4, R8, R10

Move's dependency model is package-based: `Move.toml` declares dependencies with git URLs and revisions. Unlike EVM's compiled-and-deployed model where dependencies are inlined at compile time, Sui Move packages can depend on other PUBLISHED packages (on-chain) or source packages (compiled together). Third-party math libraries, utility packages, and protocol SDKs are common dependency vectors.

**STEP PRIORITY**: Steps 3 (Critical Function Audit, especially Step 4 Math Library Audit) and 5 (Shared Object Dependencies) are where HIGH/CRITICAL severity findings most commonly hide. The Cetus hack originated from a custom math library bit shift bug. Do NOT rush these steps.

---

## Trigger Patterns

```
[dependencies]|git\s*=|subdir\s*=|rev\s*=|published-at|math|utils|library|helpers|common
```

---

## Step 1: Dependency Inventory

Parse `Move.toml` and build a complete dependency tree. Categorize:

| # | Dependency Name | Source Type | Source URL/Address | Version/Rev Pinned? | Trust Level | Upgrade Risk |
|---|----------------|------------|-------------------|---------------------|-------------|-------------|
| 1 | Sui | Framework | sui framework | Validator-controlled | TRUSTED | Framework upgrade by validators |
| 2 | MoveStdlib | Framework | std library | Validator-controlled | TRUSTED | Framework upgrade by validators |
| 3 | {third_party} | Git source | {url} | YES (rev={hash}) / NO (branch) | MUST_AUDIT | {describe} |
| 4 | {on_chain_dep} | Published | {on-chain address} | YES (version pinned) / NO | MUST_AUDIT | {describe} |
| 5 | {protocol_own} | Local path | {path} | N/A (in scope) | IN_SCOPE | N/A |

**Trust classification**:
- **TRUSTED**: Sui framework packages (`sui`, `std`). Audited by Mysten Labs, upgraded by validator governance. Minimal audit needed (but check for version-specific quirks).
- **MUST_AUDIT**: Third-party packages. MUST analyze critical functions used by the protocol.
- **IN_SCOPE**: Protocol's own packages. Full audit in main analysis.

---

## Step 2: Package Immutability Check

For each third-party dependency, assess immutability and upgrade risk:

| Dependency | Pinned to Specific Rev? | Published On-Chain? | UpgradeCap Status | Upgrade Policy | Risk |
|-----------|------------------------|--------------------|--------------------|---------------|------|
| {dep} | YES (rev: {hash}) / NO (branch: main) | YES/NO | Destroyed (immutable) / Held by {who} / UNKNOWN | {compatible/additive/dep_only/immutable} | {assess} |

**Source dependencies** (compiled together):
- Pinned to specific git revision -> code is fixed at that commit. Safe from upstream changes.
- Pinned to a branch (e.g., `main`) -> upstream pushes automatically affect next compilation. **FINDING**: unpinned dependency.
- No `rev` field -> defaults to latest on default branch. Highest risk.

**Published on-chain dependencies** (referenced via `published-at`):
- Immutable package (UpgradeCap destroyed) -> behavior cannot change. Safe.
- Package with active UpgradeCap + `compatible` policy -> behavior CAN change.
- Your package pins to a specific version at compile time. If dependency publishes V2, you still use V1.
- **Risk**: When YOU upgrade (recompile), you may pull in dependency's latest version unknowingly.

**Known upgrade history**: Has the dependency been upgraded before? How many versions exist? Frequent upgrades indicate active development but also active change risk.

**Checklist**:
- [ ] Every third-party dependency is pinned to a specific git revision (not a branch)
- [ ] Published dependencies are either immutable or their upgrade policy is documented
- [ ] No dependency uses a `latest` or `main` branch reference

---

## Step 3: Transitive Dependency Risk

Map the full dependency tree:

| Dependency A | Depends On | Dep B Audited? | Dep B Upgrade Risk | Version Conflict? |
|-------------|-----------|---------------|-------------------|------------------|
| {dep_A} | {dep_B, dep_C} | YES/NO | {describe} | YES/NO |

**Transitive dependency risks**:
- A -> B -> C: If C has vulnerability, A is affected even though A does not directly import C
- Version conflicts: If A depends on C v1 and B depends on C v2, Move compilation may fail. Sui resolves diamond dependencies by requiring all paths to agree on the same version.
- Transitive upgrade: If B upgrades and changes its dependency on C, your next recompile may pull different C code.

**If Dep B upgrades, does it affect us through Dep A?**
- Only if we recompile our package (Sui does not dynamically resolve dependencies)
- But: if Dep B is an on-chain published package that Dep A calls via CPI-equivalent, behavior changes immediately after Dep B upgrades

---

## Step 4: Math Library Audit (CRITICAL -- Cetus Precedent)

> **Historical context**: A major DeFi exploit targeted a bug in a custom bit shift helper function in a math library. This step is MANDATORY for any custom math/arithmetic library in the dependency tree.

For any custom math/arithmetic library dependency:

### 4a. Bit Shift Operation Audit (MR2)

Trace ALL bit shift operations (`<<`, `>>`) in the math library:

| # | Function | Shift Operation | Shift Amount Source | Bounds Checked? | Overflow Possible? |
|---|----------|----------------|--------------------|-----------------|--------------------|
| 1 | {func} | `value << amount` | {parameter / constant / computed} | YES/NO | YES/NO |

**Move bit shift rules**:
- `<<` and `>>` do NOT abort if shift amount >= bit width -- they produce 0
- Custom bit shift helpers MUST validate shift amount < bit width
- If shift amount comes from user input or computation, it must be bounds-checked

**Specific checks**:
- [ ] Are ALL shift amounts validated to be < bit width of the operand type?
- [ ] Do custom bit shift helpers correctly handle edge cases (shift amount >= bit width, zero inputs, overflow)?
- [ ] Can intermediate computation produce a shift amount >= bit width?
- [ ] Are there any bit manipulation patterns that assume shift produces a specific non-zero result?

### 4b. Overflow/Underflow Audit

| # | Function | Operation | Input Range | Overflow Possible? | Handling |
|---|----------|-----------|------------|--------------------|---------|
| 1 | {func} | `a * b` | {describe} | YES if a,b > sqrt(MAX_U128) | abort (safe) / wrapping (DANGEROUS) |

**Move arithmetic safety**:
- Default `+`, `-`, `*` abort on overflow/underflow -- safe
- But: custom math libraries may use bitwise operations to implement unchecked arithmetic for gas optimization
- `as` casts between integer types abort on overflow (e.g., `(x as u64)` where x > MAX_U64)
- Fixed-point: `(a * b) / SCALE` -- intermediate `a * b` may overflow u128 even if final result fits in u64

### 4c. Rounding and Precision

| # | Function | Rounding Direction | Consistent? | Impact if Wrong Direction |
|---|----------|-------------------|-------------|--------------------------|
| 1 | {mul_div} | {up / down / nearest / truncation} | YES/NO | {describe: e.g., attacker extracts extra dust per operation} |

**Check**: For every division operation in the math library:
- Is rounding direction documented?
- Is rounding direction consistent with how the protocol uses the result?
- Can rounding errors accumulate across many operations?

---

## Step 5: Shared Object Dependencies

If the protocol uses shared objects from external packages:

| External Shared Object | Package | Our Functions That Access It | What We Read/Write | Behavior Change If Package Upgrades? |
|-----------------------|---------|----------------------------|-------------------|--------------------------------------|
| {oracle_obj} | {oracle_pkg} | {our_module::read_price} | READ price field | YES -- oracle upgrade could change price format |
| {dex_pool} | {dex_pkg} | {our_module::swap} | WRITE (swap) | YES -- DEX upgrade could change swap logic |

**Are we validating shared object state after external calls?**
- After reading price from external oracle shared object: do we validate freshness? bounds? format?
- After calling external DEX swap: do we validate received amount? slippage?
- If external package upgrades and changes shared object behavior, our code reads different data without any change on our side.

**Key risk**: External package with `compatible` upgrade policy can change function implementations. Our calls to those functions produce different results after upgrade, with no code change or compilation on our side.

---

## Step 6: Interface Compatibility

Could new abort conditions be added in dependency upgrades?

| Dependency Function | Current Abort Conditions | Possible New Abort Conditions | Impact on Our Protocol |
|-------------------|------------------------|-----------------------------|----------------------|
| {dep::func} | {list current aborts} | {what upgrades could add} | {describe: e.g., our transaction aborts unexpectedly} |

**Check**:
- If a dependency function currently never aborts but an upgrade adds an abort condition -> our protocol's transactions may start failing
- If a dependency function changes its return value semantics (e.g., rounding direction changes) -> our calculations become incorrect
- If a dependency adds new type constraints -> our generic calls may no longer compile on next recompile

---

## Key Questions (Must Answer All)

1. **Pinning**: Are all third-party dependencies pinned to specific git revisions?
2. **Critical functions**: For each math/utility function from a dependency, does it handle edge cases correctly?
3. **Bit shifts**: Are ALL bit shift operations in math libraries bounds-checked? (Cetus precedent)
4. **Upgrade risk**: Can any dependency change behavior without the protocol team's knowledge?
5. **Shared objects**: If we use shared objects from external packages, can their behavior change via upgrade?
6. **Transitive**: Are there transitive dependencies, and are they audited?

---

## Common False Positives

1. **Framework dependencies**: `sui::*` and `std::*` are validator-controlled and well-audited. Findings about framework functions are rarely valid unless version-specific.
2. **Pinned and immutable**: Dependency pinned to specific rev AND on-chain package is immutable -> no upgrade risk.
3. **Unused imports**: Dependency imported but no functions actually called -> no runtime risk.
4. **Well-known libraries**: Widely-used and audited libraries with specific rev pinning -> lower risk, but STILL check edge cases for specific functions used.

---

## Output Schema

```markdown
## Finding [DEP-N]: Title

**Verdict**: CONFIRMED / PARTIAL / REFUTED / CONTESTED
**Step Execution**: check1,2,3,4,5,6 | skip(reason) | uncertain
**Rules Applied**: [R1:___, R4:___, R8:___, R10:___]
**Severity**: Critical/High/Medium/Low/Info
**Location**: Move.toml or sources/{module}.move:LineN (where dep function is called)

**Dependency**: {dependency_name}
**Function**: {specific function if applicable}
**Issue Type**: UNPINNED_VERSION / ARITHMETIC_UNSAFE / BIT_SHIFT_UNSAFE / EDGE_CASE_UNHANDLED / SPEC_MISMATCH / TRANSITIVE_RISK / UPGRADE_RISK / SHARED_OBJECT_DEP

**Description**: What is wrong
**Impact**: What can happen (incorrect calculation, overflow, unexpected abort, supply manipulation)
**Evidence**: Code showing the issue
**Recommendation**: How to fix (pin version, add validation, use alternative, wrap with checks)
```

---

## Step Execution Checklist (MANDATORY)

| Step | Required | Completed? | Notes |
|------|----------|------------|-------|
| 1. Dependency Inventory | YES | | All deps from Move.toml enumerated |
| 2. Package Immutability Check | YES | | Pinning and on-chain policy for each dep |
| 3. Transitive Dependency Risk | YES | | Full dependency tree mapped |
| 4. Math Library Audit | IF math/arithmetic deps exist | | **HIGH PRIORITY** -- Cetus precedent |
| 4a. Bit Shift Operation Audit | IF bit shifts in math deps | | Every shift bounds-checked |
| 4b. Overflow/Underflow Audit | IF math deps | | Checked vs unchecked arithmetic |
| 4c. Rounding and Precision | IF division in math deps | | Direction documented and consistent |
| 5. Shared Object Dependencies | IF external shared objects used | | Behavior change on upgrade |
| 6. Interface Compatibility | IF upgradeable deps | | New abort conditions, return value changes |

### Cross-Reference Markers

**After Step 2**: If any dependency unpinned -> immediate Informational/Low finding.

**After Step 4**: If math library has unchecked bit shifts -> cross-reference with BIT_SHIFT_SAFETY skill for protocol-level impact analysis.

**After Step 5**: If shared object dependencies from upgradeable packages -> cross-reference with PACKAGE_VERSION_SAFETY Step 3 and EXTERNAL_PRECONDITION_AUDIT Step 3b.

If any step skipped, document valid reason (N/A, no third-party deps, framework-only, no math functions used).

