Skip to content

Commit

Permalink
Feature: Add client input validation (#278)
Browse files Browse the repository at this point in the history
* add client input validation

* fix regex recursivity

* remove unnecesary tests and fix asyn validation

* remove comment

* fix code smells

* fix build

* fix rebase
  • Loading branch information
josemarinas authored Oct 9, 2023
1 parent 878910a commit 8a4bf30
Show file tree
Hide file tree
Showing 36 changed files with 3,423 additions and 188 deletions.
5 changes: 5 additions & 0 deletions modules/client-common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ TEMPLATE:
-->

## [UPCOMING]
### Added

- Add common schemas for validations

## 1.6.0

### Added

Expand Down
3 changes: 2 additions & 1 deletion modules/client-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@ethersproject/providers": "^5.5.0",
"@ethersproject/wallet": "^5.6.0",
"graphql": "^16.5.0",
"graphql-request": "^4.3.0"
"graphql-request": "^4.3.0",
"yup": "^1.2.0"
}
}
29 changes: 29 additions & 0 deletions modules/client-common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { activeContractsList as activeContractsListV1_0_0 } from "@aragon/osx-et
import { ProposalMetadata, SupportedNetwork, SupportedVersion } from "./types";
import { NetworkDeployment } from "./internal";
import { Network } from "@ethersproject/networks";
import { keccak256 } from "@ethersproject/keccak256";
import { toUtf8Bytes } from "@ethersproject/strings";

/** Timeout that will be applied to operations involving
* many fetch requests that could take a long time */
Expand Down Expand Up @@ -344,3 +346,30 @@ export const ADDITIONAL_NETWORKS: Network[] = [
chainId: 31337,
},
];

const Permissions = {
UPGRADE_PERMISSION: "UPGRADE_PERMISSION",
SET_METADATA_PERMISSION: "SET_METADATA_PERMISSION",
EXECUTE_PERMISSION: "EXECUTE_PERMISSION",
WITHDRAW_PERMISSION: "WITHDRAW_PERMISSION",
SET_SIGNATURE_VALIDATOR_PERMISSION: "SET_SIGNATURE_VALIDATOR_PERMISSION",
SET_TRUSTED_FORWARDER_PERMISSION: "SET_TRUSTED_FORWARDER_PERMISSION",
ROOT_PERMISSION: "ROOT_PERMISSION",
CREATE_VERSION_PERMISSION: "CREATE_VERSION_PERMISSION",
REGISTER_PERMISSION: "REGISTER_PERMISSION",
REGISTER_DAO_PERMISSION: "REGISTER_DAO_PERMISSION",
REGISTER_ENS_SUBDOMAIN_PERMISSION: "REGISTER_ENS_SUBDOMAIN_PERMISSION",
MINT_PERMISSION: "MINT_PERMISSION",
MERKLE_MINT_PERMISSION: "MERKLE_MINT_PERMISSION",
MODIFY_ALLOWLIST_PERMISSION: "MODIFY_ALLOWLIST_PERMISSION",
SET_CONFIGURATION_PERMISSION: "SET_CONFIGURATION_PERMISSION",
};

const PermissionIds = Object.entries(Permissions).reduce(
(acc, [k, v]) => ({ ...acc, [k + "_ID"]: keccak256(toUtf8Bytes(v)) }),
{} as { [k: string]: string },
);
Object.freeze(Permissions);
export { Permissions };
Object.freeze(PermissionIds);
export { PermissionIds };
1 change: 1 addition & 0 deletions modules/client-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./context";
export * from "./constants";
export * from "./types";
export * from "./utils";
export * from "./schemas";
1 change: 1 addition & 0 deletions modules/client-common/src/internal/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ANY_ADDRESS = "0xffffffffffffffffffffffffffffffffffffffff";
99 changes: 99 additions & 0 deletions modules/client-common/src/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
InvalidAddressOrEnsError,
InvalidCidError,
InvalidContractAbiError,
InvalidParameter,
InvalidSubdomainError,
isEnsName,
isIpfsUri,
isSubdomain,
} from "@aragon/sdk-common";
import { array, mixed, number, object, string } from "yup";
import { isAddress } from "@ethersproject/address";
import { ANY_ADDRESS } from "./internal/constants";

