Skip to content

Commit

Permalink
feat(hostRules/matchHost): massage and validate (#29487)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
RahulGautamSingh and rarkins authored Jun 28, 2024
1 parent c314022 commit b8b7607
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 22 deletions.
2 changes: 2 additions & 0 deletions lib/config/migrations/custom/host-rules-migration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('config/migrations/custom/host-rules-migration', () => {
{ hostName: 'some.domain.com', token: '123test' },
{ endpoint: 'domain.com/', token: '123test' },
{ host: 'some.domain.com', token: '123test' },
{ matchHost: 'some.domain.com:8080', token: '123test' },
],
} as any,
{
Expand Down Expand Up @@ -58,6 +59,7 @@ describe('config/migrations/custom/host-rules-migration', () => {
{ matchHost: 'some.domain.com', token: '123test' },
{ matchHost: 'https://domain.com/', token: '123test' },
{ matchHost: 'some.domain.com', token: '123test' },
{ matchHost: 'https://some.domain.com:8080', token: '123test' },
],
},
);
Expand Down
13 changes: 3 additions & 10 deletions lib/config/migrations/custom/host-rules-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CONFIG_VALIDATION } from '../../../constants/error-messages';
import { logger } from '../../../logger';
import type { HostRule } from '../../../types';
import type { LegacyHostRule } from '../../../util/host-rules';
import { massageHostUrl } from '../../../util/url';
import { AbstractMigration } from '../base/abstract-migration';
import { migrateDatasource } from './datasource-migration';

Expand All @@ -25,7 +26,7 @@ export class HostRulesMigration extends AbstractMigration {

if (key === 'matchHost') {
if (is.string(value)) {
newRule.matchHost ??= massageUrl(value);
newRule.matchHost ??= massageHostUrl(value);
}
continue;
}
Expand All @@ -45,7 +46,7 @@ export class HostRulesMigration extends AbstractMigration {
key === 'domainName'
) {
if (is.string(value)) {
newRule.matchHost ??= massageUrl(value);
newRule.matchHost ??= massageHostUrl(value);
}
continue;
}
Expand Down Expand Up @@ -91,14 +92,6 @@ function validateHostRule(rule: LegacyHostRule & HostRule): void {
}
}

function massageUrl(url: string): string {
if (!url.includes('://') && url.includes('/')) {
return 'https://' + url;
} else {
return url;
}
}

function removeUndefinedFields(
obj: Record<string, any>,
): Record<string, string> {
Expand Down
42 changes: 42 additions & 0 deletions lib/config/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,48 @@ describe('config/validation', () => {
]);
});

it('errors if invalid matchHost values in hostRules', async () => {
GlobalConfig.set({ allowedHeaders: ['X-*'] });

const config = {
hostRules: [
{
matchHost: '://',
token: 'token',
},
{
matchHost: '',
token: 'token',
},
{
matchHost: undefined,
token: 'token',
},
{
hostType: 'github',
token: 'token',
},
],
};
const { errors } = await configValidation.validateConfig('repo', config);
expect(errors).toMatchObject([
{
topic: 'Configuration Error',
message:
'Configuration option `hostRules[2].matchHost` should be a string',
},
{
topic: 'Configuration Error',
message:
'Invalid value for hostRules matchHost. It cannot be an empty string.',
},
{
topic: 'Configuration Error',
message: 'hostRules matchHost `://` is not a valid URL.',
},
]);
});

