Skip to content

Commit b3051b2

Browse files
authored
Merge pull request #159 from mafintosh/safe-regex
Avoid catastrophic backtracking
2 parents 5c6a43d + 767c6c0 commit b3051b2

File tree

3 files changed

+49
-6
lines changed

3 files changed

+49
-6
lines changed

formats.js

+32-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,40 @@
1-
exports['date-time'] = /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}[tT ]\d{2}:\d{2}:\d{2}(\.\d+)?([zZ]|[+-]\d{2}:\d{2})$/
1+
var createIpValidator = require('is-my-ip-valid')
2+
3+
var reEmailWhitespace = /\s/
4+
var reHostnameFirstPass = /^[a-zA-Z0-9.-]+$/
5+
var reHostnamePart = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/
6+
var rePhoneFirstPass = /^\+[0-9][0-9 ]{5,27}[0-9]$/
7+
var rePhoneDoubleSpace = / {2}/
8+
var rePhoneGlobalSpace = / /g
9+
10+
exports['date-time'] = /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}[tT ]\d{2}:\d{2}:\d{2}(?:\.\d+|)([zZ]|[+-]\d{2}:\d{2})$/
211
exports['date'] = /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}$/
312
exports['time'] = /^\d{2}:\d{2}:\d{2}$/
4-
exports['email'] = /^\S+@\S+$/
5-
exports['ip-address'] = exports['ipv4'] = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
6-
exports['ipv6'] = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-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-Fa-f]{1,4}:){5}(((:[0-9A-Fa-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-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-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-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-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-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-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-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-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-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-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}))|:)))(%.+)?\s*$/
13+
exports['email'] = function (input) { return (input.indexOf('@') !== -1) && (!reEmailWhitespace.test(input)) }
14+
exports['ip-address'] = exports['ipv4'] = createIpValidator({ version: 4 })
15+
exports['ipv6'] = createIpValidator({ version: 6 })
716
exports['uri'] = /^[a-zA-Z][a-zA-Z0-9+-.]*:[^\s]*$/
817
exports['color'] = /(#?([0-9A-Fa-f]{3,6})\b)|(aqua)|(black)|(blue)|(fuchsia)|(gray)|(green)|(lime)|(maroon)|(navy)|(olive)|(orange)|(purple)|(red)|(silver)|(teal)|(white)|(yellow)|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\))/
9-
exports['hostname'] = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$/
18+
exports['hostname'] = function (input) {
19+
if (!(reHostnameFirstPass.test(input))) return false
20+
21+
var parts = input.split('.')
22+
23+
for (var i = 0; i < parts.length; i++) {
24+
if (!(reHostnamePart.test(parts[i]))) return false
25+
}
26+
27+
return true
28+
}
1029
exports['alpha'] = /^[a-zA-Z]+$/
1130
exports['alphanumeric'] = /^[a-zA-Z0-9]+$/
1231
exports['style'] = /\s*(.+?):\s*([^;]+);?/g
13-
exports['phone'] = /^\+(?:[0-9] ?){6,14}[0-9]$/
32+
exports['phone'] = function (input) {
33+
if (!(rePhoneFirstPass.test(input))) return false
34+
if (rePhoneDoubleSpace.test(input)) return false
35+
36+
var digits = input.substring(1).replace(rePhoneGlobalSpace, '').length
37+
38+
return (digits >= 7 && digits <= 15)
39+
}
1440
exports['utc-millisec'] = /^[0-9]{1,15}\.?[0-9]{0,15}$/

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
"dependencies": {
77
"generate-function": "^2.0.0",
88
"generate-object-property": "^1.1.0",
9+
"is-my-ip-valid": "^1.0.0",
910
"jsonpointer": "^4.0.0",
1011
"xtend": "^4.0.0"
1112
},
1213
"devDependencies": {
14+
"safe-regex": "^1.1.0",
1315
"tape": "^2.13.4"
1416
},
1517
"scripts": {

test/safe-regex.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var tape = require('tape')
2+
var safeRegex = require('safe-regex')
3+
4+
var formats = require('../formats')
5+
6+
tape('safe-regex', function (t) {
7+
var key
8+
for (key in formats) {
9+
if (formats[key] instanceof RegExp) {
10+
t.ok(safeRegex(formats[key]), key + ' should be a safe regex')
11+
}
12+
}
13+
14+
t.end()
15+
})

0 commit comments

Comments
 (0)