From 583380e8e3c9576c0dc5f6cd80820f10b6a006fe Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Thu, 25 Nov 2021 00:37:16 +0000 Subject: [PATCH] validate hostname, ipv4 and ipv6 formats (#100) --- src/parser/jsonParser.ts | 8 ++++++- src/test/parser.test.ts | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/parser/jsonParser.ts b/src/parser/jsonParser.ts index f4b5f724..1a2edb2b 100644 --- a/src/parser/jsonParser.ts +++ b/src/parser/jsonParser.ts @@ -23,7 +23,10 @@ const formats = { 'date-time': { errorMessage: localize('dateTimeFormatWarning', 'String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i }, 'date': { errorMessage: localize('dateFormatWarning', 'String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i }, 'time': { errorMessage: localize('timeFormatWarning', 'String is not a RFC3339 time.'), pattern: /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i }, - 'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ } + 'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ }, + 'hostname': { errorMessage: localize('hostnameFormatWarning', 'String is not a hostname.'), pattern: /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i }, + 'ipv4': { errorMessage: localize('ipv4FormatWarning', 'String is not an IPv4 address.'), pattern: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ }, + 'ipv6': { errorMessage: localize('ipv6FormatWarning', 'String is not an IPv6 address.'), pattern: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i }, }; export interface IProblem { @@ -683,6 +686,9 @@ function validate(n: ASTNode | undefined, schema: JSONSchema, validationResult: case 'date': case 'time': case 'email': + case 'hostname': + case 'ipv4': + case 'ipv6': const format = formats[schema.format]; if (!node.value || !format.pattern.exec(node.value)) { validationResult.problems.push({ diff --git a/src/test/parser.test.ts b/src/test/parser.test.ts index 6bf05baf..6a3c793d 100644 --- a/src/test/parser.test.ts +++ b/src/test/parser.test.ts @@ -656,6 +656,57 @@ suite('JSON Parser', () => { semanticErrors = validate('{"one":"//foo/bar"}', schemaWithURIReference); assert.strictEqual(semanticErrors!.length, 0, 'uri-reference'); + const schemaWithHostname = { + type: 'object', + properties: { + "hostname": { + type: 'string', + format: 'hostname' + } + } + }; + + semanticErrors = validate('{"hostname":"code.visualstudio.com"}', schemaWithHostname); + assert.strictEqual(semanticErrors!.length, 0, "hostname"); + + semanticErrors = validate('{"hostname":"foo/bar"}', schemaWithHostname); + assert.strictEqual(semanticErrors!.length, 1, "hostname"); + assert.strictEqual(semanticErrors![0].message, 'String is not a hostname.'); + + const schemaWithIPv4 = { + type: 'object', + properties: { + "hostaddr4": { + type: 'string', + format: 'ipv4' + } + } + }; + + semanticErrors = validate('{"hostaddr4":"127.0.0.1"}', schemaWithIPv4); + assert.strictEqual(semanticErrors!.length, 0, "hostaddr4"); + + semanticErrors = validate('{"hostaddr4":"1916:0:0:0:0:F00:1:81AE"}', schemaWithIPv4); + assert.strictEqual(semanticErrors!.length, 1, "hostaddr4"); + assert.strictEqual(semanticErrors![0].message, 'String is not an IPv4 address.'); + + const schemaWithIPv6 = { + type: 'object', + properties: { + "hostaddr6": { + type: 'string', + format: 'ipv6' + } + } + }; + + semanticErrors = validate('{"hostaddr6":"1916:0:0:0:0:F00:1:81AE"}', schemaWithIPv6); + assert.strictEqual(semanticErrors!.length, 0, "hostaddr6"); + + semanticErrors = validate('{"hostaddr6":"127.0.0.1"}', schemaWithIPv6); + assert.strictEqual(semanticErrors!.length, 1, "hostaddr6"); + assert.strictEqual(semanticErrors![0].message, 'String is not an IPv6 address.'); + const schemaWithEMail = { type: 'object', properties: {