Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 5 min read

Your audit covers a commit, not your launch

Every audit report names a commit hash in its scope section, ours included. That hash is the whole claim: at that commit, inside that scope, in that many days, these reviewers found these issues. The report says nothing

Every audit report names a commit hash in its scope section, ours included. That hash is the whole claim: at that commit, inside that scope, in that many days, these reviewers found these issues. The report says nothing about the code you deploy three weeks later, the script that deploys it, or the people who hold the keys afterwards.

A lot of money has gone missing in that gap. Some of the biggest losses of the last few years came from code that changed after the review, or from parts nobody reviewed at all.

How a clean report goes stale

Fix commits and small upgrades. Euler lost about $197M in March 2023 through donateToReserves, a function added in a July 2022 upgrade. That upgrade had its own review, and the function sat in production for eight months with a bug bounty running. A fix or a small upgrade usually gets less attention than the original codebase, and it changes code everyone already considered done.

The upgrade itself. In August 2024 Ronin's bridge lost about $12M after an upgrade ran initializeV4 but never initializeV3. _totalOperatorWeight stayed at zero, so the minimum vote weight check passed without the operator votes it was there to require. The contract logic was fine. The deployment wasn't.

Nomad (August 2022, about $190M) is the same family. An upgrade initialized the trusted root to 0x00, and since an unproven message maps to that same zero root, any message passed as proven.

The compiler and the things you call. In July 2023 a Vyper compiler bug broke the reentrancy lock in versions 0.2.15, 0.2.16 and 0.3.0, and several Curve pools lost around $70M. The source code was fine. Price feeds get deprecated, a lending market changes its API, a liquid staking token stops trading at 1:1. Each of those changes the assumptions the report was written against.

New features. Penpie lost about $27M in September 2024 through a feature that let anyone register a Pendle market. A market with a fake SY token re-entered the reward harvest.

Governance. A new role grant, a new collateral, an oracle swap, a shorter timelock. Each one changes the threat model even when no code changes.

Check that what you deploy is what was audited

Verify the source on Etherscan or Sourcify and compare it with the commit in the report. Then diff every commit after it: the fixes, the deploy script, and the constructor and initializer arguments. The deploy script runs once with full privileges, and it's often outside the audit scope.

For every upgradeable contract, write down which initializers must run and in which order, and check the resulting storage before you unpause. A fork test that runs the real upgrade script against mainnet state and then reads the values you care about catches the Ronin 2024 case.

Watch the privileged events, not only the balances

Ronin's 2022 loss ($625M) was found six days after the fact, when a user couldn't withdraw. A signing permission granted for a gas-free RPC program in November 2021 was still active months after the program ended, and nobody was watching it.

Fund flow is the floor: outbound transfers above your normal range, freshly deployed destination contracts, bursts. Above that, alert on every privileged event: Upgraded and the EIP-1967 implementation slot, OwnershipTransferred, RoleGranted and RoleRevoked, and the timelock's QueueTransaction and ExecuteTransaction. For oracles, compare updatedAt against the feed's heartbeat, and on L2s read the sequencer uptime feed too.

Then point your test invariants at live state. If totalDebt <= totalCollateral * maxLTV is a property in your fuzz suite, a bot can read the same values every block and pause when it breaks. EIP-7265 proposes a standard circuit breaker that delays or blocks outflows above a threshold. It's still a draft, but nothing stops you from building the same check into your own contracts.

In CI, a split that works: a short fuzz run on every PR, longer Echidna or Medusa campaigns nightly, multi-hour runs before a release, with the corpus cached between runs.

A pause doesn't stop a stolen admin key

Pause helps against code bugs: a reentrancy, an oracle manipulation, a math error. It does little when the attacker holds the role that can upgrade the contract, because that role can unpause or upgrade the pause away. At Radiant in October 2024, three hardware wallet signatures were enough to call transferOwnership, and with the owner role the attacker could swap the contracts.

So decide what your guardian can do besides pausing. Compound's Pause Guardian is worth reading: it can pause minting, borrowing, transfers and liquidations, it can never block redeem or repay, and it can't unpause. Put upgrades and ownership transfers behind a timelock that your monitoring watches. Compound's Timelock (2 day minimum delay, 30 day maximum, 14 day grace period) is a common reference.

Treat signing as attack surface

Radiant and Bybit (February 2025, about $1.5B) weren't contract bugs. In both, the signers saw one transaction in the Safe interface and approved another. At Radiant, malware on the developers' machines showed legitimate data in the Safe UI while their hardware wallets received the malicious calldata.

  • Set a threshold that is a real share of the signers, not three out of many.
  • Mix hardware wallet vendors across signers.
  • No blind signing. Decode the calldata on a second device or with an independent tool, and compare the Safe transaction hash before confirming on the device.
  • A transaction that "failed" and needs to be signed again is an incident until proven otherwise.
  • Rotate a signer with one swapOwner call instead of a remove followed by an add.

Have the contacts before you need them

Write a runbook with names in it: who decides, who reproduces and patches, who talks publicly, who calls exchanges and law enforcement, who keeps the timeline in UTC. Run through it once before launch.

Know how to reach SEAL 911 (the Security Alliance's emergency Telegram bot, @seal_911_bot) before launch, so nobody is searching for it during an exploit. Read the Safe Harbor agreement too. A protocol adopts it in advance, and it lets whitehats rescue funds during an active exploit for a capped bounty, by default 10% up to $1M. Adopting it mid-incident is too late.

Trail of Bits' Rekt Test is twelve yes or no questions, such as "Do you have a written and tested incident response plan?" and "Do you require hardware security keys for production systems?". Answer it for the code and the team you're launching with. The questions about incident plans, hardware keys and key management are outside what a code audit looks at.

Adapted from a longer piece our team published on zealynx.io, which also covers monitoring tools, a maturity model and more incidents.

I wrote this with help from AI for drafting and editing. The opinions and the audit experience are mine.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.