Cross-Contract Taint Remediation
Overview
Related Detector: Cross-Contract Taint
When the return value of an untrusted external call flows into a sensitive sink — a delegatecall target, a transfer amount, an access-control decision, a storage key — the callee controls your contract’s behaviour. The remediation is to treat every external return value as untrusted input and validate it against your own state before it reaches a sink.
Recommended Fix
Before (Vulnerable)
function settle(address quoter) external {
// The quoted amount comes from an untrusted contract and is paid out directly.
uint256 amount = IQuoter(quoter).quote();
payable(msg.sender).transfer(amount);
}
After (Fixed — validate against trusted state and bounds)
function settle(address quoter) external {
require(trustedQuoter[quoter], "Untrusted quoter"); // constrain the source
uint256 amount = IQuoter(quoter).quote();
require(amount <= owed[msg.sender], "Exceeds entitlement"); // bound by own accounting
owed[msg.sender] -= amount; // effect before interaction
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "Transfer failed");
}
Sensitive sinks ranked by risk: a tainted delegatecall target is critical, tainted call targets and values are high. Never let any of them derive from an unvalidated external return.
Common Mistakes
- Trusting a return value because the interface is known, ignoring that the implementation is attacker-controlled.
- Validating the amount but letting a tainted address reach a
call/delegatecalltarget. - Using an external contract’s returned index or id directly as a storage key.