snap-validate 0.4.1 → 0.4.3
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/README.md +76 -51
- package/package.json +12 -6
- package/src/core/BaseValidator.js +594 -0
- package/src/core/ValidationResult.js +19 -0
- package/src/index.js +10 -950
- package/src/schema/runner.js +32 -0
- package/src/schema/validate.js +78 -0
- package/src/utils/safeRegex.js +72 -0
- package/src/validators/alphanumeric.js +9 -0
- package/src/validators/creditCard.js +49 -0
- package/src/validators/email.js +11 -0
- package/src/validators/index.js +19 -0
- package/src/validators/numeric.js +9 -0
- package/src/validators/password.js +41 -0
- package/src/validators/phone.js +24 -0
- package/src/validators/url.js +10 -0
- package/src/validators/zipCode.js +15 -0
- package/types/index.d.ts +85 -7
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snap Validate - Shared schema-runner helpers
|
|
3
|
+
* Extracted from the duplicated bodies of validate / validateAsync.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function assertSchemaAndData(schema, data) {
|
|
7
|
+
if (!schema || typeof schema !== 'object') {
|
|
8
|
+
throw new Error('Schema must be a valid object');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!data || typeof data !== 'object') {
|
|
12
|
+
throw new Error('Data must be a valid object');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function buildResponse(results, isValid) {
|
|
17
|
+
return {
|
|
18
|
+
isValid,
|
|
19
|
+
errors: results,
|
|
20
|
+
getErrors: () => {
|
|
21
|
+
const errors = {};
|
|
22
|
+
for (const [field, result] of Object.entries(results)) {
|
|
23
|
+
if (!result.isValid) {
|
|
24
|
+
errors[field] = result.errors;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return errors;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { assertSchemaAndData, buildResponse };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snap Validate - Schema validation entry points
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { ValidationResult } = require('../core/ValidationResult');
|
|
6
|
+
const { assertSchemaAndData, buildResponse } = require('./runner');
|
|
7
|
+
|
|
8
|
+
// Main validation function
|
|
9
|
+
const validate = (schema, data) => {
|
|
10
|
+
assertSchemaAndData(schema, data);
|
|
11
|
+
|
|
12
|
+
const results = {};
|
|
13
|
+
let isValid = true;
|
|
14
|
+
|
|
15
|
+
for (const [field, validator] of Object.entries(schema)) {
|
|
16
|
+
try {
|
|
17
|
+
const fieldValue = data[field];
|
|
18
|
+
const validatorInstance =
|
|
19
|
+
typeof validator === 'function' ? validator(fieldValue) : validator;
|
|
20
|
+
|
|
21
|
+
// Set field name for better error context
|
|
22
|
+
validatorInstance.setFieldName(field);
|
|
23
|
+
|
|
24
|
+
const result = validatorInstance.validate();
|
|
25
|
+
|
|
26
|
+
results[field] = result;
|
|
27
|
+
if (!result.isValid) {
|
|
28
|
+
isValid = false;
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
results[field] = new ValidationResult(false, [
|
|
32
|
+
`${field}: Validation setup error - ${error.message}`
|
|
33
|
+
]);
|
|
34
|
+
isValid = false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return buildResponse(results, isValid);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Async validation function
|
|
42
|
+
const validateAsync = async (schema, data) => {
|
|
43
|
+
assertSchemaAndData(schema, data);
|
|
44
|
+
|
|
45
|
+
const results = {};
|
|
46
|
+
let isValid = true;
|
|
47
|
+
|
|
48
|
+
for (const [field, validator] of Object.entries(schema)) {
|
|
49
|
+
try {
|
|
50
|
+
const fieldValue = data[field];
|
|
51
|
+
|
|
52
|
+
const validatorInstance =
|
|
53
|
+
typeof validator === 'function' ? validator(fieldValue) : validator;
|
|
54
|
+
|
|
55
|
+
// Set field name for better error context
|
|
56
|
+
validatorInstance.setFieldName(field);
|
|
57
|
+
|
|
58
|
+
const result =
|
|
59
|
+
validatorInstance.asyncRules && validatorInstance.asyncRules.length > 0
|
|
60
|
+
? await validatorInstance.validateAsync()
|
|
61
|
+
: validatorInstance.validate();
|
|
62
|
+
|
|
63
|
+
results[field] = result;
|
|
64
|
+
if (!result.isValid) {
|
|
65
|
+
isValid = false;
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
results[field] = new ValidationResult(false, [
|
|
69
|
+
`${field}: Validation setup error - ${error.message}`
|
|
70
|
+
]);
|
|
71
|
+
isValid = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return buildResponse(results, isValid);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
module.exports = { validate, validateAsync };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snap Validate - Safe regex utilities
|
|
3
|
+
*
|
|
4
|
+
* Honest note on ReDoS protection
|
|
5
|
+
* --------------------------------
|
|
6
|
+
* JavaScript runs regexes synchronously on a single thread, so a timer CANNOT
|
|
7
|
+
* interrupt a regex that is mid-backtrack: the event loop is blocked until
|
|
8
|
+
* regex.test() returns on its own. This module therefore does NOT attempt
|
|
9
|
+
* (fake) timeout-based interruption. The protections that actually work are:
|
|
10
|
+
* 1. an input-length cap, which rejects oversized inputs before matching; and
|
|
11
|
+
* 2. isRegexSafe(), a best-effort STATIC check that flags a few common
|
|
12
|
+
* catastrophic-backtracking shapes.
|
|
13
|
+
* isRegexSafe is a heuristic - it can miss dangerous patterns and can
|
|
14
|
+
* occasionally over-reject safe ones. For guaranteed linear-time matching you
|
|
15
|
+
* need a non-backtracking engine (e.g. the native `re2` module) or a worker /
|
|
16
|
+
* subprocess with a real timeout.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const MAX_INPUT_LENGTH = 10000;
|
|
20
|
+
|
|
21
|
+
// Best-effort STATIC detection of a few catastrophic-backtracking shapes.
|
|
22
|
+
// Heuristic only - see the module note above.
|
|
23
|
+
const isRegexSafe = (regex) => {
|
|
24
|
+
const regexStr = regex.toString();
|
|
25
|
+
|
|
26
|
+
const dangerousPatterns = [
|
|
27
|
+
/\([^)]*[+*?][^)]*\)[+*?]/,
|
|
28
|
+
/\([^)]*\|[^)]*\)[+*]/,
|
|
29
|
+
/\([^)]*\.\*[^)]*\)\*/,
|
|
30
|
+
/[+*?]{2,}/,
|
|
31
|
+
/\([^)]*\|[^)]*\)\+.*\([^)]*\|[^)]*\)\+/
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const isDangerous = dangerousPatterns.some((pattern) =>
|
|
35
|
+
pattern.test(regexStr)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return !isDangerous;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Asynchronous wrapper, kept returning a Promise for API compatibility.
|
|
42
|
+
// Applies the two REAL guards (length cap + static safety check) and then runs
|
|
43
|
+
// the match. It does NOT - and cannot - interrupt a running regex.
|
|
44
|
+
const safeRegexTest = (regex, str) => {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
if (str.length > MAX_INPUT_LENGTH) {
|
|
47
|
+
reject(new Error('Input too long for regex validation'));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!isRegexSafe(regex)) {
|
|
52
|
+
reject(new Error('Unsafe regex pattern detected'));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
resolve(regex.test(str));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
reject(error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Synchronous safe regex test with input-length protection.
|
|
65
|
+
const safeRegexTestSync = (regex, str, maxLength = MAX_INPUT_LENGTH) => {
|
|
66
|
+
if (str.length > maxLength) {
|
|
67
|
+
throw new Error('Input too long for pattern validation');
|
|
68
|
+
}
|
|
69
|
+
return regex.test(str);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
module.exports = { isRegexSafe, safeRegexTest, safeRegexTestSync };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
|
|
3
|
+
const alphanumeric = (value) => {
|
|
4
|
+
return new BaseValidator(value)
|
|
5
|
+
.required('This field is required')
|
|
6
|
+
.pattern(/^[a-zA-Z0-9]+$/, 'Only letters and numbers are allowed');
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
module.exports = { alphanumeric };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
const { safeRegexTestSync } = require('../utils/safeRegex');
|
|
3
|
+
|
|
4
|
+
const luhnCheck = (num) => {
|
|
5
|
+
let sum = 0;
|
|
6
|
+
let isEven = false;
|
|
7
|
+
|
|
8
|
+
const cleanNum = String(num).replace(/\s/g, '');
|
|
9
|
+
|
|
10
|
+
for (let i = cleanNum.length - 1; i >= 0; i--) {
|
|
11
|
+
let digit = parseInt(cleanNum[i], 10);
|
|
12
|
+
|
|
13
|
+
if (isEven) {
|
|
14
|
+
digit *= 2;
|
|
15
|
+
if (digit > 9) digit -= 9;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
sum += digit;
|
|
19
|
+
isEven = !isEven;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return sum % 10 === 0;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const creditCard = (value) => {
|
|
26
|
+
return new BaseValidator(value)
|
|
27
|
+
.required('Credit card number is required')
|
|
28
|
+
.custom((val) => {
|
|
29
|
+
// required() already handles emptiness; skip the digit/Luhn checks
|
|
30
|
+
// for falsy values to avoid emitting a second error.
|
|
31
|
+
if (!val) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cleanValue = String(val).replace(/\s/g, '');
|
|
36
|
+
|
|
37
|
+
if (!safeRegexTestSync(/^\d{13,19}$/, cleanValue)) {
|
|
38
|
+
return 'Credit card must be 13-19 digits';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!luhnCheck(cleanValue)) {
|
|
42
|
+
return 'Invalid credit card number';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
module.exports = { creditCard };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
|
|
3
|
+
const email = (value) => {
|
|
4
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5
|
+
return new BaseValidator(value)
|
|
6
|
+
.transform((v) => (typeof v === 'string' ? v.trim().toLowerCase() : v))
|
|
7
|
+
.required('Email is required')
|
|
8
|
+
.pattern(emailRegex, 'Invalid email format');
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
module.exports = { email };
|
package/src/validators/index.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { email } = require('./email');
|
|
2
|
+
const { phone } = require('./phone');
|
|
3
|
+
const { creditCard } = require('./creditCard');
|
|
4
|
+
const { url } = require('./url');
|
|
5
|
+
const { password } = require('./password');
|
|
6
|
+
const { alphanumeric } = require('./alphanumeric');
|
|
7
|
+
const { numeric } = require('./numeric');
|
|
8
|
+
const { zipCode } = require('./zipCode');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
email,
|
|
12
|
+
phone,
|
|
13
|
+
creditCard,
|
|
14
|
+
url,
|
|
15
|
+
password,
|
|
16
|
+
alphanumeric,
|
|
17
|
+
numeric,
|
|
18
|
+
zipCode
|
|
19
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
|
|
3
|
+
const password = (value, options = {}) => {
|
|
4
|
+
const {
|
|
5
|
+
minLength = 8,
|
|
6
|
+
requireUppercase = true,
|
|
7
|
+
requireLowercase = true,
|
|
8
|
+
requireNumbers = true,
|
|
9
|
+
requireSpecialChars = false
|
|
10
|
+
} = options;
|
|
11
|
+
|
|
12
|
+
const validator = new BaseValidator(value)
|
|
13
|
+
.required('Password is required')
|
|
14
|
+
.min(minLength, `Password must be at least ${minLength} characters`);
|
|
15
|
+
|
|
16
|
+
if (requireUppercase) {
|
|
17
|
+
validator.pattern(
|
|
18
|
+
/[A-Z]/,
|
|
19
|
+
'Password must contain at least one uppercase letter'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
if (requireLowercase) {
|
|
23
|
+
validator.pattern(
|
|
24
|
+
/[a-z]/,
|
|
25
|
+
'Password must contain at least one lowercase letter'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (requireNumbers) {
|
|
29
|
+
validator.pattern(/\d/, 'Password must contain at least one number');
|
|
30
|
+
}
|
|
31
|
+
if (requireSpecialChars) {
|
|
32
|
+
validator.pattern(
|
|
33
|
+
/[!@#$%^&*(),.?":{}|<>]/,
|
|
34
|
+
'Password must contain at least one special character'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return validator;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
module.exports = { password };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
const { safeRegexTestSync } = require('../utils/safeRegex');
|
|
3
|
+
|
|
4
|
+
const phone = (value, format = 'us') => {
|
|
5
|
+
const phoneRegex = {
|
|
6
|
+
us: /^[+]?[1]?[0-9]{10}$/,
|
|
7
|
+
international: /^[+][1-9][0-9]{7,14}$/,
|
|
8
|
+
simple: /^[0-9]{10,15}$/
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return new BaseValidator(value)
|
|
12
|
+
.required('Phone number is required')
|
|
13
|
+
.custom((val) => {
|
|
14
|
+
const cleaned = String(val).replace(/[^+0-9]/g, '');
|
|
15
|
+
const regex = phoneRegex[format] || phoneRegex.simple;
|
|
16
|
+
|
|
17
|
+
if (!safeRegexTestSync(regex, cleaned)) {
|
|
18
|
+
return 'Invalid phone number format';
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = { phone };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
|
|
3
|
+
const url = (value) => {
|
|
4
|
+
const urlRegex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i;
|
|
5
|
+
return new BaseValidator(value)
|
|
6
|
+
.required('URL is required')
|
|
7
|
+
.pattern(urlRegex, 'Invalid URL format');
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
module.exports = { url };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { BaseValidator } = require('../core/BaseValidator');
|
|
2
|
+
|
|
3
|
+
const zipCode = (value, country = 'us') => {
|
|
4
|
+
const zipRegex = {
|
|
5
|
+
us: /^\d{5}(-\d{4})?$/,
|
|
6
|
+
ca: /^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$/,
|
|
7
|
+
uk: /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
return new BaseValidator(value)
|
|
11
|
+
.required('Zip code is required')
|
|
12
|
+
.pattern(zipRegex[country] || zipRegex.us, 'Invalid zip code format');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = { zipCode };
|
package/types/index.d.ts
CHANGED
|
@@ -31,7 +31,8 @@ declare module 'snap-validate' {
|
|
|
31
31
|
export type CountryCode = 'us' | 'ca' | 'uk';
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Custom validation function that returns boolean
|
|
34
|
+
* Custom validation function that returns boolean, an error string, or a
|
|
35
|
+
* ValidationResult
|
|
35
36
|
*/
|
|
36
37
|
export type CustomValidatorFunction = (
|
|
37
38
|
value: any
|
|
@@ -44,6 +45,11 @@ declare module 'snap-validate' {
|
|
|
44
45
|
value: any
|
|
45
46
|
) => Promise<boolean | string | ValidationResult>;
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Value transform/sanitize function used by transform()
|
|
50
|
+
*/
|
|
51
|
+
export type TransformFunction = (value: any) => any;
|
|
52
|
+
|
|
47
53
|
/**
|
|
48
54
|
* Conditional validation condition
|
|
49
55
|
*/
|
|
@@ -63,8 +69,17 @@ declare module 'snap-validate' {
|
|
|
63
69
|
rules: Array<() => ValidationResult>;
|
|
64
70
|
asyncRules: Array<() => Promise<ValidationResult>>;
|
|
65
71
|
isOptional: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* @deprecated No-op. A timer cannot interrupt a synchronous regex on a
|
|
74
|
+
* single thread, so this value is ignored. Retained for compatibility.
|
|
75
|
+
*/
|
|
66
76
|
regexTimeout: number;
|
|
67
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Set the field name used to prefix contextual error messages
|
|
80
|
+
*/
|
|
81
|
+
setFieldName(name: string): BaseValidator;
|
|
82
|
+
|
|
68
83
|
/**
|
|
69
84
|
* Make field required
|
|
70
85
|
*/
|
|
@@ -76,27 +91,82 @@ declare module 'snap-validate' {
|
|
|
76
91
|
optional(): BaseValidator;
|
|
77
92
|
|
|
78
93
|
/**
|
|
79
|
-
*
|
|
94
|
+
* @deprecated No-op, retained for backward compatibility and chainability.
|
|
95
|
+
* Regex execution cannot be interrupted by a timeout on a single thread, so
|
|
96
|
+
* this setting is ignored.
|
|
80
97
|
*/
|
|
81
98
|
setRegexTimeout(timeoutMs: number): BaseValidator;
|
|
82
99
|
|
|
83
100
|
/**
|
|
84
|
-
*
|
|
101
|
+
* Transform/sanitize the value before subsequent rules run
|
|
102
|
+
*/
|
|
103
|
+
transform(fn: TransformFunction, errorMessage?: string): BaseValidator;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Require the value to strictly equal compareValue
|
|
107
|
+
*/
|
|
108
|
+
equals(compareValue: any, message?: string): BaseValidator;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Require the value to be one of the allowed values
|
|
112
|
+
*/
|
|
113
|
+
oneOf(allowedValues: any[], message?: string): BaseValidator;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Require a numeric value between min and max (inclusive)
|
|
117
|
+
*/
|
|
118
|
+
between(min: number, max: number, message?: string): BaseValidator;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Set minimum length (string/array) or minimum value (number)
|
|
85
122
|
*/
|
|
86
123
|
min(length: number, message?: string): BaseValidator;
|
|
87
124
|
|
|
88
125
|
/**
|
|
89
|
-
* Set maximum length/value
|
|
126
|
+
* Set maximum length (string/array) or maximum value (number)
|
|
90
127
|
*/
|
|
91
128
|
max(length: number, message?: string): BaseValidator;
|
|
92
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Require the value to be an array
|
|
132
|
+
*/
|
|
133
|
+
array(message?: string): BaseValidator;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Validate each item of an array (synchronous)
|
|
137
|
+
*/
|
|
138
|
+
arrayOf(
|
|
139
|
+
validator: BaseValidator | ValidationFunction,
|
|
140
|
+
message?: string
|
|
141
|
+
): BaseValidator;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Validate each item of an array (asynchronous)
|
|
145
|
+
*/
|
|
146
|
+
arrayOfAsync(
|
|
147
|
+
validator: BaseValidator | ValidationFunction,
|
|
148
|
+
message?: string
|
|
149
|
+
): BaseValidator;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Validate a nested object against a schema (synchronous)
|
|
153
|
+
*/
|
|
154
|
+
object(schema: Schema, message?: string): BaseValidator;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Validate a nested object against a schema (asynchronous)
|
|
158
|
+
*/
|
|
159
|
+
objectAsync(schema: Schema, message?: string): BaseValidator;
|
|
160
|
+
|
|
93
161
|
/**
|
|
94
162
|
* Validate against regex pattern (synchronous)
|
|
95
163
|
*/
|
|
96
164
|
pattern(regex: RegExp, message?: string): BaseValidator;
|
|
97
165
|
|
|
98
166
|
/**
|
|
99
|
-
* Validate against regex pattern
|
|
167
|
+
* Validate against a regex pattern (asynchronous). Input-length and
|
|
168
|
+
* static-safety guards apply; there is no runtime timeout interruption
|
|
169
|
+
* (impossible for synchronous regex execution).
|
|
100
170
|
*/
|
|
101
171
|
patternAsync(regex: RegExp, message?: string): BaseValidator;
|
|
102
172
|
|
|
@@ -190,7 +260,13 @@ declare module 'snap-validate' {
|
|
|
190
260
|
): Promise<SchemaValidationResult>;
|
|
191
261
|
|
|
192
262
|
/**
|
|
193
|
-
* Safely test regex
|
|
263
|
+
* Safely test a regex asynchronously. Applies an input-length cap and the
|
|
264
|
+
* isRegexSafe static heuristic, then runs the match. Returns a Promise.
|
|
265
|
+
*
|
|
266
|
+
* Note: there is NO runtime timeout interruption - a timer cannot stop a
|
|
267
|
+
* synchronous regex on a single thread.
|
|
268
|
+
*
|
|
269
|
+
* @param timeoutMs @deprecated Ignored; retained for backward compatibility.
|
|
194
270
|
*/
|
|
195
271
|
export function safeRegexTest(
|
|
196
272
|
regex: RegExp,
|
|
@@ -208,7 +284,9 @@ declare module 'snap-validate' {
|
|
|
208
284
|
): boolean;
|
|
209
285
|
|
|
210
286
|
/**
|
|
211
|
-
*
|
|
287
|
+
* Best-effort STATIC heuristic that flags a few common catastrophic-
|
|
288
|
+
* backtracking regex shapes. Not a guarantee: it can miss dangerous patterns
|
|
289
|
+
* and occasionally over-reject safe ones.
|
|
212
290
|
*/
|
|
213
291
|
export function isRegexSafe(regex: RegExp): boolean;
|
|
214
292
|
}
|