OpenZeppelin / OpenZeppelin/compact-contracts
L-03: Inefficient Module Composability Can Lead To Confused Deputy Behavior
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 55
- Forks
- 29
- Avg merge
- 5d 7h
- Merged PRs (30d)
- 25
Description
L-06 Inefficient Module Composability Can Lead To Confused Deputy Behavior
Severity: Low
Source: Midnight Foundation #02 — OpenZeppelin Compact Contracts Audit, Release 0.3.0-alpha.1 (2026-08-18)
Several modules derive the calling identity from their own witness. Ownable derives it from wit_OwnableSK, AccessControl from wit_AccessControlSK, and the account-based token modules each from their own secret, so that FungibleToken acts on left(_computeAccountId()) derived from wit_FungibleTokenSK. Witnesses are offchain code supplied by the prover, and nothing constrains two of them to return the same value.
A composed circuit that authorizes through one module and acts through another therefore binds its authorization and its effect to different identities. A prover can satisfy the ownership witness with the real owner's secret so that assertOnlyOwner passes, satisfy the token witness with a secret of their own, and have the resulting transfer or mint credited to the second identity. The authorization check is sound in isolation and the effect is sound in isolation; only their pairing is unconstrained, which is the shape of a confused deputy and is not visible at the call site. The library already contains the remedy in one place: ConfidentialFungibleToken has each caller-authenticating circuit return the identifier it authenticated precisely so a wrapper gates the value that was actually used, and its header explains that a separate witness call in the wrapper could return a different secret. That convention was not propagated to the modules named above, whose internal identity derivations are non-exported, so a composer has no sound value to compare against.
Consider deriving one caller identity in the composing circuit and asserting equality across the modules it drives, or preferring the internal circuits which accept the principal as an explicit argument rather than reading a module-local witness. It is further advisable to state in each module that its derived identity is independent of every other module's, since a composed circuit that assumes a single caller is unsound unless that equality is added.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked caller-authentication circuits in Ownable.compact, AccessControl.compact, FungibleToken.compact, and ConfidentialFungibleToken.compact. Compare how each derives or returns the authenticated identity, then review the audit’s composition scenario. Done means the project has a decided, consistently applicable approach for preserving caller identity across composed circuits and states the required module guidance or changes.
Written by the indexing model from the issue text.
Assessment
- Domain
- blockchain, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100