Trigger Pattern EXTERNAL_LIB flag (third-party Move dependencies detected in Move.toml beyond Sui framework) - Inject Into Breadth agents, depth-external
Scanned 9/6/2026
Install to Claude Code
npx -y skills add PlamenTSV/plamen --skill dependency-audit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Dependency Audit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/plamentsv-dependency-audit-plamen)More formats (shields.io, HTML) on the badges page.
---
name: "dependency-audit"
description: "Trigger Pattern EXTERNAL_LIB flag (third-party Move dependencies detected in Move.toml beyond Sui framework) - Inject Into Breadth agents, depth-external"
---
# 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).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!