Skip to content

Conversation

@jeffrey-pinyan-ithreat
Copy link
Contributor

Replaces [\s\S]*? with less explosive alternatives.

// 'root' is just a slash, or nothing.
var splitPathRe =
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
/^((\/?)(?:[^\/]*\/)*)((\.{1,2}|[^\/]+?|)(\.[^.\/]*|))[\/]*$/;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These look like breaking changes to me, and I am not entirely sure if this fixes the DoS. I did a little work on this issue. It is tempting to fix the groups, the whitespace (\s\S), etc., but I found that the existing unit tests were incomplete (even if some are unrealistic).

I found that these tests needed to be added:

var regexDosFileNameLength = 50000;
var regexDosFileName = Buffer.alloc(regexDosFileNameLength, '/').toString() + 'file.txt';

[{ root: '/', dir: '/home/user/dir', base: 'file.txt', ext: '.txt', name: 'file' }, '/home/user/dir/file.txt/'],
[{ root: '/', dir: '/\n\thome/user/dir', base: 'file.txt', ext: '.txt', name: 'file' }, '/\n\thome/user/dir/file.txt/'],
[{ root: '/', dir: '/////////', base: 'file.txt', ext: '.txt', name: 'file' }, '//////////file.txt'],
[{ root: '/', dir: '/', base: '', ext: '', name: '' }, '//////////'],
[{ root: '/', dir: Buffer.alloc(regexDosFileNameLength - 1, '/').toString(), base: 'file.txt', ext: '.txt', name: 'file' }, regexDosFileName],
 ];

If you want these to be breaking changes, then fine. The regexDosFileNameLength is specifically to test the regex DoS.

Copy link
Contributor Author

@jeffrey-pinyan-ithreat jeffrey-pinyan-ithreat May 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can pass those tests with

var splitPathRe =
  /^((\/?)(?:[^\/]*\/)*)((\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:(?<=^\/)|(?<!\/))[\/]*$/;`

return {
  // ...
  dir: allParts[0] === '/' ? allparts[0] : allParts[0].slice(0, -1),
  // ...
}

But as thrilling as it is to have a single regex handling the parsing, it'd be much wiser to clean up the parameter (e.g. by stripping trailing slashes, while retaining a leading slash) and keeping the regex simple.

And as for avoiding the ReDoS, my testing shows a significant improvement:

attack_str.length: 1970001: 332 ms
attack_str.length: 1980001: 359 ms
attack_str.length: 1990001: 385 ms
attack_str.length: 2000001: 295 ms

Copy link

@immense055 immense055 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.npmignore

Copy link

@immense055 immense055 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.npmignore

@immense055
Copy link

09e1086

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants