Skip to content
Snippets Groups Projects
Select Git revision
  • 91f067b39f1db50024d226dff5d6263dca5fcc40
  • main default protected
  • renovate/main-renovatebot-osv-offline-1.x
  • fix/36927-maven-tags
  • renovate/main-redis-5.x
  • next
  • revert-31645-feat/rename-gradle-wrapper-validation-action
  • fix/36615b-branch-reuse-no-cache
  • chore/punycode
  • refactor/pin-new-value
  • feat/36219--git-x509-signing
  • feat/structured-logger
  • hotfix/39.264.1
  • feat/skip-dangling
  • gh-readonly-queue/next/pr-36034-7a061c4ca1024a19e2c295d773d9642625d1c2be
  • hotfix/39.238.3
  • refactor/gitlab-auto-approve
  • feat/template-strings
  • gh-readonly-queue/next/pr-35654-137d934242c784e0c45d4b957362214f0eade1d7
  • fix/32307-global-extends-merging
  • fix/32307-global-extends-repositories
  • 41.37.7
  • 41.37.6
  • 41.37.5
  • 41.37.4
  • 41.37.3
  • 41.37.2
  • 41.37.1
  • 41.37.0
  • 41.36.2
  • 41.36.1
  • 41.36.0
  • 41.35.2
  • 41.35.1
  • 41.35.0
  • 41.34.1
  • 41.34.0
  • 41.33.0
  • 41.32.3
  • 41.32.2
  • 41.32.1
41 results

host-rules.ts

Blame
  • user avatar
    David Straub authored and GitHub committed
    Co-authored-by: default avatarRhys Arkins <rhys@arkins.net>
    8fb9197d
    History
    host-rules.ts 4.07 KiB
    import merge from 'deepmerge';
    import { logger } from '../logger';
    import { HostRule } from '../types';
    import { clone } from './clone';
    import * as sanitize from './sanitize';
    import { parseUrl, validateUrl } from './url';
    
    let hostRules: HostRule[] = [];
    
    const legacyHostFields = ['hostName', 'domainName', 'baseUrl'];
    
    export function add(params: HostRule): void {
      const rule = clone(params);
      const matchedFields = legacyHostFields.filter((field) => rule[field]);
      if (matchedFields.length) {
        if (rule.matchHost || matchedFields.length > 1) {
          throw new Error(
            `hostRules cannot contain more than one host-matching field - use "matchHost" only.`
          );
        }
        const field = matchedFields[0];
        logger.warn({ field }, 'Legacy hostRules field needs migrating');
        rule.matchHost = rule[field];
        delete rule[field];
      }
    
      const confidentialFields = ['password', 'token'];
      if (rule.matchHost) {
        const parsedUrl = parseUrl(rule.matchHost);
        rule.resolvedHost = parsedUrl?.hostname || rule.matchHost;
        confidentialFields.forEach((field) => {
          if (rule[field]) {
            logger.debug(
              `Adding ${field} authentication for ${rule.matchHost} to hostRules`
            );
          }
        });
      }
      confidentialFields.forEach((field) => {
        const secret = rule[field];
        if (secret && secret.length > 3) {
          sanitize.add(secret);
        }
      });
      if (rule.username && rule.password) {
        const secret = Buffer.from(`${rule.username}:${rule.password}`).toString(
          'base64'
        );
        sanitize.add(secret);
      }
      hostRules.push(rule);
    }
    
    export interface HostRuleSearch {
      hostType?: string;
      url?: string;
    }
    
    function isEmptyRule(rule: HostRule): boolean {
      return !rule.hostType && !rule.resolvedHost;
    }
    
    function isHostTypeRule(rule: HostRule): boolean {
      return rule.hostType && !rule.resolvedHost;
    }
    
    function isHostOnlyRule(rule: HostRule): boolean {
      return !rule.hostType && !!rule.matchHost;
    }
    
    function isMultiRule(rule: HostRule): boolean {
      return rule.hostType && !!rule.resolvedHost;
    }
    
    function matchesHostType(rule: HostRule, search: HostRuleSearch): boolean {
      return rule.hostType === search.hostType;
    }
    
    function matchesHost(rule: HostRule, search: HostRuleSearch): boolean {
      if (validateUrl(rule.matchHost)) {
        return search.url.startsWith(rule.matchHost);
      }
      const parsedUrl = parseUrl(search.url);
      if (!parsedUrl?.hostname) {
        return false;
      }
      const { hostname } = parsedUrl;
      const dotPrefixedMatchHost = rule.matchHost.startsWith('.')
        ? rule.matchHost
        : `.${rule.matchHost}`;
      return hostname === rule.matchHost || hostname.endsWith(dotPrefixedMatchHost);
    }
    
    export function find(search: HostRuleSearch): HostRule {
      if (!(search.hostType || search.url)) {
        logger.warn({ search }, 'Invalid hostRules search');
        return {};
      }
      let res = {} as any as HostRule;
      // First, apply empty rule matches
      hostRules
        .filter((rule) => isEmptyRule(rule))
        .forEach((rule) => {
          res = merge(res, rule);
        });
      // Next, find hostType-only matches
      hostRules
        .filter((rule) => isHostTypeRule(rule) && matchesHostType(rule, search))
        .forEach((rule) => {
          res = merge(res, rule);
        });
      hostRules
        .filter((rule) => isHostOnlyRule(rule) && matchesHost(rule, search))
        .forEach((rule) => {
          res = merge(res, rule);
        });
      // Finally, find combination matches
      hostRules
        .filter(
          (rule) =>
            isMultiRule(rule) &&
            matchesHostType(rule, search) &&
            matchesHost(rule, search)
        )
        .forEach((rule) => {
          res = merge(res, rule);
        });
      delete res.hostType;
      delete res.resolvedHost;
      delete res.matchHost;
      return res;
    }
    
    export function hosts({ hostType }: { hostType: string }): string[] {
      return hostRules
        .filter((rule) => rule.hostType === hostType)
        .map((rule) => rule.resolvedHost)
        .filter(Boolean);
    }
    
    export function findAll({ hostType }: { hostType: string }): HostRule[] {
      return hostRules.filter((rule) => rule.hostType === hostType);
    }
    
    export function clear(): void {
      logger.debug('Clearing hostRules');
      hostRules = [];
      sanitize.clear();
    }