Comparing version
@@ -5,20 +5,4 @@ # v2 | ||
### v2.0.2 | ||
## v2.0.0 | ||
1. Update regex-tree to address transitive dep DoS vector | ||
2. Update tape to address DoS vector | ||
Contributors: | ||
- [teppeis](https://github.com/teppeis) | ||
- [davisjam](https://github.com/davisjam) | ||
### v2.0.1 | ||
1. Fix parsing bug introduced during switch to regexp-tree. | ||
Contributors: | ||
- [davisjam](https://github.com/davisjam) | ||
### v2.0.0 | ||
1. Update README. | ||
@@ -25,0 +9,0 @@ 2. Switch AST library from ret to regexp-tree. |
91
index.js
@@ -1,22 +0,26 @@ | ||
const regexpTree = require('regexp-tree'); | ||
const analyzer = require('./lib/analyzer'); | ||
const analyzerFamily = require('./lib/analyzer-family'); | ||
module.exports = function (re, opts) { | ||
if (!opts) opts = {}; | ||
const replimit = opts.limit === undefined ? 25 : opts.limit; | ||
const DEFAULT_SAFE_REP_LIMIT = 25; | ||
const RET_IS_SAFE = true; | ||
const RET_IS_VULNERABLE = false; | ||
// Build an AST | ||
let myRegExp = null; | ||
let ast = null; | ||
class Args { | ||
constructor(regExp, analyzerOptions) { | ||
this.regExp = regExp; | ||
this.analyzerOptions = analyzerOptions; | ||
} | ||
} | ||
function safeRegex(re, opts) { | ||
try { | ||
// Construct a RegExp object | ||
if (re instanceof RegExp) { | ||
myRegExp = re; | ||
} else if (typeof re === 'string') { | ||
myRegExp = new RegExp(re); | ||
const args = buildArgs(re, opts); | ||
const analyzerResponses = askAnalyzersIfVulnerable(args); | ||
// Did any analyzer say true? | ||
if (analyzerResponses.find((isVulnerable) => isVulnerable)) { | ||
return RET_IS_VULNERABLE; | ||
} else { | ||
myRegExp = new RegExp(String(re)); | ||
return RET_IS_SAFE; | ||
} | ||
// Build an AST | ||
ast = regexpTree.parse(myRegExp); | ||
} catch (err) { | ||
@@ -26,26 +30,45 @@ // Invalid or unparseable input | ||
} | ||
} | ||
let currentStarHeight = 0; | ||
let maxObservedStarHeight = 0; | ||
function buildArgs(re, opts) { | ||
// Build AnalyzerOptions | ||
if (!opts) opts = {}; | ||
const heuristic_replimit = opts.limit === undefined ? DEFAULT_SAFE_REP_LIMIT : opts.limit; | ||
let repetitionCount = 0; | ||
const analyzerOptions = new analyzer.AnalyzerOptions(heuristic_replimit); | ||
regexpTree.traverse(ast, { | ||
'Repetition': { | ||
pre ({node}) { | ||
repetitionCount++; | ||
// Build RegExp | ||
let regExp = null; | ||
// Construct a RegExp object | ||
if (re instanceof RegExp) { | ||
regExp = re; | ||
} else if (typeof re === 'string') { | ||
regExp = new RegExp(re); | ||
} else { | ||
regExp = new RegExp(String(re)); | ||
} | ||
currentStarHeight++; | ||
if (maxObservedStarHeight < currentStarHeight) { | ||
maxObservedStarHeight = currentStarHeight; | ||
} | ||
}, | ||
return new Args(regExp, analyzerOptions); | ||
} | ||
post ({node}) { | ||
currentStarHeight--; | ||
} | ||
function askAnalyzersIfVulnerable(args) { | ||
let analyzerSaysVulnerable = []; | ||
// Query the Analyzers | ||
let Analyzer; | ||
for (Analyzer of analyzerFamily) { | ||
try { | ||
const analyzer = new Analyzer(args.analyzerOptions); | ||
analyzerSaysVulnerable.push(analyzer.isVulnerable(args.regExp)); | ||
} catch (err) { | ||
/* istanbul ignore next */ // No need to worry about code coverage here. | ||
analyzerSaysVulnerable.push(false); | ||
} | ||
}); | ||
} | ||
return (maxObservedStarHeight <= 1) && (repetitionCount <= replimit); | ||
}; | ||
return analyzerSaysVulnerable; | ||
} | ||
// Export | ||
module.exports = safeRegex; |
{ | ||
"name": "safe-regex", | ||
"version": "2.0.2", | ||
"version": "2.1.0", | ||
"description": "detect possibly catastrophic, exponential-time regular expressions", | ||
@@ -10,18 +10,29 @@ "main": "index.js", | ||
"devDependencies": { | ||
"tape": "^4.10.1" | ||
"jest": "^24.9.0" | ||
}, | ||
"scripts": { | ||
"test": "tape test/*.js" | ||
"test": "jest" | ||
}, | ||
"testling": { | ||
"files": "test/*.js", | ||
"browsers": [ | ||
"ie/8", | ||
"ie/9", | ||
"ie/10", | ||
"firefox/latest", | ||
"chrome/latest", | ||
"opera/latest", | ||
"safari/latest" | ||
] | ||
"jest": { | ||
"moduleFileExtensions": [ | ||
"js" | ||
], | ||
"testRegex": "test.*\\.spec\\.js$", | ||
"collectCoverage": true, | ||
"coverageReporters": [ | ||
"text-summary", | ||
"html", | ||
"lcov" | ||
], | ||
"collectCoverageFrom": [ | ||
"*.js" | ||
], | ||
"coverageThreshold": { | ||
"global": { | ||
"statements": 100, | ||
"branches": 100, | ||
"functions": 100, | ||
"lines": 100 | ||
} | ||
} | ||
}, | ||
@@ -28,0 +39,0 @@ "repository": { |
@@ -12,6 +12,4 @@ # safe-regex | ||
[](https://ci.testling.com/substack/safe-regex) | ||
[](https://travis-ci.com/davisjam/safe-regex) | ||
[](http://travis-ci.org/substack/safe-regex) | ||
# Example | ||
@@ -62,4 +60,18 @@ | ||
# Versioning | ||
This project follows [Semantic Versioning 2.0 (semver)](https://semver.org/). | ||
Here are the project-specific meanings of MAJOR, MINOR, and PATCH updates: | ||
- MAJOR: "Incompatible" API changes were introduced. There are two types in this module: | ||
- Changes that modify the interface | ||
- Changes that cause any regexes to be marked as unsafe that were formerly marked as safe | ||
- MINOR: Functionality was added in a backwards-compatible manner. There are two types in this module: | ||
- Refactoring the analyses but not changing their results | ||
- Modifying the analyses to reduce false positives, without affecting negatives (false or true) | ||
- PATCH: I don't anticipate using PATCH for this module | ||
# License | ||
MIT | ||
[MIT](https://github.com/davisjam/safe-regex/blob/master/LICENSE) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
14937
123.47%12
50%350
253.54%75
17.19%1
Infinity%1
Infinity%