From ab7c8a211401b3916978362c95d68965494a5aef Mon Sep 17 00:00:00 2001 From: Jo-anny Date: Sat, 29 Aug 2026 12:13:44 +0100 Subject: [PATCH] Require explicit admin capability for governance changes --- contracts/predictify-hybrid/src/governance.rs | 15 ++++++++++---- .../predictify-hybrid/src/governance_tests.rs | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/contracts/predictify-hybrid/src/governance.rs b/contracts/predictify-hybrid/src/governance.rs index 995f3584..b68d1aa3 100644 --- a/contracts/predictify-hybrid/src/governance.rs +++ b/contracts/predictify-hybrid/src/governance.rs @@ -1,3 +1,4 @@ +use crate::admin::{AdminAccessControl, AdminPermission}; use crate::events::EventEmitter; use soroban_sdk::{contracttype, panic_with_error, Address, Bytes, BytesN, Env, String, Symbol, Vec}; @@ -816,14 +817,20 @@ impl GovernanceContract { /// Simple helper to check admin fn ensure_admin(env: &Env, caller: Address) -> Result<(), GovernanceError> { caller.require_auth(); - let admin: Address = env + + let primary_admin: Address = env .storage() .persistent() .get(&StorageKey::Admin) .ok_or(GovernanceError::NotInitialized)?; - if admin != caller { - return Err(GovernanceError::NotAdmin); + + if primary_admin == caller { + return Ok(()); + } + + match AdminAccessControl::validate_permission(env, &caller, &AdminPermission::UpdateConfig) { + Ok(()) => Ok(()), + Err(_) => Err(GovernanceError::NotAdmin), } - Ok(()) } } diff --git a/contracts/predictify-hybrid/src/governance_tests.rs b/contracts/predictify-hybrid/src/governance_tests.rs index a2036650..6128415a 100644 --- a/contracts/predictify-hybrid/src/governance_tests.rs +++ b/contracts/predictify-hybrid/src/governance_tests.rs @@ -398,6 +398,26 @@ fn governance_set_voting_period_is_applied_to_new_proposals() { assert_eq!(proposal.end_time - proposal.start_time, 250); } +#[test] +fn governance_requires_explicit_config_capability_for_admin_changes() { + let fixture = GovernanceFixture::new(100, 1); + let delegated_admin = Address::generate(&fixture.env); + + fixture.env.as_contract(&fixture.contract_id, || { + crate::admin::AdminSystemIntegration::ensure_migration(&fixture.env).unwrap(); + crate::admin::AdminManager::add_admin( + &fixture.env, + &fixture.admin, + &delegated_admin, + crate::admin::AdminRole::ConfigAdmin, + ) + .unwrap(); + }); + + assert!(fixture.set_quorum(delegated_admin.clone(), 7).is_ok()); + assert_eq!(fixture.set_quorum(Address::generate(&fixture.env), 9), Err(GovernanceError::NotAdmin)); +} + /// ---- Quorum Decay Tests ---- #[test]