DEV Community

Cover image for The Minimum-Score Problem: Why Bridge Security Audits Miss the Point
turboline-ai
turboline-ai

Posted on

The Minimum-Score Problem: Why Bridge Security Audits Miss the Point

Security pages for cross-chain bridges share a recognizable structure. There is a section on audits, usually listing two or three firm names. There is language about "decentralized validators" or "trustless relaying." Sometimes there is a bug bounty number with a lot of zeros. What is almost never present is a clear mapping between those claims and something you can actually verify yourself.

This is not an oversight. Compliance-style language is genuinely useful for filling space where cryptographic guarantees are hard to explain or, in some cases, do not yet exist. The problem is that most developers read security pages like a checklist: enough boxes ticked means the bridge is probably safe. That mental model is exactly what gets exploited.

Why Averages Don't Survive Contact With Attackers

The Ronin bridge lost $625 million because five of its nine validators were controlled by a single entity, Sky Mavis. The Wormhole exploit in 2022 came down to a signature verification bug that bypassed guardian consensus entirely. Nomad collapsed because a single configuration change made every message valid by default.

None of these projects had weak security across the board. They had one weak layer. And that is the point: attackers do not average across your security surface. They find the minimum and push on it until something breaks.

This is why a composite security score calculated as an average is nearly useless for bridges. A bridge that scores 9/10 on validator independence, 8/10 on timelock governance, and 2/10 on L1 verification does not have an overall score of 6.3. It has a critical vulnerability. The minimum score across all layers is the number that matters.

A Framework Built Around the Weakest Layer

When evaluating a bridge, there are four dimensions worth scoring independently, and the lowest score is the one you should report:

L1 verification: Does the bridge actually verify proofs on the destination chain's L1, or does it rely on an intermediary oracle or relayer to assert validity? Light client verification and ZK proof verification are verifiable on-chain. "Our team monitors all transactions" is not.

Validator independence: How many independent parties can collude to authorize a fraudulent withdrawal? Count the actual signers, not the claimed count. Check whether multiple validator keys are controlled by the same organizational entity.

Upgrade controls: Can the bridge contracts be upgraded, and if so, who controls that? A multisig with three signers is not meaningfully decentralized. A timelock with a 24-hour delay and a multisig that can cancel it is only as strong as whoever controls that multisig.

Timelocks on governance: Separate from upgrade controls, does any parameter change or governance action have a forced delay before execution? This is the one mechanism that gives users time to exit before a malicious change takes effect.

Everything Here Is Verifiable With cast

The value of this framework is that none of it requires trusting the bridge's own documentation. You can check most of it directly with Foundry's cast tool and a public RPC endpoint.

To check who controls an upgradeable proxy:

cast storage <proxy_address> \
  0x360894a13ba1a3210667c828492db98dca3e2076 \
  --rpc-url <your_rpc_url>
Enter fullscreen mode Exit fullscreen mode

That storage slot is the EIP-1967 implementation slot. If you get back a contract address, check whether that address has an owner or a multisig controlling it.

To check for a timelock, look at the contract that owns the proxy. If it is a TimelockController, you can read the minimum delay:

cast call <timelock_address> "getMinDelay()(uint256)" \
  --rpc-url <your_rpc_url>
Enter fullscreen mode Exit fullscreen mode

A return value of 0 means changes can be executed immediately. A return value of 86400 means 24 hours. Neither is inherently safe, but now you know the actual number instead of what a security page claims.

For validator sets, most bridge contracts expose a function to read current signers or required thresholds. The function name varies by implementation, but the data is almost always public. You are looking for the ratio of required signatures to total signers, and then separately evaluating whether the entities behind those keys are actually independent.

What "Audited" Actually Tells You

An audit tells you that a specific set of contract code was reviewed at a specific point in time. It does not tell you whether those contracts have been upgraded since, whether the deployment matches the audited commit, or whether the threat model the auditors used matches how the bridge is actually deployed.

To verify a deployed contract matches a known source:

cast etherscan-source <contract_address> --chain <chain_name>
Enter fullscreen mode Exit fullscreen mode

Then compare the deployed bytecode hash against what the audit report references. If the bridge has been upgraded and the new implementation has not been re-audited at equivalent depth, that gap is yours to carry, not theirs.

The Concrete Takeaway

Security pages describe intent. On-chain state describes reality. For any bridge holding meaningful value in your stack, run the verification steps above before you rely on the landing page copy. Score the four dimensions independently, take the minimum, and treat that number as your actual risk exposure. If one of those dimensions comes back unverifiable because the contracts are not open source or the logic is inside an opaque relayer, that absence is itself a score: zero.

The bridges that have failed were not mystery boxes. The information needed to identify the weak layer was available before the exploit. It just required looking somewhere other than the security page.

Top comments (0)