chore: refactor permission rules and add additional validation#7844
chore: refactor permission rules and add additional validation#7844jeffsmale90 wants to merge 13 commits intomainfrom
Conversation
4cd5553 to
df72ecd
Compare
a9450f5 to
8700f23
Compare
packages/gator-permissions-controller/src/decodePermission/rules/erc20TokenPeriodic.test.ts
Show resolved
Hide resolved
52df231 to
e1cb91e
Compare
packages/gator-permissions-controller/src/decodePermission/rules/erc20TokenStream.ts
Show resolved
Hide resolved
| }); | ||
|
|
||
| it('rejects when startTime is 0', () => { | ||
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddddd' as Hex; |
There was a problem hiding this comment.
I think the valid 20-byte addresses should be 40 hex chars.
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddddd' as Hex; | |
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddd' as Hex; |
There was a problem hiding this comment.
I think it is 40 hex chars:
> "0xdddddddddddddddddddddddddddddddddddddddd".length
42
"0x" followed by 40 "d"s is 42 chars
| }); | ||
|
|
||
| it('rejects when startTime is 0', () => { | ||
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddddd' as Hex; |
There was a problem hiding this comment.
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddddd' as Hex; | |
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddd' as Hex; |
There was a problem hiding this comment.
Lol, I counted those ds very carefully!
There was a problem hiding this comment.
Actually, the existing string if 42 chars, which is 20 bytes, which is correct.
I added validation to the erc20 permission decoders to ensure that the token addresses are the correct length.
There was a problem hiding this comment.
Actually, I didn't, because we can't validate individual component lengths (the bytes are concatenated), but we do have terms length validation for each permission type.
packages/gator-permissions-controller/src/decodePermission/rules/nativeTokenPeriodic.ts
Show resolved
Hide resolved
| splitHex(terms, [20, 32, 32, 32]); | ||
| const periodDuration = hexToNumber(periodDurationRaw); | ||
| const startTime = hexToNumber(startTimeRaw); | ||
|
|
There was a problem hiding this comment.
should we add periodAmount > 0 check here?
packages/gator-permissions-controller/src/decodePermission/rules/nativeTokenPeriodic.ts
Outdated
Show resolved
Hide resolved
packages/gator-permissions-controller/src/decodePermission/rules/erc20TokenPeriodic.test.ts
Show resolved
Hide resolved
packages/gator-permissions-controller/src/decodePermission/rules/erc20TokenStream.ts
Show resolved
Hide resolved
9990e4f to
5ff2c48
Compare
packages/gator-permissions-controller/src/decodePermission/rules/erc20TokenStream.test.ts
Show resolved
Hide resolved
packages/gator-permissions-controller/src/decodePermission/rules/nativeTokenStream.ts
Show resolved
Hide resolved
packages/gator-permissions-controller/src/decodePermission/rules/nativeTokenStream.ts
Outdated
Show resolved
Hide resolved
packages/gator-permissions-controller/src/decodePermission/rules/erc20TokenStream.ts
Show resolved
Hide resolved
…ion to ensure that permission data invariants are not violated.
…ype is self-describing and can be more easily tested in isolation. Add validation and test coverage for each permission type.
Plus minor changes: - Remove redundant amendment to ChecksumCaveat type - Remove unused ValidateDecodedPermission type - Fixes controller tests that expected the controller to self-report GatorPermissionsSnap id - Make decode functions internal, and rename to align with public interface
…e ChecksumEnforcersByChainId type rather than explicitly declaring a new type
…ithout specifying snapId
- use metamask/utils isHexAddress instead of regex to validate addresses - use hexToBigInt to validate tokenPeriod instead of hexToNumber
830e176 to
feff8e1
Compare
feff8e1 to
c7ba989
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| }); | ||
|
|
||
| it('rejects when startTime is 0', () => { | ||
| const tokenAddress = '0xdddddddddddddddddddddddddddddddddddddddd' as Hex; |
There was a problem hiding this comment.
Token address has 42 hex chars (21 bytes) instead of 40
Medium Severity
The tokenAddress '0xdddddddddddddddddddddddddddddddddddddddd' contains 42 d characters (21 bytes) instead of the required 40 (20 bytes). When these tests manually construct terms via `0x${tokenAddress.slice(2)}...`, the resulting byte length is 1 byte too long. This causes the byte-length validation to reject the terms before reaching the intended validation (e.g., startTime must be a positive number or amountPerSecond must be a positive number), so these tests pass for the wrong reason and don't actually cover the validation they intend to test.
Additional Locations (2)
MoMannn
left a comment
There was a problem hiding this comment.
isSubset is now dead code and can be removed
| ); | ||
| } | ||
|
|
||
| if (periodAmountBigInt <= 0n) { |
There was a problem hiding this comment.
These can check only if === 0n. There are no negative numbers in solidity so its not possible for them to be less then 0.
|
|
||
| const EXPECTED_VALUE_LTE_TERMS_BYTELENGTH = 32; | ||
|
|
||
| if (getByteLength(valueLteTerms) !== EXPECTED_VALUE_LTE_TERMS_BYTELENGTH) { |
There was a problem hiding this comment.
This check is redundant. The next if is validating valueLteTerms !== ZERO_32_BYTES which also validates the length.
| * const result = matchingRules[0].validateAndDecodePermission(caveats); | ||
| * if (result.isValid) { ... result.expiry, result.data ... } | ||
| * | ||
| * getPermissionRuleMatchingCaveatTypes and getPermissionDataAndExpiry use these rules |
There was a problem hiding this comment.
I believe these functions got removed now?


Explanation
It's critical that the
GatorPermissionController's Permission decoding logic is strict, and will not decode EIP-712 payload to a permission unless the payload exactly meets the expectations of that permission.This PR refactors the permission rules to be more self contained, and self describing. This allows each permission type's decoding rules to be more thoroughly tested in isolation. Validation and decoding logic is combined, as decoding is an implicit part of the validation step.
This PR also adds explicit validation of the "implicit" caveats for each permission type (
valueLtefor ERC20 permissions,exactCalldatafor native token permissions), where previously we were ensuring that they caveats exist, but not validating their terms.References
Checklist
Note
Medium Risk
Refactors core permission decoding/matching logic and adds stricter term validation, which may cause previously-accepted permission contexts to be rejected if they are even slightly malformed. The change is well-covered by new rule-focused and adversarial tests but touches a security-sensitive boundary (EIP-712 permission decoding).
Overview
Refactors permission decoding to be rule-driven:
GatorPermissionsController.decodePermissionFromPermissionContextForOriginnow builds permission rules per chain, finds the single matching rule by caveat/enforcer addresses, and uses the rule to validate and decode caveat terms in one step (replacing the olderidentifyPermissionByEnforcers+getPermissionDataAndExpiryflow).Adds a new
decodePermission/ruleslayer with per-permissionvalidateAndDecodePermissionimplementations and shared utilities, tightening validation of caveat terms (e.g.,ExactCalldataEnforcermust be0x,ValueLteEnforcermust be 32-byte zero, term byte-length checks, positiveperiodAmount/durations/start times, and hextokenAddressfor ERC20 types). Extensive new tests cover each rule plus adversarial edge cases, and the changelog is updated accordingly.Written by Cursor Bugbot for commit c7ba989. This will update automatically on new commits. Configure here.