Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/gator-permissions-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Bump `@metamask/transaction-controller` from `^62.17.0` to `^62.20.0` ([#7996](https://github.com/MetaMask/core/pull/7996), [#8005](https://github.com/MetaMask/core/pull/8005), [#8031](https://github.com/MetaMask/core/pull/8031) [#8104](https://github.com/MetaMask/core/pull/8104))
- Improves permission validation during decoding ([#7844](https://github.com/MetaMask/core/pull/7844))
- Validates `ExactCalldataEnforcer` and `ValueLteEnforcer` caveat terms
- Validates that `periodAmount` is positive in `erc20-token-periodic` and `native-token-periodic` permission types
- Validates that `tokenAddress` is a valid hex string in `erc20-token-periodic` and `erc20-token-stream` permission types
- Bump `@metamask/transaction-controller` from `^62.17.0` to `^62.20.0` ([#7996](https://github.com/MetaMask/core/pull/7996), [#8005](https://github.com/MetaMask/core/pull/8005), [#8031](https://github.com/MetaMask/core/pull/8031), [#8104](https://github.com/MetaMask/core/pull/8104))

## [2.0.0]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID =
'local:http://localhost:8082' as SnapId;

const DEFAULT_TEST_CONFIG = {
gatorPermissionsProviderSnapId: MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID,
supportedPermissionTypes: [
'native-token-stream',
'native-token-periodic',
Expand Down Expand Up @@ -141,6 +142,24 @@ describe('GatorPermissionsController', () => {

expect(controller.state.isFetchingGatorPermissions).toBe(false);
});

it('instantiates successfully without gatorPermissionsProviderSnapId', () => {
const configWithoutSnapId = {
supportedPermissionTypes: DEFAULT_TEST_CONFIG.supportedPermissionTypes,
};

let controller: GatorPermissionsController | undefined;

expect(() => {
controller = new GatorPermissionsController({
messenger: getGatorPermissionsControllerMessenger(),
config: configWithoutSnapId,
});
}).not.toThrow();

expect(controller).toBeDefined();
expect(controller?.state.grantedPermissions).toStrictEqual([]);
});
});

describe('fetchAndUpdateGatorPermissions', () => {
Expand Down Expand Up @@ -721,6 +740,51 @@ describe('GatorPermissionsController', () => {
).toThrow('Failed to decode permission');
});

it('throws when caveat terms are invalid for the matched permission rule', () => {
const {
TimestampEnforcer,
NativeTokenStreamingEnforcer,
ExactCalldataEnforcer,
NonceEnforcer,
} = contracts;

const expiryTerms = createTimestampTerms(
{ timestampAfterThreshold: 0, timestampBeforeThreshold: 1720000 },
{ out: 'hex' },
);

// Enforcers match native-token-stream but stream terms are truncated (invalid)
const truncatedStreamTerms: Hex = `0x${'00'.repeat(50)}`;
const caveats = [
{
enforcer: TimestampEnforcer,
terms: expiryTerms,
args: '0x',
} as const,
{
enforcer: NativeTokenStreamingEnforcer,
terms: truncatedStreamTerms,
args: '0x',
} as const,
{ enforcer: ExactCalldataEnforcer, terms: '0x', args: '0x' } as const,
{ enforcer: NonceEnforcer, terms: '0x', args: '0x' } as const,
];

expect(() =>
controller.decodePermissionFromPermissionContextForOrigin({
origin: MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID,
chainId,
delegation: {
delegate: delegatorAddressA,
delegator: delegateAddressB,
authority: ROOT_AUTHORITY as Hex,
caveats,
},
metadata: buildMetadata(''),
}),
).toThrow('Failed to decode permission');
});

it('throws when authority is not ROOT_AUTHORITY', () => {
const {
TimestampEnforcer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import type { Hex } from '@metamask/utils';
import { DELEGATION_FRAMEWORK_VERSION } from './constants';
import type { DecodedPermission } from './decodePermission';
import {
getPermissionDataAndExpiry,
identifyPermissionByEnforcers,
createPermissionRulesForContracts,
findRuleWithMatchingCaveatAddresses,
reconstructDecodedPermission,
} from './decodePermission';
import {
Expand Down Expand Up @@ -586,21 +586,30 @@ export default class GatorPermissionsController extends BaseController<

try {
const enforcers = caveats.map((caveat) => caveat.enforcer);
const permissionRules = createPermissionRulesForContracts(contracts);

const permissionType = identifyPermissionByEnforcers({
// find the single rule where the specified enforcers contain all the required enforcers
// and no forbidden enforcers
const matchingRule = findRuleWithMatchingCaveatAddresses({
enforcers,
contracts,
permissionRules,
});

const { expiry, data } = getPermissionDataAndExpiry({
contracts,
caveats,
permissionType,
});
// validate the terms of each caveat against the matching rule, returning the decoded result
// this happens in a single function, as decoding is an inherent part of validation.
const decodeResult = matchingRule.validateAndDecodePermission(caveats);

if (!decodeResult.isValid) {
throw new PermissionDecodingError({
cause: decodeResult.error,
});
}

const { expiry, data } = decodeResult;

const permission = reconstructDecodedPermission({
chainId,
permissionType,
permissionType: matchingRule.permissionType,
delegator,
delegate,
authority,
Expand Down
Loading
Loading