DeFi Security AllianceRequest an audit
Menu

EVM patterns

Safe Module Security: Guards, Execution Paths and Treasury Approval

An enabled module can authorize execution through a path separate from the owner quorum. Safe module security therefore requires checking the module itself, the deployed account version and the guard configuration on each relevant route.

White miniature vault with a front corridor of blue gates and a separate side corridor controlled by a black gate.

Key facts

Original source census
6 public execution entrypoints across Safe v1.4.1 and v1.5.0
Owner signature check
1 of 3 enumerated entrypoints in each release reaches the owner signature check
Module guards
2 module paths in v1.5.0 contain hooks; configuration remains a separate question
Review boundary
Static source inspection, not deployed-account testing or a vulnerability count

Decide whether the module belongs in the treasury

Approve a module only when its execution authority is part of the treasury's intended policy. A signer threshold describes the owner transaction path. An enabled module supplies another authorization path, with its own rules. Raising the owner threshold does not automatically strengthen those rules. The decision is therefore about the complete set of ways funds or configuration can change.

Safe module security is the review of that additional execution authority: who can invoke it, what it can execute and which controls constrain its calls. Safe's module documentation treats modules as extensions of account behavior. A treasury should treat each extension as part of its trusted system, including the module's administrators and dependencies.

Consider a payroll requirement. Owners want a limited operational process to make recurring payments without collecting the full owner quorum each time. That is a reasonable product requirement. It also means the payroll route must enforce its own recipient and spending restrictions. If the module instead accepts unrestricted targets or changeable policy from an unreviewed administrator, the deployed authority is broader than the business requirement.

Module decisions tied to treasury requirements
RequirementUseful module scopeAcceptance evidence
Recurring paymentsDefined recipients and bounded spendingUnauthorized recipient and limit tests fail for the intended reason
Emergency responseExplicitly named protective actionsResponse authority cannot expand into general asset control
Governance executionValidated proposal executionProposal authorization and execution parameters remain bound together
Account recoveryDocumented recovery transitionRecovery works under the failure it is designed to address

The person approving an integration should be able to name the module's maximum authority without referring to its interface branding. A dashboard may expose a narrow workflow while its contract permits broader execution. Review the implementation and the deployed configuration, including any route that can upgrade the module or change its policy.

The admin key management guide covers signer custody and rotation. The DAO security review covers governance authority. A module assessment connects those questions to the account's execution paths. It should not end after confirming that the owner list looks correct.

Original source census across two Safe releases

We compared the complete public execution family named execTransaction* in Safe's core and module manager source files for releases v1.4.1 and v1.5.0. The source boundary matters: the newer release supports module guard hooks, while the older module paths do not call the owner transaction guard.

Method

Retrieval
Four release-tagged source files fetched on , with raw bytes and hashes retained.
Population
Every public or external function beginning with execTransaction in Safe.sol and ModuleManager.sol for each release.
Selection
6 entrypoints across 2 releases; no matching entrypoint excluded.
Classification
Lexical function and call traversal, followed by manual source inspection, identifies owner signature checks, transaction guard hooks and module guard hooks.
Meaning of a hook
Support for invoking a configured guard. A source hook does not prove a guard is installed or that its policy is correct.

The script is seo/research/authorization-boundaries-2026-10-03/measurements.py. The per-entrypoint trace is seo/research/authorization-boundaries-2026-10-03/safe-module-security/measurement.json. Both are retained in the repository, not published as download links. The replay checks the source hashes and compares every classification row.

Results

Execution-path checks found in the two fixed Safe releases
Release and routeOwner signature checkTransaction guard hookModule guard hook
1.4.1 owner transactionPresentPresentAbsent
1.4.1 module transactionAbsentAbsentAbsent
1.4.1 module transaction with return dataAbsentAbsentAbsent
1.5.0 owner transactionPresentPresentAbsent
1.5.0 module transactionAbsentAbsentPresent
1.5.0 module transaction with return dataAbsentAbsentPresent

In each release, only 1 of the 3 enumerated entrypoints reaches the owner signature check. In v1.5.0, both module entrypoints call the pre-execution and post-execution module hooks. In v1.4.1, the return-data variant delegates to the ordinary module execution function, which checks module membership but does not invoke a transaction guard.

This is consistent with the Safe v1.5.0 release explanation. The release introduces module guards as a way to enforce rules on module transactions alongside the existing owner transaction guard. The census supplies a reproducible map of the named entrypoints behind that explanation.

Limits

  • This is static inspection of fixed upstream source releases, not a test of a deployed Safe.
  • The parser is a bounded lexical inspection tool, not a Solidity compiler or general call-graph analyzer.
  • Fallback handlers, custom subclasses and module implementations are outside the enumerated family.
  • Absent owner signatures on module paths are intentional architecture, not a count of vulnerabilities.

