isTrustedUrlPattern.js 3.38 KiB
Newer Older
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: BSD-3-Clause

import UrlPattern from 'url-pattern';

import { SECURITY_OPTIONS } from '../options/config';

const { TRUSTED_HOSTS } = SECURITY_OPTIONS.fetherNetwork;

// Github
const GITHUB_TRUSTED_HOSTS = TRUSTED_HOSTS.github;

// Blockscout - Only includes those that are available on the Blockscout website
const BLOCKSCOUT_TRUSTED_HOSTS = TRUSTED_HOSTS.blockscout;
const BLOCKSCOUT_CHAINS_SUPPORTED = ['eth', 'etc', 'poa'];
const BLOCKSCOUT_NETWORKS_SUPPORTED = [
  'mainnet',
  'classic',
  'ropsten',
  'kovan',
  'goerli',
  'core',
  'dai',
  'sokol',
  'rinkeby'
];
const BLOCKSCOUT_HASH_KINDS = ['tx', 'address'];
const BLOCKSCOUT_HASH_TRAILERS = [
  'coin_balances',
  'internal_transactions',
  'tokens',
  'transactions'
];
const HASH_ADDRESS_LENGTH = 40;
const HASH_TX_LENGTH = 64;

// Url Patterns
const GENERAL_PATTERN = new UrlPattern(
  '(https\\://)(:subdomain.):domain.:tld(\\::port)(/*)'
);
const GITHUB_PATTERN_1 = new UrlPattern(
  '(https\\://)(:subdomain.):domain.:tld(/)(*)(.png|.jpg)'
);
const GITHUB_PATTERN_2 = new UrlPattern(
  '(https\\://)(:subdomain.):domain.:tld(/)atomiclabs/cryptocurrency-icons/(*)(.png|.jpg)'
);
const GITHUB_PATTERN_3 = new UrlPattern(
  '(https\\://)(:subdomain.):domain.:tld(/)ethcore/(*)(.png|.jpg)'
);
const BLOCKSCOUT_PATTERN = new UrlPattern(
  '(https\\://)(:subdomain.):domain.:tld(/):chain(/):network(/):hashKind(/0x):hash(/:hashTrailer)'
);

function isValidGithubUrl (url) {
  const match =
    GITHUB_PATTERN_1.match(url) ||
    GITHUB_PATTERN_2.match(url) ||
    GITHUB_PATTERN_3.match(url);

  if (!match) {
    return false;
  }

  return true;
}

function isValidBlockscoutUrl (url) {
  const match = BLOCKSCOUT_PATTERN.match(url);

  if (!match) {
    return false;
  }

  if (
    BLOCKSCOUT_CHAINS_SUPPORTED.includes(match.chain) &&
    BLOCKSCOUT_NETWORKS_SUPPORTED.includes(match.network) &&
    BLOCKSCOUT_HASH_KINDS.includes(match.hashKind) &&
    [HASH_ADDRESS_LENGTH, HASH_TX_LENGTH].includes(match.hash.length) &&
    BLOCKSCOUT_HASH_TRAILERS.includes(match.hashTrailer)
  ) {
    return true;
  }

  return false;
}

/**
 * List of trusted subdomains, domain extensions, and ports, as relevant.
 * Detect trusted prefix and then call method to validate it.
 */
function isValidUrl (url) {
  const match = GENERAL_PATTERN.match(url);

  if (!match) {
    return false;
  }

  if (!url.startsWith('https')) {
    return false;
  }

  // Blockscout URL
  if (
    BLOCKSCOUT_TRUSTED_HOSTS.includes(
      (match.subdomain && match.subdomain) +
        (match.subdomain && '.') +
        (match.domain && match.domain) +
        (match.domain && '.') +
        (match.tld && match.tld)
    ) ||
    BLOCKSCOUT_TRUSTED_HOSTS.includes(
      (match.domain && match.domain) +
        (match.domain && '.') +
        (match.tld && match.tld)
    )
  ) {
    return isValidBlockscoutUrl(url);
  }

  // Github URL
  if (
    GITHUB_TRUSTED_HOSTS.includes(
      (match.subdomain && match.subdomain) +
        (match.subdomain && '.') +
        (match.domain && match.domain) +
        (match.domain && '.') +
        (match.tld && match.tld)
    ) ||
    GITHUB_TRUSTED_HOSTS.includes(
      (match.domain && match.domain) +
        (match.domain && '.') +
        (match.tld && match.tld)
    )
  ) {
    return isValidGithubUrl(url);
  }
}

export default isValidUrl;