Skip to content

Commit 6143382

Browse files
authored
Audit Fixes 3: Concurrent admin_execute calls may lead to unexpected behavior (#549)
1 parent 5734085 commit 6143382

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

framework/contracts/account/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ pub enum AccountError {
106106
#[error("The caller ({caller}) is not the owner account's account ({account}). Only account can create sub-accounts for itself.", )]
107107
SubAccountCreatorNotAccount { caller: String, account: String },
108108

109+
#[error("You can't chain admin calls")]
110+
CantChainAdminCalls {},
111+
109112
#[error("Abstract Account Address don't match to the Contract address")]
110113
AbsAccInvalidAddr {
111114
abstract_account: String,

framework/contracts/account/src/execution.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ pub fn admin_execute(
9191
) -> AccountResult {
9292
ownership::assert_nested_owner(deps.storage, &deps.querier, &info.sender)?;
9393

94+
if CALLING_TO_AS_ADMIN.exists(deps.storage) {
95+
return Err(AccountError::CantChainAdminCalls {});
96+
}
9497
CALLING_TO_AS_ADMIN.save(deps.storage, &addr)?;
9598

9699
let msg = SubMsg::reply_on_success(
@@ -225,9 +228,71 @@ mod test {
225228

226229
mod execute_action {
227230

228-
use cosmwasm_std::{testing::MOCK_CONTRACT_ADDR, wasm_execute, CosmosMsg};
231+
use abstract_sdk::namespaces::OWNERSHIP_STORAGE_KEY;
232+
use abstract_std::objects::{gov_type::GovernanceDetails, ownership::Ownership};
233+
use cosmwasm_std::{
234+
testing::MOCK_CONTRACT_ADDR, wasm_execute, Addr, Binary, CosmosMsg, DepsMut, Empty,
235+
Env, Response, WasmMsg,
236+
};
237+
use cw_storage_plus::Item;
238+
239+
use crate::contract::AccountResult;
229240

230241
use super::*;
242+
fn execute_from_res(deps: DepsMut, env: Env, res: Response) -> AccountResult<Response> {
243+
// Execute all messages
244+
let info = message_info(&env.contract.address, &[]);
245+
if let CosmosMsg::Wasm(WasmMsg::Execute {
246+
contract_addr: _,
247+
msg,
248+
funds: _,
249+
}) = res.messages[0].msg.clone()
250+
{
251+
execute(deps, env.clone(), info, from_json(&msg)?).map_err(Into::into)
252+
} else {
253+
panic!("Wrong message received");
254+
}
255+
}
256+
257+
#[coverage_helper::test]
258+
fn admin_actions_not_chained() -> anyhow::Result<()> {
259+
let mut deps = mock_dependencies();
260+
deps.querier = abstract_mock_querier(deps.api);
261+
mock_init(&mut deps)?;
262+
let env = mock_env_validated(deps.api);
263+
264+
// We set the contract as owner.
265+
// We can't make it through execute msgs, because signatures are too messy to reproduce in tests
266+
let ownership = Ownership {
267+
owner: GovernanceDetails::AbstractAccount {
268+
address: env.contract.address.clone(),
269+
}
270+
.verify(deps.as_ref())?,
271+
pending_owner: None,
272+
pending_expiry: None,
273+
};
274+
275+
const OWNERSHIP: Item<Ownership<Addr>> = Item::new(OWNERSHIP_STORAGE_KEY);
276+
OWNERSHIP.save(deps.as_mut().storage, &ownership)?;
277+
278+
let msg = ExecuteMsg::AdminExecute {
279+
addr: env.contract.address.to_string(),
280+
msg: to_json_binary(&ExecuteMsg::<Empty>::AdminExecute {
281+
addr: env.contract.address.to_string(),
282+
msg: Binary::new(vec![]),
283+
})?,
284+
};
285+
286+
let info = message_info(&env.contract.address, &[]);
287+
let env = mock_env_validated(deps.api);
288+
289+
let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap();
290+
// We simulate it's still an admin call
291+
AUTH_ADMIN.save(deps.as_mut().storage, &true)?;
292+
let res = execute_from_res(deps.as_mut(), env, res);
293+
assert_eq!(res, Err(AccountError::CantChainAdminCalls {}));
294+
Ok(())
295+
}
231296

232297
#[coverage_helper::test]
233298
fn only_whitelisted_can_execute() -> anyhow::Result<()> {

0 commit comments

Comments
 (0)