it('errors if forbidden header in hostRules', async () => {
GlobalConfig.set({ allowedHeaders: ['X-*'] });

Expand Down
18 changes: 18 additions & 0 deletions lib/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
matchRegexOrGlobList,
} from '../util/string-match';
import * as template from '../util/template';
import { parseUrl } from '../util/url';
import {
hasValidSchedule,
hasValidTimezone,
Expand Down Expand Up @@ -813,6 +814,23 @@ export async function validateConfig(
? (config.allowedHeaders as string[]) ?? []
: GlobalConfig.get('allowedHeaders', []);
for (const rule of val as HostRule[]) {
if (is.nonEmptyString(rule.matchHost)) {
if (rule.matchHost.includes('://')) {
if (parseUrl(rule.matchHost) === null) {
errors.push({
topic: 'Configuration Error',
message: `hostRules matchHost \`${rule.matchHost}\` is not a valid URL.`,
});
}
}
} else if (is.emptyString(rule.matchHost)) {
errors.push({
topic: 'Configuration Error',
message:
'Invalid value for hostRules matchHost. It cannot be an empty string.',
});
}

if (!rule.headers) {
continue;
}
Expand Down
33 changes: 22 additions & 11 deletions lib/util/host-rules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ describe('util/host-rules', () => {
username: 'user1',
});
});

it('massages host url', () => {
add({
matchHost: 'some.domain.com:8080',
username: 'user1',
password: 'pass1',
});
add({
matchHost: 'domain.com/',
username: 'user2',
password: 'pass2',
});
expect(find({ url: 'https://some.domain.com:8080' })).toEqual({
password: 'pass1',
username: 'user1',
});
expect(find({ url: 'https://domain.com/' })).toEqual({
password: 'pass2',
username: 'user2',
});
});
});

describe('find()', () => {
Expand Down Expand Up @@ -125,7 +146,7 @@ describe('util/host-rules', () => {
});

it('matches on specific path', () => {
// Initialized platform holst rule
// Initialized platform host rule
add({
hostType: 'github',
matchHost: 'https://api.github.com',
Expand Down Expand Up @@ -244,16 +265,6 @@ describe('util/host-rules', () => {
expect(find({ url: 'httpsdomain.com' }).token).toBeUndefined();
});

it('host with port is interpreted as empty', () => {
add({
matchHost: 'domain.com:9118',
token: 'def',
});
expect(find({ url: 'https://domain.com:9118' }).token).toBe('def');
expect(find({ url: 'https://domain.com' }).token).toBe('def');
expect(find({ url: 'httpsdomain.com' }).token).toBe('def');
});

it('matches on hostType and endpoint', () => {
add({
hostType: NugetDatasource.id,
Expand Down
3 changes: 2 additions & 1 deletion lib/util/host-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { CombinedHostRule, HostRule } from '../types';
import { clone } from './clone';
import * as sanitize from './sanitize';
import { toBase64 } from './string';
import { isHttpUrl, parseUrl } from './url';
import { isHttpUrl, massageHostUrl, parseUrl } from './url';

let hostRules: HostRule[] = [];

Expand Down Expand Up @@ -43,6 +43,7 @@ export function add(params: HostRule): void {

const confidentialFields: (keyof HostRule)[] = ['password', 'token'];
if (rule.matchHost) {
rule.matchHost = massageHostUrl(rule.matchHost);
const parsedUrl = parseUrl(rule.matchHost);
rule.resolvedHost = parsedUrl?.hostname ?? rule.matchHost;
confidentialFields.forEach((field) => {
Expand Down
10 changes: 10 additions & 0 deletions lib/util/url.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getQueryString,
isHttpUrl,
joinUrlParts,
massageHostUrl,
parseLinkHeader,
parseUrl,
replaceUrlPath,
Expand Down Expand Up @@ -214,4 +215,13 @@ describe('util/url', () => {
},
});
});

it('massageHostUrl', () => {
expect(massageHostUrl('domain.com')).toBe('domain.com');
expect(massageHostUrl('domain.com:8080')).toBe('https://domain.com:8080');
expect(massageHostUrl('domain.com/some/path')).toBe(
'https://domain.com/some/path',
);
expect(massageHostUrl('https://domain.com')).toBe('https://domain.com');
});
});
13 changes: 13 additions & 0 deletions lib/util/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,16 @@ export function parseLinkHeader(
}
return _parseLinkHeader(linkHeader);
}

/**
* prefix https:// to hosts with port or path
*/
export function massageHostUrl(url: string): string {
if (!url.includes('://') && url.includes('/')) {
return 'https://' + url;
} else if (!url.includes('://') && url.includes(':')) {
return 'https://' + url;
} else {
return url;
}
}

0 comments on commit b8b7607

Please sign in to comment.