Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow ipv6 hostnames for http checks #167

Merged
merged 2 commits into from Dec 29, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 14 additions & 11 deletions src/validation.test.ts
Expand Up @@ -101,17 +101,6 @@ describe('bad targets', () => {
expect(CheckValidation.target(CheckType.HTTP, 'https://suraj/dev')).toEqual('Target must have a valid hostname');
});

it('should reject http targets with ipv6 domains', () => {
[
'https://[2001:0db8:1001:1001:1001:1001:1001:1001]/',
'https://[2001:0db8:1001:1001:1001:1001:1001:1001]:8080/',
'http://[2001:0db8:1001:1001:1001:1001:1001:1001]/',
'http://[2001:0db8:1001:1001:1001:1001:1001:1001]:8080/',
].forEach(() =>
expect(CheckValidation.target(CheckType.HTTP, 'https://hostname/')).toEqual('Target must have a valid hostname')
);
});

it('should reject URLs without schema', () => {
const testcases: string[] = ['example.org'];
testcases.forEach((testcase: string) => {
Expand All @@ -127,6 +116,11 @@ describe('bad targets', () => {
});
});

it('should reject malformed ipv6 https targets', () => {
const url = 'https://[2001:0db8:1001:1001:1001:1001:1001:1001/';
expect(CheckValidation.target(CheckType.HTTP, url)).toBe('Target must be a valid web URL');
});

it('should reject ping targets with invalid hostnames', () => {
const testcases: string[] = ['x.', '.y', 'x=y.org'];
testcases.forEach((testcase: string) => {
Expand Down Expand Up @@ -164,6 +158,15 @@ describe('good targets', () => {
});
});

it('should accept http targets with ipv6 domains', () => {
[
'https://[2001:0db8:1001:1001:1001:1001:1001:1001]/',
'https://[2001:0db8:1001:1001:1001:1001:1001:1001]:8080/',
'http://[2001:0db8:1001:1001:1001:1001:1001:1001]/',
'http://[2001:0db8:1001:1001:1001:1001:1001:1001]:8080/',
].forEach(url => expect(CheckValidation.target(CheckType.HTTP, url)).toBe(undefined));
});

it('should accept URL with IPv4 addresses as HTTP target', () => {
const testcases: string[] = [
'http://1.2.3.4/',
Expand Down
30 changes: 20 additions & 10 deletions src/validation.ts
Expand Up @@ -210,17 +210,27 @@ function validateHttpTarget(target: string): string | undefined {
if (!isValidUrl) {
return 'Target must be a valid web URL';
}
const parsedUrl = new URL(target);
if (!parsedUrl.protocol) {
return 'Target must have a valid protocol';
}
try {
const parsedUrl = new URL(target);
if (!parsedUrl.protocol) {
return 'Target must have a valid protocol';
}

// isWebUri will allow some invalid hostnames, so we need addional validation
const ipv6 = Address6.fromURL(target);
if (ipv6.address) {
return undefined;
}

// isWebUri will allow some invalid hostnames, so we need addional validation
const hostname = new URL(target).hostname;
if (validateHostname(hostname)) {
return 'Target must have a valid hostname';
const hostname = parsedUrl.hostname;
if (validateHostname(hostname)) {
return 'Target must have a valid hostname';
}
return undefined;
} catch (e) {
// The new URL constructor throws on invalid web URLs
return 'Target must be a valid web URL';
}
return undefined;
}

function validateHostname(target: string): string | undefined {
Expand All @@ -229,7 +239,7 @@ function validateHostname(target: string): string | undefined {
const pc = punycode.toASCII(target);
// note that \w matches "_"
const re = new RegExp(/^[a-z0-9]([-a-z0-9]{0,62}[a-z0-9])?(\.[a-z]([-a-z0-9]{0,62}[a-z0-9])?)+$/, 'i');
if (!pc.match(re) && !ipv4.isValid() && !ipv6.isValid()) {
if (!pc.match(re) && !ipv4.valid && !ipv6.valid) {
return 'Target must be a valid hostname';
}

Expand Down