@naturalcycles/nodejs-lib 12.45.1 → 12.47.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/stream/pipeline/pipeline.d.ts +1 -1
- package/dist/stream/pipeline/pipeline.js +6 -0
- package/dist/validation/joi/joi.shared.schemas.d.ts +1 -1
- package/dist/validation/joi/joi.shared.schemas.js +3 -6
- package/dist/validation/joi/string.extensions.d.ts +10 -1
- package/dist/validation/joi/string.extensions.js +36 -0
- package/dist/validation/sanitize.util.d.ts +8 -0
- package/dist/validation/sanitize.util.js +13 -0
- package/package.json +3 -1
- package/src/index.ts +3 -0
- package/src/stream/pipeline/pipeline.ts +10 -1
- package/src/validation/joi/joi.shared.schemas.ts +3 -6
- package/src/validation/joi/string.extensions.ts +52 -3
- package/src/validation/sanitize.util.ts +12 -0
- package/CHANGELOG.md +0 -1803
package/dist/index.d.ts
CHANGED
|
@@ -64,5 +64,6 @@ import { AnySchemaTyped, ArraySchemaTyped, BooleanSchemaTyped, NumberSchemaTyped
|
|
|
64
64
|
import { anyObjectSchema, anySchema, arraySchema, oneOfSchema, binarySchema, booleanDefaultToFalseSchema, booleanSchema, dateStringSchema, emailSchema, baseDBEntitySchema, savedDBEntitySchema, idSchema, integerSchema, ipAddressSchema, numberSchema, objectSchema, percentageSchema, semVerSchema, SEM_VER_PATTERN, slugSchema, stringSchema, unixTimestampSchema, urlSchema, userAgentSchema, utcOffsetSchema, verSchema } from './validation/joi/joi.shared.schemas';
|
|
65
65
|
import { JoiValidationError, JoiValidationErrorData } from './validation/joi/joi.validation.error';
|
|
66
66
|
import { convert, getValidationResult, isValid, JoiValidationResult, undefinedIfInvalid, validate } from './validation/joi/joi.validation.util';
|
|
67
|
-
|
|
68
|
-
export {
|
|
67
|
+
import { sanitizeHTML, SanitizeHTMLOptions } from './validation/sanitize.util';
|
|
68
|
+
export type { JoiValidationErrorData, JoiValidationResult, ValidationErrorItem, ExtendedJoi, SchemaTyped, AnySchema, AnySchemaTyped, ArraySchemaTyped, BooleanSchemaTyped, NumberSchemaTyped, ObjectSchemaTyped, StringSchemaTyped, IDebug, IDebugger, SlackServiceCfg, SlackMessage, SlackMessageProps, SlackApiBody, SlackMessagePrefixHook, ReadableTyped, WritableTyped, TransformTyped, PipelineFromNDJsonFileOptions, PipelineToNDJsonFileOptions, TransformJsonParseOptions, TransformToNDJsonOptions, TransformMapOptions, TransformMapSyncOptions, NDJSONStreamForEachOptions, TransformOptions, TransformLogProgressOptions, TransformMultiThreadedOptions, WorkerClassInterface, WorkerInput, WorkerOutput, TableDiffOptions, InspectAnyOptions, Got, GetGotOptions, AfterResponseHook, BeforeErrorHook, BeforeRequestHook, AjvValidationOptions, AjvSchemaCfg, AjvValidationErrorData, SanitizeHTMLOptions, };
|
|
69
|
+
export { JoiValidationError, validate, getValidationResult, isValid, undefinedIfInvalid, convert, Joi, booleanSchema, booleanDefaultToFalseSchema, stringSchema, numberSchema, integerSchema, percentageSchema, dateStringSchema, arraySchema, binarySchema, objectSchema, oneOfSchema, anySchema, anyObjectSchema, baseDBEntitySchema, savedDBEntitySchema, idSchema, unixTimestampSchema, verSchema, emailSchema, SEM_VER_PATTERN, semVerSchema, userAgentSchema, utcOffsetSchema, ipAddressSchema, slugSchema, urlSchema, processSharedUtil, zipBuffer, gzipBuffer, unzipBuffer, gunzipBuffer, zipString, gzipString, unzipToString, gunzipToString, requireEnvKeys, requireFileToExist, LRUMemoCache, stringId, stringIdAsync, stringIdUnsafe, ALPHABET_NUMBER, ALPHABET_LOWERCASE, ALPHABET_UPPERCASE, ALPHABET_ALPHANUMERIC_LOWERCASE, ALPHABET_ALPHANUMERIC_UPPERCASE, ALPHABET_ALPHANUMERIC, md5, hash, hashAsBuffer, md5AsBuffer, stringToBase64, base64ToString, bufferToBase64, base64ToBuffer, Debug, DebugLogLevel, getSecretMap, setSecretMap, loadSecretsFromEnv, loadSecretsFromJsonFile, removeSecretsFromEnv, secret, secretOptional, memoryUsage, memoryUsageFull, SlackService, slackDefaultMessagePrefixHook, readableCreate, readableFrom, readableFromArray, readableToArray, readableForEach, readableForEachSync, readableMap, readableMapToArray, _pipeline, transformBuffer, ndjsonMap, ndJsonFileRead, ndJsonFileWrite, ndjsonStreamForEach, pipelineFromNDJsonFile, pipelineToNDJsonFile, NDJsonStats, streamToNDJsonFile, transformJsonParse, bufferReviver, transformToNDJson, transformFilter, transformFilterSync, transformMap, transformMapSync, transformMapSimple, transformNoOp, writableForEach, writablePushToArray, transformSplit, transformToString, transformToArray, transformTap, transformLogProgress, transformLimit, writableVoid, writableFork, transformMultiThreaded, BaseWorkerClass, tableDiff, inspectAny, getGot, HTTPError, TimeoutError, _chunkBuffer, Ajv, getAjv, AjvSchema, AjvValidationError, readJsonSchemas, readAjvSchemas, hasColors, sanitizeHTML, };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ALPHABET_LOWERCASE = exports.ALPHABET_NUMBER = exports.stringIdUnsafe = exports.stringIdAsync = exports.stringId = exports.LRUMemoCache = exports.requireFileToExist = exports.requireEnvKeys = exports.gunzipToString = exports.unzipToString = exports.gzipString = exports.zipString = exports.gunzipBuffer = exports.unzipBuffer = exports.gzipBuffer = exports.zipBuffer = exports.processSharedUtil = exports.urlSchema = exports.slugSchema = exports.ipAddressSchema = exports.utcOffsetSchema = exports.userAgentSchema = exports.semVerSchema = exports.SEM_VER_PATTERN = exports.emailSchema = exports.verSchema = exports.unixTimestampSchema = exports.idSchema = exports.savedDBEntitySchema = exports.baseDBEntitySchema = exports.anyObjectSchema = exports.anySchema = exports.oneOfSchema = exports.objectSchema = exports.binarySchema = exports.arraySchema = exports.dateStringSchema = exports.percentageSchema = exports.integerSchema = exports.numberSchema = exports.stringSchema = exports.booleanDefaultToFalseSchema = exports.booleanSchema = exports.Joi = exports.convert = exports.undefinedIfInvalid = exports.isValid = exports.getValidationResult = exports.validate = exports.JoiValidationError = void 0;
|
|
4
4
|
exports.transformMapSync = exports.transformMap = exports.transformFilterSync = exports.transformFilter = exports.transformToNDJson = exports.bufferReviver = exports.transformJsonParse = exports.streamToNDJsonFile = exports.NDJsonStats = exports.pipelineToNDJsonFile = exports.pipelineFromNDJsonFile = exports.ndjsonStreamForEach = exports.ndJsonFileWrite = exports.ndJsonFileRead = exports.ndjsonMap = exports.transformBuffer = exports._pipeline = exports.readableMapToArray = exports.readableMap = exports.readableForEachSync = exports.readableForEach = exports.readableToArray = exports.readableFromArray = exports.readableFrom = exports.readableCreate = exports.slackDefaultMessagePrefixHook = exports.SlackService = exports.memoryUsageFull = exports.memoryUsage = exports.secretOptional = exports.secret = exports.removeSecretsFromEnv = exports.loadSecretsFromJsonFile = exports.loadSecretsFromEnv = exports.setSecretMap = exports.getSecretMap = exports.DebugLogLevel = exports.Debug = exports.base64ToBuffer = exports.bufferToBase64 = exports.base64ToString = exports.stringToBase64 = exports.md5AsBuffer = exports.hashAsBuffer = exports.hash = exports.md5 = exports.ALPHABET_ALPHANUMERIC = exports.ALPHABET_ALPHANUMERIC_UPPERCASE = exports.ALPHABET_ALPHANUMERIC_LOWERCASE = exports.ALPHABET_UPPERCASE = void 0;
|
|
5
|
-
exports.hasColors = exports.readAjvSchemas = exports.readJsonSchemas = exports.AjvValidationError = exports.AjvSchema = exports.getAjv = exports.Ajv = exports._chunkBuffer = exports.TimeoutError = exports.HTTPError = exports.getGot = exports.inspectAny = exports.tableDiff = exports.BaseWorkerClass = exports.transformMultiThreaded = exports.writableFork = exports.writableVoid = exports.transformLimit = exports.transformLogProgress = exports.transformTap = exports.transformToArray = exports.transformToString = exports.transformSplit = exports.writablePushToArray = exports.writableForEach = exports.transformNoOp = exports.transformMapSimple = void 0;
|
|
5
|
+
exports.sanitizeHTML = exports.hasColors = exports.readAjvSchemas = exports.readJsonSchemas = exports.AjvValidationError = exports.AjvSchema = exports.getAjv = exports.Ajv = exports._chunkBuffer = exports.TimeoutError = exports.HTTPError = exports.getGot = exports.inspectAny = exports.tableDiff = exports.BaseWorkerClass = exports.transformMultiThreaded = exports.writableFork = exports.writableVoid = exports.transformLimit = exports.transformLogProgress = exports.transformTap = exports.transformToArray = exports.transformToString = exports.transformSplit = exports.writablePushToArray = exports.writableForEach = exports.transformNoOp = exports.transformMapSimple = void 0;
|
|
6
6
|
const ajv_1 = require("ajv");
|
|
7
7
|
exports.Ajv = ajv_1.default;
|
|
8
8
|
const got_1 = require("got");
|
|
@@ -189,3 +189,5 @@ Object.defineProperty(exports, "getValidationResult", { enumerable: true, get: f
|
|
|
189
189
|
Object.defineProperty(exports, "isValid", { enumerable: true, get: function () { return joi_validation_util_1.isValid; } });
|
|
190
190
|
Object.defineProperty(exports, "undefinedIfInvalid", { enumerable: true, get: function () { return joi_validation_util_1.undefinedIfInvalid; } });
|
|
191
191
|
Object.defineProperty(exports, "validate", { enumerable: true, get: function () { return joi_validation_util_1.validate; } });
|
|
192
|
+
const sanitize_util_1 = require("./validation/sanitize.util");
|
|
193
|
+
Object.defineProperty(exports, "sanitizeHTML", { enumerable: true, get: function () { return sanitize_util_1.sanitizeHTML; } });
|
|
@@ -7,3 +7,9 @@ const util_1 = require("util");
|
|
|
7
7
|
* Promisified stream.pipeline()
|
|
8
8
|
*/
|
|
9
9
|
exports._pipeline = (0, util_1.promisify)(stream_1.pipeline);
|
|
10
|
+
// Workaround https://github.com/nodejs/node/issues/40191
|
|
11
|
+
// todo: remove it when fix is released in 16.x and in AppEngine 16.x
|
|
12
|
+
if (process.version > 'v16.13') {
|
|
13
|
+
const { pipeline } = require('stream/promises');
|
|
14
|
+
exports._pipeline = ((streams) => pipeline(...streams));
|
|
15
|
+
}
|
|
@@ -17,7 +17,7 @@ export declare function oneOfSchema<T = any>(...schemas: AnySchemaTyped<any>[]):
|
|
|
17
17
|
export declare const anySchema: import("joi").AnySchema;
|
|
18
18
|
export declare const anyObjectSchema: import("joi").ObjectSchema<any>;
|
|
19
19
|
/**
|
|
20
|
-
* [a-
|
|
20
|
+
* [a-zA-Z0-9_]*
|
|
21
21
|
* 6-64 length
|
|
22
22
|
*/
|
|
23
23
|
export declare const idSchema: import("./string.extensions").ExtendedStringSchema;
|
|
@@ -28,13 +28,10 @@ exports.anySchema = joi_extensions_1.Joi.any();
|
|
|
28
28
|
exports.anyObjectSchema = joi_extensions_1.Joi.object().options({ stripUnknown: false });
|
|
29
29
|
// 1g498efj5sder3324zer
|
|
30
30
|
/**
|
|
31
|
-
* [a-
|
|
31
|
+
* [a-zA-Z0-9_]*
|
|
32
32
|
* 6-64 length
|
|
33
33
|
*/
|
|
34
|
-
exports.idSchema = exports.stringSchema
|
|
35
|
-
.regex(/^[a-z0-9_]*$/)
|
|
36
|
-
.min(6)
|
|
37
|
-
.max(64);
|
|
34
|
+
exports.idSchema = exports.stringSchema.regex(/^[a-zA-Z0-9_]{6,64}$/);
|
|
38
35
|
/**
|
|
39
36
|
* `_` should NOT be allowed to be able to use slug-ids as part of natural ids with `_` separator.
|
|
40
37
|
*/
|
|
@@ -42,7 +39,7 @@ exports.SLUG_PATTERN = /^[a-z0-9-]*$/;
|
|
|
42
39
|
/**
|
|
43
40
|
* "Slug" - a valid URL, filename, etc.
|
|
44
41
|
*/
|
|
45
|
-
exports.slugSchema = exports.stringSchema.regex(
|
|
42
|
+
exports.slugSchema = exports.stringSchema.regex(/^[a-z0-9-]{1,255}$/);
|
|
46
43
|
// 16725225600 is 2500-01-01
|
|
47
44
|
exports.unixTimestampSchema = exports.numberSchema.integer().min(0).max(16725225600);
|
|
48
45
|
// 2
|
|
@@ -3,9 +3,18 @@ import * as Joi from 'joi';
|
|
|
3
3
|
import { AnySchemaTyped } from './joi.model';
|
|
4
4
|
export interface ExtendedStringSchema extends StringSchema, AnySchemaTyped<string> {
|
|
5
5
|
dateString(min?: string, max?: string): this;
|
|
6
|
+
stripHTML(opt?: JoiStripHTMLOptions): this;
|
|
6
7
|
}
|
|
7
|
-
export interface
|
|
8
|
+
export interface JoiDateStringOptions {
|
|
8
9
|
min?: string;
|
|
9
10
|
max?: string;
|
|
10
11
|
}
|
|
12
|
+
export interface JoiStripHTMLOptions {
|
|
13
|
+
/**
|
|
14
|
+
* 'Strict' would throw an error if it detects any HTML.
|
|
15
|
+
* Non-strict (default) does not error, but DOES convert the string to the string without HTML.
|
|
16
|
+
* Internally uses `sanitize-html` library, with allowedTags = [], and method = 'discard'.
|
|
17
|
+
*/
|
|
18
|
+
strict?: boolean;
|
|
19
|
+
}
|
|
11
20
|
export declare function stringExtensions(joi: typeof Joi): Extension;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.stringExtensions = void 0;
|
|
4
4
|
const time_lib_1 = require("@naturalcycles/time-lib");
|
|
5
|
+
const sanitize = require("sanitize-html");
|
|
5
6
|
function stringExtensions(joi) {
|
|
6
7
|
return {
|
|
7
8
|
type: 'string',
|
|
@@ -11,6 +12,7 @@ function stringExtensions(joi) {
|
|
|
11
12
|
'string.dateStringMin': '"{{#label}}" must be not earlier than {{#min}}',
|
|
12
13
|
'string.dateStringMax': '"{{#label}}" must be not later than {{#max}}',
|
|
13
14
|
'string.dateStringCalendarAccuracy': '"{{#label}}" must be a VALID calendar date',
|
|
15
|
+
'string.stripHTML': '"{{#label}}" must NOT contain any HTML tags',
|
|
14
16
|
},
|
|
15
17
|
rules: {
|
|
16
18
|
dateString: {
|
|
@@ -57,6 +59,7 @@ function stringExtensions(joi) {
|
|
|
57
59
|
err = 'string.dateStringMax';
|
|
58
60
|
}
|
|
59
61
|
else if (!(0, time_lib_1.dayjs)(v).isValid()) {
|
|
62
|
+
// todo: replace with another regex (from ajv-validators) for speed
|
|
60
63
|
err = 'string.dateStringCalendarAccuracy';
|
|
61
64
|
}
|
|
62
65
|
if (err) {
|
|
@@ -65,6 +68,39 @@ function stringExtensions(joi) {
|
|
|
65
68
|
return v; // validation passed
|
|
66
69
|
},
|
|
67
70
|
},
|
|
71
|
+
stripHTML: {
|
|
72
|
+
method(opt) {
|
|
73
|
+
return this.$_addRule({
|
|
74
|
+
name: 'stripHTML',
|
|
75
|
+
args: {
|
|
76
|
+
strict: false,
|
|
77
|
+
...opt,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
args: [
|
|
82
|
+
{
|
|
83
|
+
name: 'strict',
|
|
84
|
+
ref: true,
|
|
85
|
+
assert: v => typeof v === 'boolean',
|
|
86
|
+
message: 'must be a boolean',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
validate(v, helpers, args) {
|
|
90
|
+
// console.log('!!! stripHTML', args, v)
|
|
91
|
+
const r = sanitize(v, {
|
|
92
|
+
allowedTags: [],
|
|
93
|
+
// disallowedTagsMode: 'discard' // discard is default
|
|
94
|
+
parser: {
|
|
95
|
+
decodeEntities: false, // prevent decoding/changing of &<>"'
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
if (args.strict && r !== v) {
|
|
99
|
+
return helpers.error('string.stripHTML', args);
|
|
100
|
+
}
|
|
101
|
+
return r; // return converted value (or the same, if there was nothing to sanitize)
|
|
102
|
+
},
|
|
103
|
+
},
|
|
68
104
|
},
|
|
69
105
|
};
|
|
70
106
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as sanitize from 'sanitize-html';
|
|
2
|
+
export declare type SanitizeHTMLOptions = sanitize.IOptions;
|
|
3
|
+
/**
|
|
4
|
+
* Simply a wrapper around `sanitize-html` library.
|
|
5
|
+
*
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
8
|
+
export declare function sanitizeHTML(s: string, opt?: SanitizeHTMLOptions): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeHTML = void 0;
|
|
4
|
+
const sanitize = require("sanitize-html");
|
|
5
|
+
/**
|
|
6
|
+
* Simply a wrapper around `sanitize-html` library.
|
|
7
|
+
*
|
|
8
|
+
* @experimental
|
|
9
|
+
*/
|
|
10
|
+
function sanitizeHTML(s, opt) {
|
|
11
|
+
return sanitize(s, opt);
|
|
12
|
+
}
|
|
13
|
+
exports.sanitizeHTML = sanitizeHTML;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/nodejs-lib",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.47.2",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"prepare": "husky install",
|
|
6
6
|
"docs-serve": "vuepress dev docs",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"@naturalcycles/js-lib": "^14.0.0",
|
|
18
18
|
"@naturalcycles/time-lib": "^3.0.1",
|
|
19
19
|
"@types/lru-cache": "^5.1.0",
|
|
20
|
+
"@types/sanitize-html": "^2.5.0",
|
|
20
21
|
"@types/through2-concurrent": "^2.0.0",
|
|
21
22
|
"ajv": "^8.6.2",
|
|
22
23
|
"ajv-formats": "^2.1.0",
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"lru-cache": "^6.0.0",
|
|
36
37
|
"move-file": "^2.0.0",
|
|
37
38
|
"nanoid": "^3.0.0",
|
|
39
|
+
"sanitize-html": "^2.5.2",
|
|
38
40
|
"supports-color": "^8.0.0",
|
|
39
41
|
"through2-concurrent": "^2.0.0",
|
|
40
42
|
"yargs": "^17.0.0"
|
package/src/index.ts
CHANGED
|
@@ -172,6 +172,7 @@ import {
|
|
|
172
172
|
undefinedIfInvalid,
|
|
173
173
|
validate,
|
|
174
174
|
} from './validation/joi/joi.validation.util'
|
|
175
|
+
import { sanitizeHTML, SanitizeHTMLOptions } from './validation/sanitize.util'
|
|
175
176
|
|
|
176
177
|
export type {
|
|
177
178
|
JoiValidationErrorData,
|
|
@@ -219,6 +220,7 @@ export type {
|
|
|
219
220
|
AjvValidationOptions,
|
|
220
221
|
AjvSchemaCfg,
|
|
221
222
|
AjvValidationErrorData,
|
|
223
|
+
SanitizeHTMLOptions,
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
export {
|
|
@@ -349,4 +351,5 @@ export {
|
|
|
349
351
|
readJsonSchemas,
|
|
350
352
|
readAjvSchemas,
|
|
351
353
|
hasColors,
|
|
354
|
+
sanitizeHTML,
|
|
352
355
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { pipeline } from 'stream'
|
|
2
2
|
import { promisify } from 'util'
|
|
3
3
|
|
|
4
|
+
type AnyStream = NodeJS.ReadableStream | NodeJS.WritableStream | NodeJS.ReadWriteStream
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Promisified stream.pipeline()
|
|
6
8
|
*/
|
|
7
|
-
export
|
|
9
|
+
export let _pipeline = promisify(pipeline)
|
|
10
|
+
|
|
11
|
+
// Workaround https://github.com/nodejs/node/issues/40191
|
|
12
|
+
// todo: remove it when fix is released in 16.x and in AppEngine 16.x
|
|
13
|
+
if (process.version > 'v16.13') {
|
|
14
|
+
const { pipeline } = require('stream/promises')
|
|
15
|
+
_pipeline = ((streams: AnyStream[]) => pipeline(...streams)) as any
|
|
16
|
+
}
|
|
@@ -42,13 +42,10 @@ export const anyObjectSchema = Joi.object().options({ stripUnknown: false })
|
|
|
42
42
|
|
|
43
43
|
// 1g498efj5sder3324zer
|
|
44
44
|
/**
|
|
45
|
-
* [a-
|
|
45
|
+
* [a-zA-Z0-9_]*
|
|
46
46
|
* 6-64 length
|
|
47
47
|
*/
|
|
48
|
-
export const idSchema = stringSchema
|
|
49
|
-
.regex(/^[a-z0-9_]*$/)
|
|
50
|
-
.min(6)
|
|
51
|
-
.max(64)
|
|
48
|
+
export const idSchema = stringSchema.regex(/^[a-zA-Z0-9_]{6,64}$/)
|
|
52
49
|
|
|
53
50
|
/**
|
|
54
51
|
* `_` should NOT be allowed to be able to use slug-ids as part of natural ids with `_` separator.
|
|
@@ -58,7 +55,7 @@ export const SLUG_PATTERN = /^[a-z0-9-]*$/
|
|
|
58
55
|
/**
|
|
59
56
|
* "Slug" - a valid URL, filename, etc.
|
|
60
57
|
*/
|
|
61
|
-
export const slugSchema = stringSchema.regex(
|
|
58
|
+
export const slugSchema = stringSchema.regex(/^[a-z0-9-]{1,255}$/)
|
|
62
59
|
|
|
63
60
|
// 16725225600 is 2500-01-01
|
|
64
61
|
export const unixTimestampSchema = numberSchema.integer().min(0).max(16725225600)
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { dayjs } from '@naturalcycles/time-lib'
|
|
2
2
|
import { Extension, StringSchema } from 'joi'
|
|
3
3
|
import * as Joi from 'joi'
|
|
4
|
+
import * as sanitize from 'sanitize-html'
|
|
4
5
|
import { AnySchemaTyped } from './joi.model'
|
|
5
6
|
|
|
6
7
|
export interface ExtendedStringSchema extends StringSchema, AnySchemaTyped<string> {
|
|
7
8
|
dateString(min?: string, max?: string): this
|
|
9
|
+
stripHTML(opt?: JoiStripHTMLOptions): this
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
export interface
|
|
12
|
+
export interface JoiDateStringOptions {
|
|
11
13
|
min?: string
|
|
12
14
|
max?: string
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
export interface JoiStripHTMLOptions {
|
|
18
|
+
/**
|
|
19
|
+
* 'Strict' would throw an error if it detects any HTML.
|
|
20
|
+
* Non-strict (default) does not error, but DOES convert the string to the string without HTML.
|
|
21
|
+
* Internally uses `sanitize-html` library, with allowedTags = [], and method = 'discard'.
|
|
22
|
+
*/
|
|
23
|
+
strict?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
export function stringExtensions(joi: typeof Joi): Extension {
|
|
16
27
|
return {
|
|
17
28
|
type: 'string',
|
|
@@ -21,13 +32,14 @@ export function stringExtensions(joi: typeof Joi): Extension {
|
|
|
21
32
|
'string.dateStringMin': '"{{#label}}" must be not earlier than {{#min}}',
|
|
22
33
|
'string.dateStringMax': '"{{#label}}" must be not later than {{#max}}',
|
|
23
34
|
'string.dateStringCalendarAccuracy': '"{{#label}}" must be a VALID calendar date',
|
|
35
|
+
'string.stripHTML': '"{{#label}}" must NOT contain any HTML tags',
|
|
24
36
|
},
|
|
25
37
|
rules: {
|
|
26
38
|
dateString: {
|
|
27
39
|
method(min?: string, max?: string) {
|
|
28
40
|
return this.$_addRule({
|
|
29
41
|
name: 'dateString',
|
|
30
|
-
args: { min, max } as
|
|
42
|
+
args: { min, max } as JoiDateStringOptions,
|
|
31
43
|
})
|
|
32
44
|
},
|
|
33
45
|
args: [
|
|
@@ -44,7 +56,7 @@ export function stringExtensions(joi: typeof Joi): Extension {
|
|
|
44
56
|
message: 'must be a string',
|
|
45
57
|
},
|
|
46
58
|
],
|
|
47
|
-
validate(v: string, helpers, args:
|
|
59
|
+
validate(v: string, helpers, args: JoiDateStringOptions) {
|
|
48
60
|
// console.log('dateString validate called', {v, args})
|
|
49
61
|
|
|
50
62
|
let err: string | undefined
|
|
@@ -67,6 +79,7 @@ export function stringExtensions(joi: typeof Joi): Extension {
|
|
|
67
79
|
} else if (max && v > max) {
|
|
68
80
|
err = 'string.dateStringMax'
|
|
69
81
|
} else if (!dayjs(v).isValid()) {
|
|
82
|
+
// todo: replace with another regex (from ajv-validators) for speed
|
|
70
83
|
err = 'string.dateStringCalendarAccuracy'
|
|
71
84
|
}
|
|
72
85
|
|
|
@@ -77,6 +90,42 @@ export function stringExtensions(joi: typeof Joi): Extension {
|
|
|
77
90
|
return v // validation passed
|
|
78
91
|
},
|
|
79
92
|
},
|
|
93
|
+
stripHTML: {
|
|
94
|
+
method(opt?: JoiStripHTMLOptions) {
|
|
95
|
+
return this.$_addRule({
|
|
96
|
+
name: 'stripHTML',
|
|
97
|
+
args: {
|
|
98
|
+
strict: false,
|
|
99
|
+
...opt,
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
args: [
|
|
104
|
+
{
|
|
105
|
+
name: 'strict',
|
|
106
|
+
ref: true,
|
|
107
|
+
assert: v => typeof v === 'boolean',
|
|
108
|
+
message: 'must be a boolean',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
validate(v: string, helpers, args: JoiStripHTMLOptions) {
|
|
112
|
+
// console.log('!!! stripHTML', args, v)
|
|
113
|
+
|
|
114
|
+
const r = sanitize(v, {
|
|
115
|
+
allowedTags: [], // no html tags allowed at all
|
|
116
|
+
// disallowedTagsMode: 'discard' // discard is default
|
|
117
|
+
parser: {
|
|
118
|
+
decodeEntities: false, // prevent decoding/changing of &<>"'
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
if (args.strict && r !== v) {
|
|
123
|
+
return helpers.error('string.stripHTML', args)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return r // return converted value (or the same, if there was nothing to sanitize)
|
|
127
|
+
},
|
|
128
|
+
},
|
|
80
129
|
},
|
|
81
130
|
}
|
|
82
131
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as sanitize from 'sanitize-html'
|
|
2
|
+
|
|
3
|
+
export type SanitizeHTMLOptions = sanitize.IOptions
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Simply a wrapper around `sanitize-html` library.
|
|
7
|
+
*
|
|
8
|
+
* @experimental
|
|
9
|
+
*/
|
|
10
|
+
export function sanitizeHTML(s: string, opt?: SanitizeHTMLOptions): string {
|
|
11
|
+
return sanitize(s, opt)
|
|
12
|
+
}
|