[6.4] Rely on RFC1123 when validating server.host. (#22523)

This commit is contained in:
Aleh Zasypkin 2018-08-30 07:31:30 +02:00 committed by GitHub
parent 63ea918b50
commit d42e2be2ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 70 additions and 3 deletions

View file

@ -1,5 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`#hostname returns error when value is not a valid hostname 1`] = `"value is [host:name] but it must be a valid hostname (see RFC 1123)."`;
exports[`#hostname returns error when value is not a valid hostname 2`] = `"value is [localhost:5601] but it must be a valid hostname (see RFC 1123)."`;
exports[`#hostname returns error when value is not a valid hostname 3`] = `"value is [-] but it must be a valid hostname (see RFC 1123)."`;
exports[`#hostname returns error when value is not a valid hostname 4`] = `"value is [0:?:0:0:0:0:0:1] but it must be a valid hostname (see RFC 1123)."`;
exports[`#hostname returns error when value is not a valid hostname 5`] = `"value is [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] but it must be a valid hostname (see RFC 1123)."`;
exports[`#maxLength returns error when longer string 1`] = `"value is [foo] but it must have a maximum length of [2]."`;
exports[`#minLength returns error when shorter string 1`] = `"value is [foo] but it must have a minimum length of [4]."`;

View file

@ -53,6 +53,39 @@ describe('#maxLength', () => {
});
});
describe('#hostname', () => {
test('returns value for valid hostname as per RFC1123', () => {
const hostNameSchema = schema.string({ hostname: true });
expect(hostNameSchema.validate('www.example.com')).toBe('www.example.com');
expect(hostNameSchema.validate('3domain.local')).toBe('3domain.local');
expect(hostNameSchema.validate('hostname')).toBe('hostname');
expect(hostNameSchema.validate('2387628')).toBe('2387628');
expect(hostNameSchema.validate('::1')).toBe('::1');
expect(hostNameSchema.validate('0:0:0:0:0:0:0:1')).toBe('0:0:0:0:0:0:0:1');
expect(hostNameSchema.validate('xn----ascii-7gg5ei7b1i.xn--90a3a')).toBe(
'xn----ascii-7gg5ei7b1i.xn--90a3a'
);
const hostNameWithMaxAllowedLength = 'a'.repeat(255);
expect(hostNameSchema.validate(hostNameWithMaxAllowedLength)).toBe(
hostNameWithMaxAllowedLength
);
});
test('returns error when value is not a valid hostname', () => {
const hostNameSchema = schema.string({ hostname: true });
expect(() => hostNameSchema.validate('host:name')).toThrowErrorMatchingSnapshot();
expect(() => hostNameSchema.validate('localhost:5601')).toThrowErrorMatchingSnapshot();
expect(() => hostNameSchema.validate('-')).toThrowErrorMatchingSnapshot();
expect(() => hostNameSchema.validate('0:?:0:0:0:0:0:1')).toThrowErrorMatchingSnapshot();
const tooLongHostName = 'a'.repeat(256);
expect(() => hostNameSchema.validate(tooLongHostName)).toThrowErrorMatchingSnapshot();
});
});
describe('#defaultValue', () => {
test('returns default when string is undefined', () => {
expect(schema.string({ defaultValue: 'foo' }).validate(undefined)).toBe('foo');

View file

@ -24,6 +24,7 @@ import { Type, TypeOptions } from './type';
export type StringOptions = TypeOptions<string> & {
minLength?: number;
maxLength?: number;
hostname?: boolean;
};
export class StringType extends Type<string> {
@ -38,6 +39,10 @@ export class StringType extends Type<string> {
schema = schema.max(options.maxLength);
}
if (options.hostname === true) {
schema = schema.hostname();
}
super(schema, options);
}
@ -50,6 +55,8 @@ export class StringType extends Type<string> {
return `value is [${value}] but it must have a minimum length of [${limit}].`;
case 'string.max':
return `value is [${value}] but it must have a maximum length of [${limit}].`;
case 'string.hostname':
return `value is [${value}] but it must be a valid hostname (see RFC 1123).`;
}
}
}

View file

@ -1,5 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`accepts valid hostnames: valid host names 1`] = `
Object {
"host1": "www.example.com",
"host2": "8.8.8.8",
"host3": "::1",
"host4": "localhost",
}
`;
exports[`has defaults for config 1`] = `
Object {
"cors": false,
@ -44,7 +53,7 @@ exports[`throws if basepath is missing prepended slash 1`] = `"[basePath]: must
exports[`throws if basepath is not specified, but rewriteBasePath is set 1`] = `"cannot use [rewriteBasePath] when [basePath] is not specified"`;
exports[`throws if invalid hostname 1`] = `"[host]: must be a valid hostname"`;
exports[`throws if invalid hostname 1`] = `"[host]: value is [asdf$%^] but it must be a valid hostname (see RFC 1123)."`;
exports[`with TLS should accept known protocols\` 1`] = `
"[ssl.supportedProtocols.0]: types that failed validation:

View file

@ -25,6 +25,15 @@ test('has defaults for config', () => {
expect(httpSchema.validate(obj)).toMatchSnapshot();
});
test('accepts valid hostnames', () => {
const { host: host1 } = HttpConfig.schema.validate({ host: 'www.example.com' });
const { host: host2 } = HttpConfig.schema.validate({ host: '8.8.8.8' });
const { host: host3 } = HttpConfig.schema.validate({ host: '::1' });
const { host: host4 } = HttpConfig.schema.validate({ host: 'localhost' });
expect({ host1, host2, host3, host4 }).toMatchSnapshot('valid host names');
});
test('throws if invalid hostname', () => {
const httpSchema = HttpConfig.schema;
const obj = {

View file

@ -21,7 +21,6 @@ import { Env } from '../config';
import { ByteSizeValue, schema, TypeOf } from '../config/schema';
import { SslConfig } from './ssl_config';
const validHostnameRegex = /^(([A-Z0-9]|[A-Z0-9][A-Z0-9\-]*[A-Z0-9])\.)*([A-Z0-9]|[A-Z0-9][A-Z0-9\-]*[A-Z0-9])$/i;
const validBasePathRegex = /(^$|^\/.*[^\/]$)/;
const match = (regex: RegExp, errorMsg: string) => (str: string) =>
@ -51,7 +50,7 @@ const createHttpSchema = schema.object(
),
host: schema.string({
defaultValue: 'localhost',
validate: match(validHostnameRegex, 'must be a valid hostname'),
hostname: true,
}),
maxPayload: schema.byteSize({
defaultValue: '1048576b',