Skip to content
This repository has been archived by the owner on Nov 23, 2023. It is now read-only.

filter custom connectSrc #551

Merged
merged 3 commits into from
Apr 2, 2020
Merged
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
15 changes: 13 additions & 2 deletions src/js/data/ConnectSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ export const getEnv = () => {
return 'web';
};

// Cors validation copied from Trezor Bridge
// see: https://github.com/trezor/trezord-go/blob/05991cea5900d18bcc6ece5ae5e319d138fc5551/server/api/api.go#L229
// Its pointless to allow `trezor-connect` endpoints { connectSrc } for domains other than listed below
// `trezord` will block communication anyway
export const corsValidator = (url?: string) => {
if (typeof url !== 'string') return;
if (url.match(/^https:\/\/([A-Za-z0-9\-_]+\.)*trezor\.io\//)) return url;
if (url.match(/^https?:\/\/localhost:[58][0-9]{3}\//)) return url;
if (url.match(/^https:\/\/([A-Za-z0-9\-_]+\.)*sldev\.cz\//)) return url;
};

export const parse = (input: $Shape<ConnectSettings> = {}) => {
const settings: ConnectSettings = { ...currentSettings };
if (Object.prototype.hasOwnProperty.call(input, 'debug')) {
Expand All @@ -91,7 +102,7 @@ export const parse = (input: $Shape<ConnectSettings> = {}) => {
}
// For debugging purposes `connectSrc` could be defined in `global.__TREZOR_CONNECT_SRC` variable
if (typeof global !== 'undefined' && typeof global.__TREZOR_CONNECT_SRC === 'string') {
settings.connectSrc = global.__TREZOR_CONNECT_SRC;
settings.connectSrc = corsValidator(global.__TREZOR_CONNECT_SRC);
}

// For debugging purposes `connectSrc` could be defined in url query of hosting page. Usage:
Expand All @@ -101,7 +112,7 @@ export const parse = (input: $Shape<ConnectSettings> = {}) => {
const customUrl = vars.find(v => v.indexOf('trezor-connect-src') >= 0);
if (customUrl) {
const [, connectSrc] = customUrl.split('=');
settings.connectSrc = decodeURIComponent(connectSrc);
settings.connectSrc = corsValidator(decodeURIComponent(connectSrc));
}
}
const src = settings.connectSrc || DEFAULT_DOMAIN;
Expand Down
72 changes: 72 additions & 0 deletions src/js/data/__tests__/ConnectSettings.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { corsValidator, parse } from '../ConnectSettings';

describe('data/ConnectSettings', () => {
const { location } = window;
beforeAll(() => {
delete window.location;
});
afterAll(() => {
window.location = location; // restore default
});

it('corsValidator', () => {
expect(corsValidator('https://connect.trezor.io/8-beta/')).toBeDefined();
expect(corsValidator('https://az-AZ_123.trezor.io/')).toBeDefined();
expect(corsValidator('https://multiple.sub.domain.trezor.io/')).toBeDefined();
expect(corsValidator('https://trezor.sldev.io/')).not.toBeDefined();
expect(corsValidator('https://testxtrezor.io/')).not.toBeDefined();
expect(corsValidator('https://testxtrezorxio/')).not.toBeDefined();
expect(corsValidator('https://non!alpha*numeric?.trezor.io/')).not.toBeDefined();
expect(corsValidator('https://connect.trezor.io')).not.toBeDefined(); // missing slash at the end
expect(corsValidator('http://connect.trezor.io/')).not.toBeDefined(); // missing https
expect(corsValidator('https://localhost:8088/')).toBeDefined();
expect(corsValidator('https://localhost:5088/')).toBeDefined();
expect(corsValidator('https://localhost:8088/subdir/')).toBeDefined();
expect(corsValidator('http://localhost:8088/')).toBeDefined();
expect(corsValidator('https://connect.sldev.cz/')).toBeDefined();
expect(corsValidator('https://az-AZ_123.sldev.cz/')).toBeDefined();
expect(corsValidator('https://multiple.sub.domain.sldev.cz/')).toBeDefined();
expect(corsValidator('https://sldev.trezor.cz/')).not.toBeDefined();
expect(corsValidator('https://testxsldev.cz/')).not.toBeDefined();
expect(corsValidator('https://testxsldevxcz/')).not.toBeDefined();
expect(corsValidator('https://non!alpha*numeric?.sldev.cz/')).not.toBeDefined();
expect(corsValidator('https://connect.sldev.cz')).not.toBeDefined(); // missing slash at the end
expect(corsValidator('http://connect.sldev.cz/')).not.toBeDefined(); // missing https
expect(corsValidator(null)).not.toBeDefined();
expect(corsValidator(undefined)).not.toBeDefined();
expect(corsValidator({})).not.toBeDefined();
expect(corsValidator(1)).not.toBeDefined();
expect(corsValidator('https://other-domain.com/connect.trezor.io/8/')).not.toBeDefined();
});

it('parse: custom connect src', () => {
window.location = { search: 'trezor-connect-src=https://connect.trezor.io/beta.1/' };
expect(parse({}).connectSrc).toEqual('https://connect.trezor.io/beta.1/');

window.location = { search: 'foo=bar&trezor-connect-src=https://connect.trezor.io/beta.2/' };
expect(parse({}).connectSrc).toEqual('https://connect.trezor.io/beta.2/');

window.location = { search: 'trezor-connect-src=https://connect.trezor.io/beta.3/&foo=bar' };
expect(parse({}).connectSrc).toEqual('https://connect.trezor.io/beta.3/');

window.location = { search: 'trezor-connect-src=https%3A%2F%2Fconnect.trezor.io%2Fbeta.encoded%2F' }; // encoded
expect(parse({}).connectSrc).toEqual('https://connect.trezor.io/beta.encoded/');

window.location = { search: 'trezor-connect-src=https://connect-beta.trezor.oi/beta.3/' }; // invalid domain "io"
expect(parse({}).connectSrc).toEqual(undefined);

delete window.location.search; // restore

window.__TREZOR_CONNECT_SRC = 'https://connect.trezor.io/beta.4/';
expect(parse({}).connectSrc).toEqual('https://connect.trezor.io/beta.4/');

window.__TREZOR_CONNECT_SRC = 'https://connect-beta.trezor.oi/beta.4/'; // invalid domain
expect(parse({}).connectSrc).toEqual(undefined);

delete window.__TREZOR_CONNECT_SRC; // restore

global.__TREZOR_CONNECT_SRC = 'https://connect.trezor.io/beta.4/';
expect(parse({}).connectSrc).toEqual('https://connect.trezor.io/beta.4/');
delete global.__TREZOR_CONNECT_SRC; // restore
});
});