| name | audit-from-bugs |
| description | Audit skill derived from 27 confirmed real OP_NET bugs. Triggers on: audit, review, security check, code review, PR review of OPNet code.
Covers: btc-runtime, native-swap, opnet, opnet-node, op-vm, transaction repos.
Categories: serialization, storage/pointer, arithmetic/AMM, access-control/crypto,
business-logic, memory/bounds, gas/runtime, networking/indexer, type-safety.
|
audit-from-bugs Skill
You are performing a security audit using patterns derived from 27 real OP_NET bugs. Apply ALL checks to every file you review. Do not stop at the first finding.
Reference
Full pattern documentation with code examples: knowledge/slices/security-audit.md (section "Real-Bug Vulnerability Patterns")
The 27 Checks
SERIALIZATION (5 checks)
-
[CRITICAL] PAT-S1: Generic integer read โ Search for value[0] as T in any generic read/deserialize method. This returns only the low byte of any integer. Must use new BytesReader(value).read<T>(). (btc-runtime PR #137)
-
[CRITICAL] PAT-S2: BytesReader/BytesWriter offset โ For every readUXX() / writeUXX() method: must read/write at currentOffset (not currentOffset + BYTE_LENGTH), must increment currentOffset += BYTE_LENGTH after. (btc-runtime PR #57)
-
[HIGH] PAT-S3: Save/load type matrix โ Build a matrix of every writeU8/16/32/64() in save() paired with readU8/16/32/64() in load(). Any mismatch = upper bytes silently truncated. (btc-runtime PR #88)
-
[HIGH] PAT-S4: Hex prefix stripping โ Search for .replace('0x', ''). ALWAYS wrong for hex stripping. Replace with str.startsWith('0x') ? str.slice(2) : str. (opnet PR #135)
-
[MEDIUM] PAT-S5: Implicit integer narrowing โ Every writeU16(x), writeU8(x), writeU32(x) โ verify x is explicitly cast to the matching type or is already that type. (btc-runtime PR #52)
STORAGE / POINTER (3 checks)
-
[CRITICAL] PAT-P1: Key concatenation collision โ Any template literal joining two string keys: `${a}${b}` without a delimiter. Must use length-prefix: `${a.length}:${a}${b.length}:${b}`. (btc-runtime PR #61)
-
[HIGH] PAT-P2: Conditional hash bypass โ Search for typed.length !== 32 ? hash(typed) : typed or similar. Always hash unconditionally. (btc-runtime PR #61)
-
[HIGH] PAT-P3: verifyEnd parameter โ In verifyEnd(size), condition must be size > buffer.byteLength, NOT currentOffset > buffer.byteLength. (btc-runtime PR #60)
ARITHMETIC / AMM (4 checks)
-
[HIGH] PAT-A1: Silent zero return for undefined math โ Search for if (x.isZero()) return u256.Zero in functions where zero input is undefined (log, sqrt of negative). Must throw new Revert(...). (btc-runtime PR #129)
-
[CRITICAL] PAT-A2: AMM constant-product integrity โ Any reserve update function: verify BOTH sides maintain k = B * T. Pattern T -= dT; B += dB independently is WRONG. Must: k = B*T; T -= dT; B = k/T. (native-swap PR #63)
-
[CRITICAL] PAT-A3: Proportional reserve removal on purge/slash โ Any function removing tokens from virtual token reserve must remove proportional BTC: btcOut = (tokens * virtualBTC) / virtualTokens. Both updated atomically. (native-swap PR #51)
-
[HIGH] PAT-L2: Trade accumulator exhaustion โ Before adding to any buy/sell accumulator, verify total + new < projectedReserve. Revert if accumulation would exhaust the pool. (native-swap PR #63)
ACCESS CONTROL / CRYPTO (4 checks)
-
[CRITICAL] PAT-C1: Signature replay protection โ Any verifySignature call: verify the signed payload includes a per-address monotonically-increasing nonce. Increment after success. (btc-runtime PR #60)
-
[HIGH] PAT-C2: Selector type string accuracy โ Every encodeSelector('funcname(type1,type2,...)') call: cross-check every type against actual AssemblyScript parameter types. (btc-runtime PR #61, PR #60)
-
[CRITICAL] PAT-C3: Decrypt returns null on failure โ Any decrypt function: return type must be T | null. NEVER return the original ciphertext on failure. (opnet-node PR #192)
-
[HIGH] PAT-C4: EC public key prefix validation โ Any code identifying EC public key by buffer length: must also validate buf[0] in {0x02, 0x03} for 33 bytes, or buf[0] === 0x04 for 65 bytes. (opnet-node PR #225)
BUSINESS LOGIC (2 checks)
-
[CRITICAL] PAT-L1: CEI order in AMM activation โ Provider activation that reads liquidity for reserve calculation must occur BEFORE any subtract/decrease on that provider's liquidity in the same call path. (native-swap PR #67)
-
[HIGH] PAT-L3: UTXO commitment placement โ reportUTXOUsed() / markUTXOSpent() must only appear in confirmed-success paths, never at a point where both success and failure branches have merged. (native-swap PR #48)
MEMORY / BOUNDS (2 checks)
-
[HIGH] PAT-M1: Array push off-by-one โ All push() bounds checks: > MAX must be >= MAX for 0-indexed arrays where valid indices are 0..MAX-1. (btc-runtime PR #61)
-
[HIGH] PAT-M2: Memory padding offset โ In write-data-then-pad-to-length operations: track bytes_written = slice.len(), use that as padding base offset, NOT source_buffer.len(). (op-vm PR #130)
GAS / RUNTIME (2 checks)
-
[HIGH] PAT-G1: Gas capture before ExitData โ In any VM exit handler: get_used_gas() must be called BEFORE constructing the ExitData/ExitResult. (op-vm PR #109)
-
[CRITICAL] PAT-G2: Mutex double-lock โ In Rust async code: search for two mutex.lock() calls on the same mutex within the same task/scope. Second lock while first guard is alive = deadlock. (op-vm PR #77)
NETWORKING / INDEXER (2 checks)
-
[HIGH] PAT-N1: Null-safe Buffer construction โ Any Buffer.from(x, 'hex') || fallback where x may be null/undefined: || doesn't catch exceptions. Must be x ? Buffer.from(x, 'hex') : fallback. (opnet-node PR #218)
-
[HIGH] PAT-N2: Promise resolve in search loop โ Any Promise that resolves a found-item: resolve INSIDE the loop at the point of match (resolve(item); return;), not outside after the loop ends. (opnet-node PR #192)
TYPE SAFETY (3 checks)
-
[MEDIUM] PAT-T1: Abstract interface return types โ Every abstract method implementing an external interface: return type must exactly match the interface specification. (transaction PR #86)
-
[MEDIUM] PAT-T2: Browser ECPair RNG โ Any ECPair.makeRandom() without custom rng option in browser-capable code: must provide rng: (size) => Buffer.from(randomBytes(size)). (transaction PR #109)
-
[MEDIUM] PAT-T3: Reassigned variable used for fallback โ Any code pattern x = x[key]; if (!x) x = x[fallbackKey] โ after the first assignment, x is no longer the original map. Preserve original reference. (opnet PR #78)
Output Format
For each finding:
**[SEVERITY] PAT-XX: Title**
- File: path/to/file.ts
- Line: N
- Pattern: which pattern above triggered
- Evidence: the actual code fragment
- Impact: what can go wrong
- Fix: specific remediation
- Ref: original bug PR
Always check ALL 27 patterns. Do not stop at the first finding.
Severity Definitions (OPNet Context)
- CRITICAL: Can drain funds, corrupt consensus state, enable replay attacks, cause node deadlock/crash
- HIGH: Can corrupt indexes, bypass authentication, cause logic errors that affect trades or balances
- MEDIUM: Type mismatches, browser compatibility, silent wrong behavior without financial impact
- LOW: Code quality, missing error messages, minor inconsistencies