diff --git a/CHANGELOG.md b/CHANGELOG.md index 62517f86f1..a40b46d10d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to available under `tendermint33`. - @cosmjs/proto-signing and @cosmjs/stargate: Create a Stargate-ready `parseCoins` that replaces the `parseCoins` re-export from `@cosmjs/amino`. +- @cosmjs/cosmwasm-stargate: Export `isValidBuilder`, which is a clone of + `isValidBuilder` from @cosmjs/cosmwasm-launchpad. ### Changed diff --git a/packages/cosmwasm-stargate/src/builder.spec.ts b/packages/cosmwasm-stargate/src/builder.spec.ts new file mode 100644 index 0000000000..20926f4c82 --- /dev/null +++ b/packages/cosmwasm-stargate/src/builder.spec.ts @@ -0,0 +1,63 @@ +import { isValidBuilder } from "./builder"; + +describe("builder", () => { + describe("isValidBuilder", () => { + // Valid cases + + it("returns true for simple examples", () => { + expect(isValidBuilder("myorg/super-optimizer:0.1.2")).toEqual(true); + expect(isValidBuilder("myorg/super-optimizer:42")).toEqual(true); + }); + + it("supports images with multi level names", () => { + expect(isValidBuilder("myorg/department-x/office-y/technology-z/super-optimizer:0.1.2")).toEqual(true); + }); + + it("returns true for tags with lower and upper chars", () => { + expect(isValidBuilder("myorg/super-optimizer:0.1.2-alpha")).toEqual(true); + expect(isValidBuilder("myorg/super-optimizer:0.1.2-Alpha")).toEqual(true); + }); + + // Invalid cases + + it("returns false for missing or empty tag", () => { + expect(isValidBuilder("myorg/super-optimizer")).toEqual(false); + expect(isValidBuilder("myorg/super-optimizer:")).toEqual(false); + }); + + it("returns false for name components starting or ending with a separator", () => { + expect(isValidBuilder(".myorg/super-optimizer:42")).toEqual(false); + expect(isValidBuilder("-myorg/super-optimizer:42")).toEqual(false); + expect(isValidBuilder("_myorg/super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg./super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg-/super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg_/super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg/.super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg/-super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg/_super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg/super-optimizer.:42")).toEqual(false); + expect(isValidBuilder("myorg/super-optimizer-:42")).toEqual(false); + expect(isValidBuilder("myorg/super-optimizer_:42")).toEqual(false); + }); + + it("returns false for upper case character in name component", () => { + expect(isValidBuilder("mYorg/super-optimizer:42")).toEqual(false); + expect(isValidBuilder("myorg/super-Optimizer:42")).toEqual(false); + }); + + it("returns false for long images", () => { + expect( + isValidBuilder( + "myorgisnicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenicenice/super-optimizer:42", + ), + ).toEqual(false); + }); + + it("returns false for images with no organization", () => { + // Those are valid dockerhub images from https://hub.docker.com/_/ubuntu and https://hub.docker.com/_/rust + // but not valid in the context of CosmWasm Verify + expect(isValidBuilder("ubuntu:xenial-20200212")).toEqual(false); + expect(isValidBuilder("rust:1.40.0")).toEqual(false); + }); + }); +}); diff --git a/packages/cosmwasm-stargate/src/builder.ts b/packages/cosmwasm-stargate/src/builder.ts new file mode 100644 index 0000000000..31a790c79a --- /dev/null +++ b/packages/cosmwasm-stargate/src/builder.ts @@ -0,0 +1,20 @@ +// A docker image regexp. We remove support for non-standard registries for simplicity. +// https://docs.docker.com/engine/reference/commandline/tag/#extended-description +// +// An image name is made up of slash-separated name components (optionally prefixed by a registry hostname). +// Name components may contain lowercase characters, digits and separators. +// A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator. +// +// A tag name must be valid ASCII and may contain lowercase and uppercase letters, digits, underscores, periods and dashes. +// A tag name may not start with a period or a dash and may contain a maximum of 128 characters. +const dockerImagePattern = new RegExp( + "^[a-z0-9][a-z0-9._-]*[a-z0-9](/[a-z0-9][a-z0-9._-]*[a-z0-9])+:[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$", +); + +/** Max length in bytes/characters (regexp enforces all ASCII, even if that is not required by the standard) */ +const builderMaxLength = 128; + +export function isValidBuilder(builder: string): boolean { + if (builder.length > builderMaxLength) return false; + return !!builder.match(dockerImagePattern); +} diff --git a/packages/cosmwasm-stargate/src/index.ts b/packages/cosmwasm-stargate/src/index.ts index e90bbc571d..c02bdae8f0 100644 --- a/packages/cosmwasm-stargate/src/index.ts +++ b/packages/cosmwasm-stargate/src/index.ts @@ -1,4 +1,5 @@ export { cosmWasmTypes } from "./aminotypes"; +export { isValidBuilder } from "./builder"; export { Code, CodeDetails, diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index 8d614adc1d..f02a4f8474 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { encodeSecp256k1Pubkey, makeSignDoc as makeSignDocAmino } from "@cosmjs/amino"; -import { isValidBuilder } from "@cosmjs/cosmwasm-launchpad"; import { sha256 } from "@cosmjs/crypto"; import { fromBase64, toHex, toUtf8 } from "@cosmjs/encoding"; import { Int53, Uint53 } from "@cosmjs/math"; @@ -47,6 +46,7 @@ import Long from "long"; import pako from "pako"; import { cosmWasmTypes } from "./aminotypes"; +import { isValidBuilder } from "./builder"; import { CosmWasmClient } from "./cosmwasmclient"; import { MsgClearAdminEncodeObject,