The practical result is a scope correction. If a treasury requires one policy to govern both owner and module transactions, evidence from an owner-path guard alone is incomplete. The deployed account version and both configured guard paths must be checked.

Map both execution paths before installing guards

A guard constrains execution at a particular hook. Its location determines what it can inspect. The Safe guard documentation and the separate module guard reference describe different configuration surfaces. A policy statement such as "every transfer is checked" needs a path-by-path demonstration.

Safe Module Security: Guards, Execution Paths and Treasury ApprovalThe owner route uses its transaction guard; module guard coverage depends on release and configuration. Both paths can reach execution.Owner signaturesTransaction guardExecutionEnabled moduleModule guardTrace each authority before accepting configuration
The owner route uses its transaction guard; module guard coverage depends on release and configuration. Both paths can reach execution.
Owner route
Owner signature validation reaches the configured transaction guard before the requested execution.
Module route
An enabled module supplies its own authorization logic. Releases with module guard support can invoke the separately configured module guard.
Common effects
Either permitted route can reach a target call within its actual authority. Review configuration changes as well as asset movement.

Begin with a deployed-account inventory. Confirm the implementation version and enumerate all enabled modules. Safe exposes getModulesPaginated; stopping after one page is not a complete inventory when a continuation is present. Retain the block used for the inventory so that a module enabled later cannot be mistaken for part of the reviewed state.

Then inspect the guard configuration that applies to each route. A nonzero address establishes that a guard is configured, but not what it allows. An interface check establishes compatibility with a required interface, not correctness of the policy implementation. Read the policy and its administrators, then exercise the operations the treasury expects it to reject.

A configuration-changing call deserves the same attention as a transfer. A route able to remove a guard, enable another module or rewrite the account's authority can change future behavior without moving assets immediately. The simulation guide explains why an unchanged balance in a preview can miss such a permission change.

Review CALL and DELEGATECALL as distinct operations. The latter executes code in the caller's storage context and can therefore affect account configuration in ways a simple recipient allowlist does not describe. A policy permitting an operation should explain the storage and authority consequences it accepts. A familiar target address is not a substitute for that explanation.

Finally, verify the installation sequence. If the intended control needs several configuration transactions, the intermediate states have their own permissions. An atomic batch can reduce an installation window only when the batch itself is correct and supported. Reproduce the actual sequence, including its failure behavior, before relying on the final diagram.

Make the inventory reproducible

A module inventory needs a termination condition. In the inspected source, pagination uses a sentinel to indicate the end of the module list. When another page remains, the continuation is based on the last returned module. An inventory script should follow that documented contract until the end and preserve the pages it read. A screenshot containing several module names is not evidence that the list was exhausted.

Use a fixed block for the entire inventory. If modules change between requests, pagination can describe a mixture of states. The resulting list might omit an entry or make a changed order appear inconsistent. A reviewer should be able to repeat the reads at the same block and obtain the same enabled-module set.

The inventory should include the code behind each module address. If an address is a proxy, resolve the implementation and the authority that can change it. The module's access policy can also depend on a separate role manager. Record that dependency even when it is not displayed in the wallet's module screen.

Test policy changes as part of the review. Suppose a module is intended to allow a limited operational account to make payments. Establish whether that same account can change the recipient list, increase a limit or replace a policy contract. A payment-only requirement is not satisfied if the operator can first rewrite the restriction and then make an unrestricted payment.

Keep legitimate administration possible under a documented route. Blocking every configuration call can prevent routine maintenance and complicate recovery. The acceptance matrix should distinguish authorized policy changes from changes attempted through an operational role. That distinction lets the owner understand the actual trade-off before deployment, rather than discovering it during an incident.

When an inventory cannot resolve an implementation or administrator, mark that dependency unknown and keep it in the scope decision. Do not omit the module merely because its metadata is unavailable. The enabled address still represents a real execution path.

Specify the module review scope

The review brief should name the treasury requirement and the code that enforces it. "Audit our multisig" is too vague when the actual scope includes a proxy, several modules and separately administered guards. Specify the deployed addresses and source revisions, then connect each requirement to the path that is supposed to enforce it.

For an external assessment, Pharos Production's smart contract access-control and logic review describes manual and static analysis of authorization and business-logic risks, with findings and remediation guidance. That scope fits the question here: whether module calls and guard configuration enforce the treasury's intended authority. The engagement should explicitly include both execution paths, the module's administrators and recovery behavior.

The specific service page is more relevant to this paragraph than a general application penetration test. The object under review is contract execution authority and the evidence needed to accept a fix. The Pharos Production directory profile provides site context; the member directory supports broader provider research. Neither listing establishes that a particular deployment has been reviewed.

Deliverables to request for a module integration
Review objectRequired artifactAccountable owner
Authorization rulesAllowed and rejected call matrixSecurity lead
Deployment configurationAddresses, versions and block-specific settingsDeployment operator
RemediationFinding-to-fix mapping and reviewed revisionEngineering lead
RecoveryDemonstrated procedure under the stated failureTreasury operations owner

Ask the reviewer to state what is excluded. A review of module source may not include deployment scripts or the organization's signer process. A guard review may assume an immutable target while the deployment points to an upgradeable proxy. These assumptions should appear beside the findings, because they determine whether the report supports the intended release.

Acceptance should include negative evidence. Show that an unauthorized caller cannot trigger the module, that a forbidden operation is rejected and that a policy administrator cannot silently expand permissions beyond the approved design. Pair each rejection with a valid control case. A test that fails because the fixture has no funds does not demonstrate an authorization boundary.

Practice recovery without creating a bypass

A guard can cause denial of service if it rejects every relevant transaction. Safe's module guard documentation explicitly warns about this possibility and calls for recovery design. The correct response is a tested recovery route whose authority is understood. Adding an unrestricted emergency module can merely move the original risk to a different entrypoint.

Define the failure before selecting the recovery mechanism. A broken module guard, an unavailable signer and a compromised module administrator are different conditions. A procedure that requires the unavailable signer does not solve that scenario. A procedure controlled by the compromised module administrator cannot be counted as an independent safety control against that administrator.

  1. State the exact failure being rehearsed, including which contracts reject calls and which actors remain available.
  2. Reproduce the deployed configuration in an isolated test environment at a recorded revision.
  3. Attempt the documented recovery action through its intended entrypoint and inspect every guard it encounters.
  4. Verify the resulting module list, guard settings and owner authority, not only the transaction's success status.
  5. Attempt an unauthorized use of the recovery path and confirm that its policy rejects the action for the expected reason.
  6. Restore the ordinary operating configuration and verify that temporary recovery authority has ended.

Recovery tests should include dependencies. A module may consult another contract, an oracle or a role registry. If that dependency becomes unavailable, a permission check can fail before the intended recovery action is reached. Record whether the design blocks operations, uses an alternate route or requires a separate governance decision. Each choice has a cost that belongs in the approval record.

Owner rotation also needs a postcondition outside the owner list. If an old operator can still invoke a module, replacing that operator as a Safe owner has not necessarily removed their effective authority. Repeat the module authorization tests after rotation. The old identity should fail wherever the rotation requirement says it must lose access.

Do not label a recovery route independent merely because it uses a different contract. Shared administrators or shared signing devices can produce the same control failure across both paths. The evidence should identify the actor that can authorize recovery and the actor whose compromise the recovery is meant to survive.

Approve the deployment from evidence

The final approval combines reviewed code with observed configuration. A report tied to one revision cannot by itself approve a different module build. A correct source implementation cannot establish that the deployed account has the intended guard. The release owner needs both pieces, joined by addresses and a recorded block.

Code acceptance
The reviewed revisions include the module, guard logic and dependencies relevant to the requirement.
Configuration acceptance
The deployment inventory shows the intended modules, guard paths and administrative authorities.
Behavior acceptance
Allowed actions succeed and forbidden actions fail through every relevant entrypoint.
Operational acceptance
Recovery and rotation have demonstrated postconditions, with a named owner for changes.

Keep a change trigger in the operating procedure. Enabling a new module, changing a guard or upgrading a policy dependency can invalidate earlier evidence even when the Safe's owner threshold stays unchanged. Reopen the affected part of the assessment when one of those changes occurs. An unchanged threshold is not an unchanged trust model.

The source census establishes a bounded fact: the inspected releases expose different guard coverage across their module execution family. The treasury decision remains specific to the deployed version and configuration. The approving owner should sign off on that configuration only after the release packet demonstrates the authority the treasury intended to grant.

Frequently asked questions

Is opening a Safe App the same as enabling a module?

No. An application interface and an enabled on-chain module are different objects. Inspect the transactions the app proposes and the resulting module configuration before assigning execution authority.

Does a module audit cover the organization's signer custody?

Only if that work is explicitly included in the engagement. A contract report does not establish how the organization stores keys or whether its recovery actors are independent.

Should an unused account on another chain inherit this approval automatically?

No. Verify its own deployment and configuration. Similar addresses or owner lists do not establish an identical module set or guard policy.

Comments

0

    Leave a comment

    Share a question or observation about this article.

    10 to 3,000 characters.