import is from '@sindresorhus/is' ; 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 = [ ] ; interface LegacyHostRule { hostName ? : string ; domainName ? : string ; baseUrl ? : string ; } function migrateRule ( rule ) { const cloned = clone ( rule ) ; delete cloned . hostName ; delete cloned . domainName ; delete cloned . baseUrl ; const result = cloned ; const { matchHost } = result ; const { hostName , domainName , baseUrl } = rule ; const hostValues = [ matchHost , hostName , domainName , baseUrl ] . filter ( Boolean ) ; if ( hostValues . length === 1 ) { const [ matchHost ] = hostValues ; result . matchHost = matchHost ; } else if ( hostValues . length > 1 ) { throw new Error ( `hostRules cannot contain more than one host-matching field - use "matchHost" only.` ) ; } return result ; } export function add ( params ) { const rule = migrateRule ( params ) ; 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 ( is . string ( 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 ) { return ! rule . hostType && ! rule . resolvedHost ; } function isHostTypeRule ( rule ) { return ! ! rule . hostType && ! rule . resolvedHost ; } function isHostOnlyRule ( rule ) { return ! rule . hostType && ! ! rule . matchHost ; } function isMultiRule ( rule ) { return ! ! rule . hostType && ! ! rule . resolvedHost ; } function matchesHostType ( rule , search ) { return rule . hostType === search . hostType ; } function matchesHost ( rule , search ) { if ( ! rule . matchHost ) { return false ; } if ( search . url && validateUrl ( rule . matchHost ) ) { return search . url . startsWith ( rule . matchHost ) ; } const parsedUrl = search . url ? parseUrl ( search . url ) : null ; 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 ) { if ( ! ( search . hostType || search . url ) ) { logger . warn ( { search } , 'Invalid hostRules search' ) ; return { } ; } let res = { } as any as HostRule ; hostRules . filter ( ( rule ) => isEmptyRule ( rule ) ) . forEach ( ( rule ) => { res = merge ( res , rule ) ; } ) ; 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 ) ; } ) ; 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 } ) { const result = [ ] ; for ( const rule of hostRules ) { if ( rule . hostType === hostType && rule . resolvedHost ) { result . push ( rule . resolvedHost ) ; } } return result ; } export function findAll ( { hostType } ) { return hostRules . filter ( ( rule ) => rule . hostType === hostType ) ; } returns export function getAll ( ) { return clone ( hostRules ) ; } export function clear ( ) { logger . debug ( 'Clearing hostRules' ) ; hostRules = [ ] ; sanitize . clear ( ) ; }