-
-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Fix ReDoS vulnerability CVE-2021-35065 #49
Changes from 2 commits
d16d629
723eed9
9bc351f
468dde3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ var gp = require('../'); | |
var expect = require('expect'); | ||
var isWin32 = require('os').platform() === 'win32'; | ||
|
||
var performance = require('perf_hooks').performance; | ||
|
||
describe('glob-parent', function () { | ||
it('should strip glob magic to return parent path', function (done) { | ||
expect(gp('.')).toEqual('.'); | ||
|
@@ -224,6 +226,26 @@ describe('glob2base test patterns', function () { | |
|
||
done(); | ||
}); | ||
|
||
it('should not increase calc. time exponentially by \'/\' count [CVE-2021-35065]', function (done) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment says "exponential". This regex will exhibit polynomial growth, not exponential growth. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll remove this case and add a new case to check absolute time. |
||
var measure = function(n) { | ||
var input = "{" + "/".repeat(n); | ||
var st = performance.now(); | ||
gp(input); | ||
var ed = performance.now(); | ||
return (ed - st) / (n * n); | ||
}; | ||
|
||
var result0 = measure(5000); | ||
|
||
[50000, 500000].forEach(function(n) { | ||
var result1 = measure(n); | ||
expect(result1 / result0).toBeLessThan(0.9); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly you should also check for a "reasonable" absolute matching time, instead of just checking for a low-enough relative increase. For example, you could also assert that the 500K string finishes in under 1 second. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll remove this case and add a new case to check absolute time. |
||
result0 = result1; | ||
}); | ||
|
||
done(); | ||
}); | ||
}); | ||
|
||
if (isWin32) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this suffice (and be a fair bit easier to read as well)?
If not, we should probably add a test case that shows it not working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Trott The direct solution of this issue is the way as you suggested. But the reporter suggested this solution and we agree with it because we can't accept newlines in a filepath.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the decision is "we won't accept newlines in a path", then OK. But if the belief is "newlines cannot legitimately appear in a path", then I do not believe that is correct. See https://superuser.com/a/129532/81159. Or try this (which is basically the same thing as in that linked answer):
(The
quote>
thing is zsh console output. Here's basically the same thing in Bourne shell instead.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a onetime file system tester, I'm with @Trott here: newlines are valid path elements in many file systems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This regex is much improved in its performance. However. I did not check the context in which it is used, but if one of the "match a substring within this string" APIs is used then the performance can still be problematic.
Try for example:
(This is slow in a Node v12 REPL I found on the web).
The slow performance is because:
n
open-braces{
(the first *'d group).O(n)
of them before rejecting each time -- that makes forO(n^2)
behavior.So, food for thought:
2.Are there any additional constraints on the input we could add in? (e.g. enforcing a maximum length, or additional properties that the regex can check) I think there is a possibly-illegible refactoring that would do the trick, but it would be nicer if there is a more maintainable option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at the project again and it seems like the regex
/(^|[^\\])([{[]|\([^)]+$)/
has the same problem for all inputs"(".repeat(n)
.Whatever solution is chosen for
/[[{[][^/\r\n\u2028\u2029]*\/.*[}\]]$/
should also be applied to/(^|[^\\])([{[]|\([^)]+$)/
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine that ideally, this wouldn't be doing regex matching at all but would instead use some module that does proper parsing for globs. Although I guess there really isn't a single globbing standard to adhere to. Anyway, if no one's looked yet, it might be worth looking at what's out there (to make sure they just don't do regexp matching under the hood).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option is to use a linear-time regex engine. For example, you could add
re2
as a (somewhat heavy, alas) dependency. It supports all of the features used in these regexes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Please @ me if you want further opinions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RunDevelopment I think that regex has no problem.