Skip to content

Commit

Permalink
Restrict http traffic
Browse files Browse the repository at this point in the history
  • Loading branch information
serjonya-trili committed Oct 7, 2024
1 parent 9d2603b commit 186186d
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 20 deletions.
7 changes: 7 additions & 0 deletions apps/desktop/public/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ function createWindow() {
}
});

mainWindow.webContents.session.webRequest.onBeforeRequest((details, callback) => {
if (details.url.startsWith("http://")) {
return callback({ cancel: true });
}
callback({});
});

protocol.handle(APP_PROTOCOL, async req => {
try {
const uri = new URL(decodeURI(req.url));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ describe("<UpsertNetworkModal />", () => {

const updatedNetwork = {
...customNetwork,
rpcUrl: "https://rpc",
tzktApiUrl: "https://tzkt",
tzktExplorerUrl: "https://explorer",
rpcUrl: "https://rpc.com",
tzktApiUrl: "https://tzkt.com",
tzktExplorerUrl: "https://explorer.com",
buyTezUrl: "",
};

Expand All @@ -57,9 +57,9 @@ describe("<UpsertNetworkModal />", () => {

const updatedNetwork = {
...customNetwork,
rpcUrl: "https://rpc",
tzktApiUrl: "https://tzkt",
tzktExplorerUrl: "https://explorer",
rpcUrl: "https://rpc.com",
tzktApiUrl: "https://tzkt.com",
tzktExplorerUrl: "https://explorer.com",
buyTezUrl: "",
};

Expand Down
14 changes: 11 additions & 3 deletions apps/desktop/src/views/settings/network/UpsertNetworkModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ModalFooter,
ModalHeader,
} from "@chakra-ui/react";
import { useDynamicModalContext } from "@umami/components";
import { useDynamicModalContext, validateUrl } from "@umami/components";
import { networksActions, useAvailableNetworks } from "@umami/state";
import { type Network } from "@umami/tezos";
import { useForm } from "react-hook-form";
Expand Down Expand Up @@ -69,6 +69,7 @@ export const UpsertNetworkModal = ({ network }: { network?: Network }) => {
{...register("rpcUrl", {
required: "RPC URL is required",
setValueAs: removeTrailingSlashes,
validate: validateUrl,
})}
/>
{errors.rpcUrl && <FormErrorMessage>{errors.rpcUrl.message}</FormErrorMessage>}
Expand All @@ -80,6 +81,7 @@ export const UpsertNetworkModal = ({ network }: { network?: Network }) => {
{...register("tzktApiUrl", {
required: "Tzkt API URL is required",
setValueAs: removeTrailingSlashes,
validate: validateUrl,
})}
/>
{errors.tzktApiUrl && <FormErrorMessage>{errors.tzktApiUrl.message}</FormErrorMessage>}
Expand All @@ -91,6 +93,7 @@ export const UpsertNetworkModal = ({ network }: { network?: Network }) => {
{...register("tzktExplorerUrl", {
required: "Tzkt Explorer URL is required",
setValueAs: removeTrailingSlashes,
validate: validateUrl,
})}
/>
{errors.tzktExplorerUrl && (
Expand All @@ -100,10 +103,15 @@ export const UpsertNetworkModal = ({ network }: { network?: Network }) => {

<FormControl>
<FormLabel>Buy Tez URL</FormLabel>
<Input placeholder="https://faucet.ghostnet.teztnets.com" {...register("buyTezUrl")} />
<Input
placeholder="https://faucet.ghostnet.teztnets.com"
{...register("buyTezUrl", {
validate: url => validateUrl(url, { allowEmpty: true }),
})}
/>
</FormControl>
<ModalFooter>
<Button width="100%" isDisabled={!isValid} onClick={() => {}} type="submit">
<Button width="100%" isDisabled={!isValid} type="submit">
{mode === "edit" ? "Save changes" : "Add network"}
</Button>
</ModalFooter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ beforeEach(() => {

describe("<EditNetworkMenu />", () => {
describe("edit mode", () => {
beforeEach(() => {
store.dispatch(networksActions.upsertNetwork(customNetwork));
});
beforeEach(() => store.dispatch(networksActions.upsertNetwork(customNetwork)));

it("doesn't render name field", async () => {
await renderInDrawer(<EditNetworkMenu network={MAINNET} />, store);
Expand All @@ -29,9 +27,9 @@ describe("<EditNetworkMenu />", () => {

const updatedNetwork = {
...customNetwork,
rpcUrl: "https://rpc",
tzktApiUrl: "https://tzkt",
tzktExplorerUrl: "https://explorer",
rpcUrl: "https://rpc.com",
tzktApiUrl: "https://tzkt.com",
tzktExplorerUrl: "https://explorer.com",
buyTezUrl: "",
};

Expand All @@ -57,9 +55,9 @@ describe("<EditNetworkMenu />", () => {

const updatedNetwork = {
...customNetwork,
rpcUrl: "https://rpc",
tzktApiUrl: "https://tzkt",
tzktExplorerUrl: "https://explorer",
rpcUrl: "https://rpc.com",
tzktApiUrl: "https://tzkt.com",
tzktExplorerUrl: "https://explorer.com",
buyTezUrl: "",
};

Expand Down
12 changes: 10 additions & 2 deletions apps/web/src/components/Menu/NetworkMenu/EditNetworkMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, FormControl, FormErrorMessage, FormLabel, Input, VStack } from "@chakra-ui/react";
import { useDynamicDrawerContext } from "@umami/components";
import { useDynamicDrawerContext, validateUrl } from "@umami/components";
import { networksActions, useAppDispatch, useAvailableNetworks } from "@umami/state";
import { type Network } from "@umami/tezos";
import { useForm } from "react-hook-form";
Expand Down Expand Up @@ -56,6 +56,7 @@ export const EditNetworkMenu = ({ network }: EditNetworkMenuProps) => {
{...register("rpcUrl", {
required: "RPC URL is required",
setValueAs: removeTrailingSlashes,
validate: validateUrl,
})}
/>
{errors.rpcUrl && <FormErrorMessage>{errors.rpcUrl.message}</FormErrorMessage>}
Expand All @@ -67,6 +68,7 @@ export const EditNetworkMenu = ({ network }: EditNetworkMenuProps) => {
{...register("tzktApiUrl", {
required: "Tzkt API URL is required",
setValueAs: removeTrailingSlashes,
validate: validateUrl,
})}
/>
{errors.tzktApiUrl && <FormErrorMessage>{errors.tzktApiUrl.message}</FormErrorMessage>}
Expand All @@ -78,6 +80,7 @@ export const EditNetworkMenu = ({ network }: EditNetworkMenuProps) => {
{...register("tzktExplorerUrl", {
required: "Tzkt Explorer URL is required",
setValueAs: removeTrailingSlashes,
validate: validateUrl,
})}
/>
{errors.tzktExplorerUrl && (
Expand All @@ -87,7 +90,12 @@ export const EditNetworkMenu = ({ network }: EditNetworkMenuProps) => {

<FormControl>
<FormLabel>Buy Tez URL</FormLabel>
<Input placeholder="https://faucet.ghostnet.teztnets.com" {...register("buyTezUrl")} />
<Input
placeholder="https://faucet.ghostnet.teztnets.com"
{...register("buyTezUrl", {
validate: url => validateUrl(url, { allowEmpty: true }),
})}
/>
</FormControl>
</VStack>
<Button width="100%" marginTop="30px" isDisabled={!isValid} type="submit" variant="primary">
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./DynamicDisclosure";
export * from "./hooks";
export * from "./ReactIdenticon";
export * from "./MnemonicAutocomplete";
export * from "./validateUrl";
1 change: 1 addition & 0 deletions packages/components/src/validateUrl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./validateUrl";
38 changes: 38 additions & 0 deletions packages/components/src/validateUrl/validateUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { validateUrl } from "./validateUrl";

describe("validateUrl", () => {
it.each(["mailto", "http", "umami", "file"])(
"returns 'Invalid URL' for %s protocol",
protocol => {
expect(validateUrl(`${protocol}://example.com`)).toBe("Invalid URL");
}
);

describe("empty URL", () => {
it("returns 'Invalid URL'", () => {
expect(validateUrl("")).toBe("Invalid URL");
expect(validateUrl(undefined)).toBe("Invalid URL");
});

it("returns true if allowEmpty option is set", () => {
expect(validateUrl("", { allowEmpty: true })).toBe(true);
expect(validateUrl(undefined, { allowEmpty: true })).toBe(true);
});
});

it('returns "Invalid URL" for invalid URL', () => {
expect(validateUrl("invalid url")).toBe("Invalid URL");
expect(validateUrl("http://invalid url")).toBe("Invalid URL");
expect(validateUrl("https://invalid url")).toBe("Invalid URL");
expect(validateUrl("https://")).toBe("Invalid URL");
expect(validateUrl("https:/example.com")).toBe("Invalid URL");
expect(validateUrl("https:/example")).toBe("Invalid URL");
});

it("returns true for a valid url", () => {
expect(validateUrl("https://example.com")).toBe(true);
expect(validateUrl("https://example.com:8080")).toBe(true);
expect(validateUrl("https://example.com/path")).toBe(true);
expect(validateUrl("https://example.com/path?query=1")).toBe(true);
});
});
23 changes: 23 additions & 0 deletions packages/components/src/validateUrl/validateUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const URL_REGEX = new RegExp(
"^(https:\\/\\/)?" + // protocol
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR IP (v4) address
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
"(\\#[-a-z\\d_]*)?$", // fragment locator
"i"
);

const ERROR_MESSAGE = "Invalid URL";

export const validateUrl = (url: string | undefined, { allowEmpty } = { allowEmpty: false }) => {
try {
if (!url) {
return allowEmpty || ERROR_MESSAGE;
}
new URL(url);
return URL_REGEX.test(url) || ERROR_MESSAGE;
} catch {
return ERROR_MESSAGE;
}
};

0 comments on commit 186186d

Please sign in to comment.