From b946d609c25dfec27a921a4e768b0dc381db2d0d Mon Sep 17 00:00:00 2001 From: Dave Houlbrooke Date: Sun, 16 Dec 2018 01:34:23 +0000 Subject: [PATCH] feat: Add path, relative, absolute checkers for validating filesystem paths --- README.md | 3 +++ lib/checkers/checkers.js | 24 ++++++++++++++++++------ test/checkers/checkers.test.js | 10 ++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 441cfaa..fec4c6c 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,9 @@ This section lists all types that are available in Blork. Types are strings made | `kebab`, `slug` | kebab-case strings e.g. URL slugs (non-empty alphanumeric lowercase) | `train` | Train-Case strings e.g. HTTP-Headers (non-empty with uppercase first letters) | `identifier` | JavaScript identifier names (string starting with **_**, **$**, or letter) +| `path` | Valid filesystem path (e.g. "abc/def") +| `absolute`, `abs` | Valid absolute path (e.g. "/abc/def" or "C:\abd\def") +| `relative`, `rel` | Valid relative path (e.g. "../abc/def" or "..\abd\def") | `function`, `func` | Functions (using **instanceof Function**) | `object`, `obj` | Plain objects (using **typeof && !null** and constructor check) | `objectlike` | Any object (using **typeof && !null**) diff --git a/lib/checkers/checkers.js b/lib/checkers/checkers.js index 9d56205..7dda008 100644 --- a/lib/checkers/checkers.js +++ b/lib/checkers/checkers.js @@ -9,13 +9,14 @@ const R_ALPHABETIC = /^[a-zA-Z]+$/; const R_NUMERIC = /^[0-9]+$/; const R_UPPER = /^[A-Z]+$/; const R_LOWER = /^[a-z]+$/; -const R_CAMEL = /^[a-z][a-zA-Z0-9]*$/; -const R_PASCAL = /^[A-Z][a-zA-Z0-9]*$/; -const R_SNAKE = /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/; -const R_SCREAMING = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/; -const R_KEBAB = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/; -const R_TRAIN = /^[A-Z][a-zA-Z0-9]*(-[A-Z0-9][a-zA-Z0-9]+)*$/; +const R_CAMEL = /^[a-z][a-zA-Z0-9]*$/; // e.g. myVarName +const R_PASCAL = /^[A-Z][a-zA-Z0-9]*$/; // e.g. MyVarName +const R_SNAKE = /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/; // e.g. my_var_name +const R_SCREAMING = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/; // e.g. MY_VAR_NAME +const R_KEBAB = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/; // e.g. my-var-name +const R_TRAIN = /^[A-Z][a-zA-Z0-9]*(-[A-Z0-9][a-zA-Z0-9]+)*$/; // e.g. My-Var-Name const R_IDENTIFIER = /^[$a-zA-Z_][a-zA-Z0-9_$]*$/; +const R_ABSOLUTE = /^(?:\/|[a-zA-Z]:\\|[/\\]{2}[^/\\]+[/\\/]+[^/\\]+)()/; // e.g. "/" (Unix) "C:\" (Windows) or "//server/file" or "\\server\file" (UNC) // Checkers. function isNull(v) { return v === null; } @@ -54,6 +55,9 @@ function isScreaming(v) { return typeof v === "string" && R_SCREAMING.test(v); } function isKebab(v) { return typeof v === "string" && R_KEBAB.test(v); } function isTrain(v) { return typeof v === "string" && R_TRAIN.test(v); } function isIdentifier(v) { return typeof v === "string" && R_IDENTIFIER.test(v); } +function isPath(v) { return typeof v === "string" && v.length > 0 && v.length <= 260 && v.indexOf(String.fromCharCode(0)) === -1; } // Total length is no more than 260 (Windows), doesn't contain NUL (Windows and Unix). +function isAbsolute(v) { return isPath(v) && R_ABSOLUTE.test(v); } +function isRelative(v) { return isPath(v) && !R_ABSOLUTE.test(v); } function isPrimitive(v) { return v === undefined || v === null || typeof v === "boolean" || typeof v === "string" || Number.isFinite(v); } function isFunction(v) { return typeof v === "function"; } function isObject(v) { return typeof v === "object" && v !== null; } @@ -117,6 +121,9 @@ isScreaming.desc = "SCREAMING_SNAKE_CASE string"; isKebab.desc = "kebab-case string"; isTrain.desc = "Camel-Kebab-Case string"; isIdentifier.desc = "valid JavaScript identifier"; +isPath.desc = "valid path"; +isAbsolute.desc = "absolute path"; +isRelative.desc = "relative path"; isFunction.desc = "function"; isObject.desc = "object"; isPlainObject.desc = "plain object"; @@ -206,6 +213,11 @@ const checkers = { "slug": isKebab, "train": isTrain, "identifier": isIdentifier, + "path": isPath, + "abs": isAbsolute, + "absolute": isAbsolute, + "rel": isRelative, + "relative": isRelative, // Objects. "function": isFunction, diff --git a/test/checkers/checkers.test.js b/test/checkers/checkers.test.js index bc4173b..b20686b 100644 --- a/test/checkers/checkers.test.js +++ b/test/checkers/checkers.test.js @@ -77,6 +77,11 @@ describe("checkers", () => { expect(mockCheck("my-var", "slug")).toBe(undefined); expect(mockCheck("My-Var", "train")).toBe(undefined); expect(mockCheck("$name", "identifier")).toBe(undefined); + expect(mockCheck("abc/def", "path")).toBe(undefined); + expect(mockCheck("..\\abc\\def", "rel")).toBe(undefined); + expect(mockCheck("../abc/def", "relative")).toBe(undefined); + expect(mockCheck("/abc/def", "abs")).toBe(undefined); + expect(mockCheck("C:\\abc\\def", "absolute")).toBe(undefined); // Objects. expect(mockCheck(function() {}, "function")).toBe(undefined); @@ -195,6 +200,11 @@ describe("checkers", () => { expect(() => mockCheck("my-VAR", "slug")).toThrow(TypeError); expect(() => mockCheck("my-var", "train")).toThrow(TypeError); expect(() => mockCheck("*name", "identifier")).toThrow(TypeError); + expect(() => mockCheck(String.fromCharCode(0), "path")).toThrow(TypeError); + expect(() => mockCheck("/abc/def", "rel")).toThrow(TypeError); + expect(() => mockCheck("C:\\abc\\def", "relative")).toThrow(TypeError); + expect(() => mockCheck("../abc/def", "abs")).toThrow(TypeError); + expect(() => mockCheck("..\\abc\\def", "absolute")).toThrow(TypeError); // Objects. expect(() => mockCheck({}, "function")).toThrow(TypeError);