Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 67
Period: 5 days
Judge: Picodes
Total Solo HM: 7
Id: 172
League: ETH
Rank: 20/67
Findings: 1
Award: $342.00
π Selected for report: 0
π Solo Findings: 0
π Selected for report: berndartmueller
Also found by: 0x1f8b, 0x4non, 0xNazgul, 0xSmartContract, Aymen0909, BClabs, Diana, Jeiwan, Lambda, LeoS, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Rolezn, SaharAP, Trust, V_B, __141345__, a12jmx, bharg4v, brgltd, carlitox477, ch0bu, chaduke, cloudjunky, cryptostellar5, cryptphi, csanuragjain, d3e4, delfin454000, erictee, fatherOfBlocks, hansfriese, ignacio, joestakey, karanctf, ladboy233, lukris02, mcwildy, minhtrng, peanuts, ret2basic, seyni, slowmoses, svskaushik, tnevler, yixxas
341.9967 USDC - $342.00
JBTiered721DelegateDeployer.deployDelegateFor
cast every governance type to JB721GlobalGovernance
.
If the chosen governance contract type is JB721TieredGovernance
or JB721GlobalGovernance
, the cloned contract is casted to the wrong contract type (JB721GlobalGovernance
).
Therefore, regardless of the choice of the caller, the contract type of the delegate is JB721GlobalGovernance
instead of the chosen type of governance.
There is 3 types of delegate depending to the governance type attached to it:
In the JBTiered721DelegateDeployer.deployDelegateFor
function the newly deployed delegate of the chosen type of the caller is casted to the JB721GlobalGovernance
contract type and assigned to the newDelegate
variable of type JB721GlobalGovernance
regardless of the governance type.
JB721GlobalGovernance newDelegate = JB721GlobalGovernance(_clone(codeToCopy));
Manual review.
I recommend to clone, deploy and cast to the corresponding contract type depending on the governance type of the delegate in the conditionnal statements like this:
if (_deployTiered721DelegateData.governanceType == JB721GovernanceType.NONE) codeToCopy = address(noGovernance); JBTiered721Delegate newDelegate = JBTiered721Delegate(_clone(codeToCopy)); else if (_deployTiered721DelegateData.governanceType == JB721GovernanceType.TIERED) codeToCopy = address(tieredGovernance); JB721TieredGovernance newDelegate = JB721TieredGovernance(_clone(codeToCopy)); else if (_deployTiered721DelegateData.governanceType == JB721GovernanceType.GLOBAL) codeToCopy = address(globalGovernance); JB721GlobalGovernance newDelegate = JB721GlobalGovernance(_clone(codeToCopy)); else revert INVALID_GOVERNANCE_TYPE();
From my understanding the team want the return variable of the JBTiered721DelegateDeployer.deployDelegateFor
function to be a governance agnostic delegate, which make sense considering how the variable is used in JBTiered721DelegateProjectDeployer
(only to set the delegate address as the data source). Therefore using IJBTiered721Delegate
as return variable type is fine.
#0 - drgorillamd
2022-10-24T11:19:43Z
Good catch
No value leak/functional impact imo (the interface has unimplemented functions, without fallback, if the caller "forget" what they wanted to deploy, calling these "ghost" fn will revert)
#1 - Picodes
2022-11-04T19:34:25Z
Downgrading as QA as there is no risk involved with the finding.
#2 - c4-judge
2022-11-08T08:43:25Z
Picodes changed the severity to QA (Quality Assurance)
#3 - c4-judge
2022-11-08T16:44:49Z
Picodes marked the issue as grade-a