Critical Security Bug
File: lifebank-soroban/contracts/identity/src/lib.rs (line 354)
Problem
IdentityContract::grant_role() is a pub function with no require_auth() call and no caller validation:
pub fn grant_role(env: Env, address: Address, role: Role) {
let key = DataKey::AddressRoles(address.clone());
// ... no auth check anywhere in this function
env.storage().persistent().set(&key, &sorted);
}
Attack Scenario
Any Stellar address can call this function directly via the Stellar RPC:
stellar contract invoke --id <identity_contract> -- grant_role --address <attacker> --role Admin
After this call, the attacker holds the Admin role and can call verify_organization(), unverify_organization(), and award_badge() without restriction.
Impact
- Complete role system bypass: any address can grant itself
Admin, BloodBank, or Hospital roles
- Attacker can verify fraudulent organizations or unverify legitimate ones on-chain
- The permission scope system (
PermissionScope::SettlementRelease, DisputeResolve) is also reachable through the role system
Fix
grant_role() must be restricted to admin-only and should be an internal helper, not a public entrypoint:
fn grant_role_internal(env: Env, address: Address, role: Role) { ... }
pub fn grant_role(env: Env, admin: Address, address: Address, role: Role) {
admin.require_auth();
Self::require_role(&env, &admin, Role::Admin)?;
Self::grant_role_internal(env, address, role);
}
Critical Security Bug
File:
lifebank-soroban/contracts/identity/src/lib.rs(line 354)Problem
IdentityContract::grant_role()is apubfunction with norequire_auth()call and no caller validation:Attack Scenario
Any Stellar address can call this function directly via the Stellar RPC:
After this call, the attacker holds the
Adminrole and can callverify_organization(),unverify_organization(), andaward_badge()without restriction.Impact
Admin,BloodBank, orHospitalrolesPermissionScope::SettlementRelease,DisputeResolve) is also reachable through the role systemFix
grant_role()must be restricted to admin-only and should be an internal helper, not a public entrypoint: