Skip to content

Dev#113

Merged
JaCoderX merged 14 commits into
mainfrom
dev
Mar 10, 2026
Merged

Dev#113
JaCoderX merged 14 commits into
mainfrom
dev

Conversation

@JaCoderX
Copy link
Copy Markdown
Member

@JaCoderX JaCoderX commented Mar 10, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added enhanced error handling for meta-transaction validation failures, improving clarity when transaction records or payments mismatch stored data.
  • Bug Fixes

    • Improved role cleanup to ensure all associated data is fully purged when removing roles, preventing stale configuration remnants.
    • Strengthened cryptographic signature validation for better security.
  • Documentation

    • Updated validation documentation to reflect inclusive range handling in validation checks.

JaCoderX added 12 commits March 8, 2026 12:49
This commit improves the role management functionality in the EngineBlox library by ensuring a thorough cleanup of associated data when a role is removed. The changes include clearing the `authorizedWallets` set and removing function permissions and selectors to prevent stale data during role recreation. This enhancement ensures better integrity and efficiency in role-based access control, contributing to a more robust implementation. Additionally, comments have been updated for clarity on the cleanup process.
…esses

This commit introduces a new internal function, `_validateMetaTxMatchRecord`, to ensure that the meta-transaction's record matches the stored transaction record for a given transaction ID. This validation prevents unauthorized modifications during the approval and cancellation flows by checking critical fields such as execution selector, target, value, requester, gas limit, operation type, and release time. Additionally, a new error, `MetaTxRecordMismatchStoredTx`, is defined to handle mismatches effectively. These enhancements improve the integrity and security of the meta-transaction handling in the EngineBlox library.
…ineBlox library

This commit adds a custom security annotation to the `_txApprovalWithMetaTx` function, clarifying that the releaseTime (timelock) is intentionally not enforced on this path. The documentation explains the design choice to allow authorized signers to approve execution without waiting for the releaseTime, highlighting the hybrid synergy between timelock workflows and meta-transaction workflows. This enhancement improves understanding of the function's behavior and security implications for developers interacting with the EngineBlox library.
…gineBlox library

This commit introduces a new internal function, `_validateMetaTxPaymentMatchRecord`, to ensure that the payment details of a meta-transaction match the stored transaction record for a given transaction ID. This validation checks critical fields such as recipient, native token amount, ERC20 token address, and ERC20 token amount, preventing unauthorized modifications to payment details. Additionally, a new error, `MetaTxPaymentMismatchStoredTx`, is defined to handle mismatches effectively. These enhancements improve the integrity and security of the meta-transaction handling in the EngineBlox library.
…lox library

This commit enhances the message hashing process for meta-transactions by introducing a selective approach that focuses on the essential components: MetaTxRecord, TxParams, and PaymentDetails. The new structure improves the integrity of the generated message hash, ensuring that only relevant data is included for signing. Additionally, the documentation for the `generateMessageHash` and `recoverSigner` functions has been updated to reflect these changes, clarifying the signing process and the expected structure for clients. These improvements contribute to a more secure and efficient handling of meta-transactions within the EngineBlox library.
…role policies

This commit improves the documentation in the RuntimeRBAC and EngineBlox libraries by detailing the security policies surrounding protected roles. Key updates include clarifications on the unauthorized modification of protected roles, the necessity of role configuration batch ordering, and the enforcement of protected-role checks within the EngineBlox library. Additionally, custom security annotations have been added to emphasize the layered defense strategy and the exclusive authority of SecureOwnable in managing system wallets. These enhancements aim to provide clearer guidance for developers and reinforce the security framework of the role-based access control system.
…Blox library

This commit introduces a call to the new internal function `_validateMetaTxPaymentMatchRecord` within the meta-transaction execution flow. This enhancement ensures that the payment details of a meta-transaction are validated against the stored transaction record, reinforcing the integrity of the payment process. By implementing this check, the library improves its security posture against unauthorized payment modifications during execution. These changes contribute to a more robust handling of meta-transactions in the EngineBlox library.
…Blox library