export const BigintSchema = mixed().test(
"isBigint",
new InvalidParameter("bigint").message,
(value) => typeof value === "bigint",
);
export const AddressOrEnsSchema = string().notRequired().test(
"isAddressOrEns",
new InvalidAddressOrEnsError().message,
(value) => value ? isAddress(value) || isEnsName(value) : true,
);
export const AddressOrEnsWithoutAnySchema = string().notRequired().test(
"isAddressOrEnsWithoutAny",
new InvalidAddressOrEnsError().message,
(value) => value ? (isAddress(value) || isEnsName(value)) && value !== ANY_ADDRESS : true
);
export const VersionTagSchema = object({
build: number().moreThan(0).required(),
release: number().moreThan(0).required(),
});
export const AbiSchema = array().notRequired().test(
"isValidAbi",
new InvalidContractAbiError().message,
// TODO: validate abi
() => true,
);
export const Uint8ArraySchema = mixed().test(
"isUint8Array",
new InvalidParameter("Uint8Array").message,
(value) => value ? value instanceof Uint8Array : true,
);
export const IpfsUriSchema = string().test(
"isIpfsUri",
new InvalidCidError().message,
(value) => value ? isIpfsUri(value) : true,
);
export const SubdomainSchema = string().test(
"isSubdomain",
new InvalidSubdomainError().message,
(value) => value ? isSubdomain(value) : true,
);

export const PaginationSchema = object({
skip: number().min(0).notRequired(),
limit: number().min(1).notRequired(),
direction: string().oneOf(["asc", "desc"]).notRequired(),
});

export const PrepareUninstallationSchema = object({
daoAddressOrEns: AddressOrEnsSchema.required(),
pluginAddress: AddressOrEnsSchema.required(),
pluginInstallationIndex: number().notRequired().min(0),
uninstallationParams: array().notRequired(),
uninstallationAbi: AbiSchema.notRequired(),
});
export const MultiTargetPermissionSchema = object({
operation: number().required().oneOf([0, 1, 2]),
permissionId: string().required(),
where: AddressOrEnsWithoutAnySchema.required(),
who: AddressOrEnsWithoutAnySchema.required(),
condition: string().notRequired(),
});

export const PrepareInstallationSchema = object({
daoAddressOrEns: AddressOrEnsSchema.required(),
pluginRepo: AddressOrEnsSchema.required(),
version: VersionTagSchema.notRequired(),
installationParams: array().notRequired(),
installationAbi: AbiSchema.notRequired(),
});

export const PluginInstallItemSchema = object({
id: AddressOrEnsSchema.required(),
data: Uint8ArraySchema.required(),
});

export const ApplyUninstallationSchema = object({
pluginAddress: AddressOrEnsSchema.required(),
pluginRepo: AddressOrEnsSchema.required(),
versionTag: VersionTagSchema.required(),
permissions: array(MultiTargetPermissionSchema).required(),
});

export const ApplyInstallationSchema = ApplyUninstallationSchema.concat(object({
helpers: array(AddressOrEnsSchema).required(),
}));
21 changes: 21 additions & 0 deletions modules/client-common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,27 @@ export type DecodedApplyInstallationParams = ApplyInstallationParamsBase & {
helpersHash: string;
};

/* Uninstallation */
export type PrepareUninstallationParams = {
daoAddressOrEns: string;
pluginAddress: string;
pluginInstallationIndex?: number;
uninstallationParams?: any[];
uninstallationAbi?: string[];
};
export enum PrepareUninstallationSteps {
PREPARING = "preparing",
DONE = "done",
}
export type PrepareUninstallationStepValue =
| { key: PrepareUninstallationSteps.PREPARING; txHash: string }
| {
key: PrepareUninstallationSteps.DONE;
} & ApplyUninstallationParams;

export type ApplyUninstallationParams = ApplyInstallationParamsBase;
export type DecodedApplyUninstallationParams = ApplyInstallationParamsBase;

export type VersionTag = {
build: number;
release: number;
Expand Down
21 changes: 21 additions & 0 deletions modules/client-common/test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,24 @@ export const TEST_ABI: MetadataAbiInput[] = [
],
},
];


export const TEST_ADDRESS = "0x0000000000000000000000000000000000000001";
export const TEST_INVALID_ADDRESS =
"0x000000000000000000000000000000000000000P";

export const TEST_ENS_NAME = "test.eth";
export const TEST_INVALID_ENS_NAME = "test.invalid";

export const TEST_IPFS_URI_V0 =
"ipfs://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR";
export const TEST_IPFS_URI_V1 =
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi";
export const TEST_INVALID_IPFS_URI =
"ipfs://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR-invalid";

export const TEST_HTTP_URI = "https://test.com";
export const TEST_INVALID_HTTP_URI = "https://te?st.com-invalid";

export const TEST_SUBDOMAIN = "test";
export const TEST_INVALID_SUBDOMAIN = "test.invalid";
Loading

0 comments on commit 8a4bf30

Please sign in to comment.