This commit updates the documentation for the EIP-712 message hashing and signature recovery processes within the EngineBlox library. It emphasizes that integrators must sign the contract-provided digest directly using `personal_sign(metaTx.message)` instead of generic `eth_signTypedData_v4` serializers, as the contract constructs a custom domain separator and type hash. These changes ensure that the signing process is clear and that clients understand the implications of using standard serializers, which may produce incompatible hashes. Additionally, the validation function for inclusive ranges in the SharedValidation documentation has been updated for clarity.
…gineBlox library

This commit updates the documentation for the EIP-712 message hashing and signature recovery processes within the EngineBlox library. It clarifies that integrators must sign the digest as a raw hash without any EIP-191 or personal_sign prefix, ensuring compatibility with the contract's verification method. The changes emphasize the importance of using the correct signing approach to avoid incompatible hashes, and the resulting digest is now included in the `message` field of the `MetaTransaction` structs for easier access. These enhancements aim to improve the clarity and security of the signing process for integrators.
This commit introduces a new validation mechanism within the EngineBlox library to ensure that the execution selector is part of the handler selector's schema flow in strict mode. The addition of the `_schemaHasHandlerSelector` function allows for a more robust check against unauthorized handler executions, enhancing the security of the meta-transaction processing. This change aims to prevent mismatches between execution and handler selectors, thereby reinforcing the integrity of the function schema relationships. Overall, these enhancements contribute to a more secure and reliable execution environment for meta-transactions.
This commit updates the PaymentTestHelper contract to include an additional execution selector in the handler selectors for the approveTransaction function. The new structure now accommodates both the self-reference and the allowed execution selector, improving the flexibility and security of the transaction approval process. This enhancement ensures that the contract can effectively manage the execution flow of approved transactions, contributing to a more robust meta-transaction handling mechanism.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 10, 2026

Warning

Rate limit exceeded

@JaCoderX has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 58 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 067fb3e5-1ffc-43d2-aa37-4a7bd161ddde

📥 Commits

Reviewing files that changed from the base of the PR and between 3ef75f3 and 013eeec.

📒 Files selected for processing (1)
  • contracts/core/lib/EngineBlox.sol
📝 Walkthrough

Walkthrough

This PR transitions from EIP-191 (Ethereum-prefixed) message signing to raw EIP-712 digest signing throughout the stack. It introduces new meta-transaction validation errors, refactors monolithic EIP-712 type hashes into granular typed constants, adds meta-tx record and payment matching validators, and enhances handler-to-execution selector enforcement with optional strictness control.

Changes

Cohort / File(s) Summary
ABI and Error Definitions
abi/EngineBlox.abi.json, sdk/typescript/abi/EngineBlox.abi.json, contracts/core/lib/utils/SharedValidation.sol
Added two new custom errors: MetaTxPaymentMismatchStoredTx and MetaTxRecordMismatchStoredTx, both accepting a txId parameter. Updated ABI entries to reflect these additions.
EIP-712 Type Hash Refactoring
contracts/core/lib/EngineBlox.sol, test/foundry/helpers/TestHelpers.sol
Replaced single monolithic TYPE_HASH with granular EIP-712 type-hash constants (META_TX_TYPE_HASH, META_TX_RECORD_TYPE_HASH, TX_PARAMS_TYPE_HASH, META_TX_PARAMS_TYPE_HASH, PAYMENT_DETAILS_TYPE_HASH). Refactored struct-hashing logic to use these modular constants, improving clarity and flexibility in EIP-712 computation.
Raw Digest Signing and Recovery
scripts/sanity/utils/eip712-signing.cjs, sdk/typescript/lib/EngineBlox.tsx, sdk/typescript/utils/metaTx/metaTransaction.tsx
Eliminated EIP-191 prefix wrapping; signatures now work with raw EIP-712 digests. Added _signRawDigest helper and updated recovery logic to use recoverAddress directly on the message hash without Ethereum prefix conversion.
Meta-Transaction Validation and Handler Enforcement
contracts/core/lib/EngineBlox.sol
Added internal validation helpers _validateMetaTxMatchRecord and _validateMetaTxPaymentMatchRecord to ensure meta-tx payload fields match stored transaction data. Introduced _schemaHasHandlerSelector helper and optional enforceHandlerRelations flag for conditional strict handler-to-selector binding enforcement.
Test Suite Signing Updates
test/foundry/fuzz/Comprehensive*Fuzz.t.sol, test/foundry/fuzz/*Fuzz.t.sol, test/foundry/helpers/PaymentTestHelper.sol
Systematically removed MessageHashUtils imports and replaced all EIP-191 signed hash signing with direct raw message-hash signing via vm.sign. Updated handler selector lists in test fixtures to include additional allowed execution targets.
Documentation and Runtime Setup
contracts/core/access/RuntimeRBAC.sol, docs/core/lib/utils/SharedValidation.md, test/foundry/invariant/StateMachineInvariants.t.sol
Added in-code security documentation in RuntimeRBAC. Updated SharedValidation docs to reflect inclusive-range validation (from ≤ to). Enhanced invariant test setup to target specific accounts and exclude permissioned selectors from fuzzing via StdInvariant.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #36: Directly overlaps in meta-transaction signing and test helper code (TestHelpers/MetaTxSigner and Foundry test updates).
  • PR #104: Modifies EngineBlox's handler-enforcement surface with the enforceHandlerRelations flag and related validation logic.
  • PR #99: Updates the same invariant tests with identical SecureOwnable selector exclusions (TRANSFER_OWNERSHIP, UPDATE_BROADCASTER, UPDATE_RECOVERY, UPDATE_TIMELOCK).

Poem

🐰 Raw digests now sing, no Ethereum prefix tonight,
Granular type hashes dance in EIP-712 light,
Meta-tx validators catch mismatches in flight,
Handlers enforce relations, handlers do right,
Our signing's reborn—secure and tight! 🔐

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The pull request title 'Dev' is vague and generic, providing no meaningful information about the substantial changes made across multiple files and systems. Replace the title with a descriptive summary of the main changes, such as 'Refactor meta-transaction signing to use raw EIP-712 digest' or 'Add meta-transaction validation and EIP-712 type-hash restructuring'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/foundry/fuzz/ComprehensiveStateMachineFuzz.t.sol (1)

997-1000: ⚠️ Potential issue | 🟡 Minor

Assertion is always true due to || true.

The condition result.result.length > 0 || true always evaluates to true, making this assertion meaningless. If you intend to verify that the failure was recorded with a reason, remove || true. If this check is intentionally optional, consider removing the assertion or converting it to a comment.

🔧 Proposed fix
             if (result.status == EngineBlox.TxStatus.FAILED) {
                 // EIP-150: low gas caused failure; catch path must not have set COMPLETED
-                assertTrue(result.result.length > 0 || true, "Failure recorded");
+                // Note: result.result may be empty for OOG failures
             }

Or, if the check is meaningful:

             if (result.status == EngineBlox.TxStatus.FAILED) {
                 // EIP-150: low gas caused failure; catch path must not have set COMPLETED
-                assertTrue(result.result.length > 0 || true, "Failure recorded");
+                assertTrue(result.result.length > 0, "Failure recorded");
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/foundry/fuzz/ComprehensiveStateMachineFuzz.t.sol` around lines 997 -
1000, The assertion in the failure branch uses a tautology
("result.result.length > 0 || true") so it never checks anything; update the
check inside the block that handles EngineBlox.TxStatus.FAILED (where `result`
is inspected) by removing the `|| true` to assert the intended condition
(`result.result.length > 0`) or, if you meant the check to be optional, delete
the assertion entirely or replace it with a descriptive comment explaining why
no assertion is required.
sdk/typescript/utils/metaTx/metaTransaction.tsx (1)

188-209: ⚠️ Potential issue | 🟠 Major

Check the recovered signer against unsignedMetaTx.params.signer.

This currently verifies the signature against the extra signerAddress argument only, so the SDK can return a “signed” meta-tx that the contract will reject whenever signerAddress/privateKey and unsignedMetaTx.params.signer diverge.

💡 Suggested fix
   async signMetaTransaction(
     unsignedMetaTx: MetaTransaction,
     signerAddress: Address,
     privateKey: Hex
   ): Promise<MetaTransaction> {
@@
     const { recoverAddress } = await import('viem');
     const recoveredAddress = await recoverAddress({
       hash: contractDigest,
       signature
     });
 
-    if (recoveredAddress.toLowerCase() !== signerAddress.toLowerCase()) {
+    const expectedSigner = unsignedMetaTx.params.signer;
+    if (
+      signerAddress.toLowerCase() !== expectedSigner.toLowerCase() ||
+      recoveredAddress.toLowerCase() !== expectedSigner.toLowerCase()
+    ) {
       throw new Error('Signature verification failed');
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/typescript/utils/metaTx/metaTransaction.tsx` around lines 188 - 209, In
signMetaTransaction, the signature is being verified against the passed-in
signerAddress instead of the meta-tx's declared signer; update the verification
to compare recoveredAddress (from recoverAddress) to
unsignedMetaTx.params.signer (or ensure both match), and throw the Error if they
differ; locate the check in signMetaTransaction and replace or extend the
comparison that currently references signerAddress with
unsignedMetaTx.params.signer to ensure the signed payload matches the
contract-expected signer.
🧹 Nitpick comments (2)
test/foundry/fuzz/ProtectedResourceFuzz.t.sol (1)

79-108: Consider expanding coverage to all protected roles like the ADD_WALLET test.

The function is prefixed with testFuzz_ but has no fuzz parameters. Also, unlike testFuzz_CannotAddWalletToProtectedRole which tests all three protected roles, this only tests revoking owner from OWNER_ROLE.

♻️ Suggested improvement for consistency
-    function testFuzz_CannotRevokeWalletFromProtectedRole() public {
-        // Test revoking owner from OWNER_ROLE
+    function test_CannotRevokeWalletFromProtectedRole() public {
+        // Test revoking wallets from protected roles
+        bytes32[3] memory protectedRoles = [OWNER_ROLE, BROADCASTER_ROLE, RECOVERY_ROLE];
+        address[3] memory protectedWallets = [owner, broadcaster, recovery];
+
+        for (uint256 i = 0; i < protectedRoles.length; i++) {
             IRuntimeRBAC.RoleConfigAction[] memory actions = new IRuntimeRBAC.RoleConfigAction[](1);
             actions[0] = IRuntimeRBAC.RoleConfigAction({
                 actionType: IRuntimeRBAC.RoleConfigActionType.REVOKE_WALLET,
-                data: abi.encode(OWNER_ROLE, owner)
+                data: abi.encode(protectedRoles[i], protectedWallets[i])
             });
+            // ... rest of test logic
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/foundry/fuzz/ProtectedResourceFuzz.t.sol` around lines 79 - 108, The
test testFuzz_CannotRevokeWalletFromProtectedRole only checks OWNER_ROLE and is
not actually fuzzing; change it to cover all protected roles like
testFuzz_CannotAddWalletToProtectedRole by iterating or fuzzing over the
protected role set (OWNER_ROLE, ADMIN_ROLE, OPERATOR_ROLE) and using that role
variable when building actions (IRuntimeRBAC.RoleConfigAction data),
executionParams, metaTx (_createMetaTxForRoleConfig), and the expectedError
(SharedValidation.CannotModifyProtected.selector, role); ensure the test
name/fuzzer signature matches (either add a role parameter to make it a true
fuzz test or loop through the three roles inside the function) and update
assertions to use the variable role instead of the hardcoded OWNER_ROLE.
contracts/core/lib/utils/SharedValidation.sol (1)

410-415: Function name validateLessThan is now misleading after semantic change.

The condition changed from from >= to to from > to, which means the function now validates from <= to (inclusive range). The NatSpec is correctly updated, but the function name suggests strict less-than semantics.

Consider renaming to validateLessThanOrEqual or validateInclusiveRange to match the new behavior and prevent confusion for future maintainers.

♻️ Suggested rename for clarity
-    function validateLessThan(uint256 from, uint256 to) internal pure {
+    function validateLessThanOrEqual(uint256 from, uint256 to) internal pure {
         if (from > to) revert InvalidRange(from, to);
     }

Note: This would require updating the call site in BaseStateMachine.getTransactionHistory.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/lib/utils/SharedValidation.sol` around lines 410 - 415, The
function validateLessThan now enforces an inclusive check (from <= to) but its
name implies strict less-than; rename the function to validateLessThanOrEqual
(or validateInclusiveRange) and update all call sites (notably
BaseStateMachine.getTransactionHistory) to use the new name, preserving the
logic (if (from > to) revert InvalidRange(from, to)) and keeping the NatSpec
aligned with the new name to avoid confusion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/core/lib/EngineBlox.sol`:
- Around line 2262-2272: The strict-mode branch only checks the schema-level
relation but misses enforcing the role-level handler→execution pairing; update
the branch where handlerSchema.enforceHandlerRelations is checked so that after
validating _schemaHasHandlerSelector, also verify the active role's
FunctionPermission.handlerForSelectors for handlerSelector includes
executionSelector (i.e., ensure the role-specific bitmap allows that execution),
using the same role identifier/context that hasActionPermission() uses; if the
role-level mapping does not permit it, revert with
SharedValidation.HandlerForSelectorMismatch (same as current) — reference
FunctionSchema, handlerSchema.enforceHandlerRelations, hasActionPermission(),
FunctionPermission.handlerForSelectors, and _schemaHasHandlerSelector to locate
the code to change.
- Around line 153-157: The new bool enforceHandlerRelations was inserted before
isProtected in the FunctionSchema struct, shifting storage and causing old
packed isProtected bits to be misread; to fix, revert that insertion order and
preserve layout by either appending enforceHandlerRelations to the end of the
struct or by adding a reserved storage gap (e.g., uint256[] __gap sized to cover
the added slot) so existing fields (isProtected and handlerForSelectors) keep
their original positions; update the FunctionSchema definition (and any related
structs) to use the gap pattern or move enforceHandlerRelations to the struct
tail to avoid breaking persisted state.

In `@scripts/sanity/utils/eip712-signing.cjs`:
- Around line 186-189: The change to signMetaTransaction() returned shape broke
callers expecting signature.signature; revert or provide the previous nested
shape by returning the metaTx plus a signature object containing the hex and
message (e.g. keep signMetaTransaction(...) returning { ...metaTx, signature: {
signature: signatureHex, message: messageHash } }) so existing callers that read
signature.signature continue to work, or alternatively update those callers to
read signature (but prefer restoring the nested shape in signMetaTransaction to
maintain backward compatibility).

---

Outside diff comments:
In `@sdk/typescript/utils/metaTx/metaTransaction.tsx`:
- Around line 188-209: In signMetaTransaction, the signature is being verified
against the passed-in signerAddress instead of the meta-tx's declared signer;
update the verification to compare recoveredAddress (from recoverAddress) to
unsignedMetaTx.params.signer (or ensure both match), and throw the Error if they
differ; locate the check in signMetaTransaction and replace or extend the
comparison that currently references signerAddress with
unsignedMetaTx.params.signer to ensure the signed payload matches the
contract-expected signer.

In `@test/foundry/fuzz/ComprehensiveStateMachineFuzz.t.sol`:
- Around line 997-1000: The assertion in the failure branch uses a tautology
("result.result.length > 0 || true") so it never checks anything; update the
check inside the block that handles EngineBlox.TxStatus.FAILED (where `result`
is inspected) by removing the `|| true` to assert the intended condition
(`result.result.length > 0`) or, if you meant the check to be optional, delete
the assertion entirely or replace it with a descriptive comment explaining why
no assertion is required.

---

Nitpick comments:
In `@contracts/core/lib/utils/SharedValidation.sol`:
- Around line 410-415: The function validateLessThan now enforces an inclusive
check (from <= to) but its name implies strict less-than; rename the function to
validateLessThanOrEqual (or validateInclusiveRange) and update all call sites
(notably BaseStateMachine.getTransactionHistory) to use the new name, preserving
the logic (if (from > to) revert InvalidRange(from, to)) and keeping the NatSpec
aligned with the new name to avoid confusion.

In `@test/foundry/fuzz/ProtectedResourceFuzz.t.sol`:
- Around line 79-108: The test testFuzz_CannotRevokeWalletFromProtectedRole only
checks OWNER_ROLE and is not actually fuzzing; change it to cover all protected
roles like testFuzz_CannotAddWalletToProtectedRole by iterating or fuzzing over
the protected role set (OWNER_ROLE, ADMIN_ROLE, OPERATOR_ROLE) and using that
role variable when building actions (IRuntimeRBAC.RoleConfigAction data),
executionParams, metaTx (_createMetaTxForRoleConfig), and the expectedError
(SharedValidation.CannotModifyProtected.selector, role); ensure the test
name/fuzzer signature matches (either add a role parameter to make it a true
fuzz test or loop through the three roles inside the function) and update
assertions to use the variable role instead of the hardcoded OWNER_ROLE.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 217f2c4b-ff71-4a1a-b939-65e16d6014b8

📥 Commits

Reviewing files that changed from the base of the PR and between 812a1fd and 3ef75f3.

📒 Files selected for processing (24)
  • abi/EngineBlox.abi.json
  • contracts/core/access/RuntimeRBAC.sol
  • contracts/core/lib/EngineBlox.sol
  • contracts/core/lib/utils/SharedValidation.sol
  • docs/core/lib/utils/SharedValidation.md
  • scripts/sanity/utils/eip712-signing.cjs
  • sdk/typescript/abi/EngineBlox.abi.json
  • sdk/typescript/lib/EngineBlox.tsx
  • sdk/typescript/utils/metaTx/metaTransaction.tsx
  • test/foundry/fuzz/ComprehensiveAccessControlFuzz.t.sol
  • test/foundry/fuzz/ComprehensiveCompositeFuzz.t.sol
  • test/foundry/fuzz/ComprehensiveGasExhaustionFuzz.t.sol
  • test/foundry/fuzz/ComprehensiveInputValidationFuzz.t.sol
  • test/foundry/fuzz/ComprehensiveMetaTransactionFuzz.t.sol
  • test/foundry/fuzz/ComprehensiveSecurityEdgeCasesFuzz.t.sol
  • test/foundry/fuzz/ComprehensiveStateMachineFuzz.t.sol
  • test/foundry/fuzz/EdgeCasesFuzz.t.sol
  • test/foundry/fuzz/MetaTransactionSecurityFuzz.t.sol
  • test/foundry/fuzz/ProtectedResourceFuzz.t.sol
  • test/foundry/fuzz/RBACPermissionFuzz.t.sol
  • test/foundry/fuzz/RuntimeRBACFuzz.t.sol
  • test/foundry/helpers/PaymentTestHelper.sol
  • test/foundry/helpers/TestHelpers.sol
  • test/foundry/invariant/StateMachineInvariants.t.sol

Comment on lines +153 to +157
/// @dev When true (strict mode): handlerForSelectors in role permissions must match this schema's handlerForSelectors at use time.
/// When false (flexible mode): no such check; forward references and unregistered selectors in handlerForSelectors are allowed at registration.
bool enforceHandlerRelations;
bool isProtected;
bytes4[] handlerForSelectors;
bytes4[] handlerForSelectors;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Appending enforceHandlerRelations here breaks persisted FunctionSchema state.

For any in-place upgrade, the old packed isProtected bit will now be read as enforceHandlerRelations, and isProtected will read back as false. Previously protected schemas would become mutable after upgrade.

💡 Safer layout
         uint16 supportedActionsBitmap; // Bitmap for TxAction enum (9 bits max)
-        bool enforceHandlerRelations;
         bool isProtected;
         bytes4[] handlerForSelectors;
+        bool enforceHandlerRelations;
As per coding guidelines, "Preserve storage layout using __gap arrays in upgradeable contracts".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/lib/EngineBlox.sol` around lines 153 - 157, The new bool
enforceHandlerRelations was inserted before isProtected in the FunctionSchema
struct, shifting storage and causing old packed isProtected bits to be misread;
to fix, revert that insertion order and preserve layout by either appending
enforceHandlerRelations to the end of the struct or by adding a reserved storage
gap (e.g., uint256[] __gap sized to cover the added slot) so existing fields
(isProtected and handlerForSelectors) keep their original positions; update the
FunctionSchema definition (and any related structs) to use the gap pattern or
move enforceHandlerRelations to the struct tail to avoid breaking persisted
state.

Comment thread contracts/core/lib/EngineBlox.sol Outdated
Comment on lines +2262 to +2272
// In strict mode, enforce that the executionSelector is part of the handlerSelector's schema flow.
// Handler schemas declare which execution selectors they are allowed to trigger.
FunctionSchema storage handlerSchema = self.functions[handlerSelector];
if (handlerSchema.enforceHandlerRelations) {
if (!_schemaHasHandlerSelector(handlerSchema, executionSelector)) {
revert SharedValidation.HandlerForSelectorMismatch(
executionSelector,
handlerSelector
);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Strict mode still skips the role-level handler/execution pairing.

hasActionPermission() only proves the wallet has the two bitmaps somewhere, and this new branch only checks the schema-level relation. A role that intentionally narrows handlerSelector to execution A via FunctionPermission.handlerForSelectors can still be used with execution B whenever the wallet separately has B permission and the schema allows it.

As per coding guidelines, "Implement role-based access control (RBAC) with per-function permissions and protected roles".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/core/lib/EngineBlox.sol` around lines 2262 - 2272, The strict-mode
branch only checks the schema-level relation but misses enforcing the role-level
handler→execution pairing; update the branch where
handlerSchema.enforceHandlerRelations is checked so that after validating
_schemaHasHandlerSelector, also verify the active role's
FunctionPermission.handlerForSelectors for handlerSelector includes
executionSelector (i.e., ensure the role-specific bitmap allows that execution),
using the same role identifier/context that hasActionPermission() uses; if the
role-level mapping does not permit it, revert with
SharedValidation.HandlerForSelectorMismatch (same as current) — reference
FunctionSchema, handlerSchema.enforceHandlerRelations, hasActionPermission(),
FunctionPermission.handlerForSelectors, and _schemaHasHandlerSelector to locate
the code to change.

Comment on lines 186 to 189
return {
...metaTx,
signature: signature.signature,
signature: signatureHex,
message: messageHash
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This return-shape change breaks existing sanity callers.

signMetaTransaction() now returns a full meta-tx with signature as a top-level hex string, but the provided callers in scripts/sanity/secure-ownable/eip712-signing-tests.cjs and scripts/sanity/secure-ownable/broadcaster-update-tests.cjs still read signature.signature. Those flows will fail before they can execute the meta-transaction.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sanity/utils/eip712-signing.cjs` around lines 186 - 189, The change
to signMetaTransaction() returned shape broke callers expecting
signature.signature; revert or provide the previous nested shape by returning
the metaTx plus a signature object containing the hex and message (e.g. keep
signMetaTransaction(...) returning { ...metaTx, signature: { signature:
signatureHex, message: messageHash } }) so existing callers that read
signature.signature continue to work, or alternatively update those callers to
read signature (but prefer restoring the nested shape in signMetaTransaction to
maintain backward compatibility).

This commit updates the `_validateExecutionAndHandlerPermissions` function within the EngineBlox library to improve the clarity and robustness of permission checks for execution and handler selectors. The documentation has been refined to emphasize the strict mode's enforcement of schema-level relationships between selectors, ensuring that the execution selector is appropriately validated against the handler's schema. These enhancements contribute to a more secure and efficient permission validation process, reinforcing the integrity of the meta-transaction handling framework.
feat: enhance permission validation in EngineBlox library
@JaCoderX JaCoderX merged commit 148ebdc into main Mar 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant