Browse Source

path: fix normalize paths ending with two dots

Fixes: https://github.com/nodejs/security/issues/147
PR-URL: https://github.com/nodejs-private/node-private/pull/94
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Doug Wilson <doug@somethingdoug.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
canary-base
Michaël Zasso 8 years ago
committed by James M Snell
parent
commit
cfee1c9778
  1. 36
      lib/path.js
  2. 7
      test/parallel/test-path-normalize.js

36
lib/path.js

@ -32,10 +32,10 @@ function assertPath(path) {
// Resolves . and .. elements in a path with directory names // Resolves . and .. elements in a path with directory names
function normalizeStringWin32(path, allowAboveRoot) { function normalizeStringWin32(path, allowAboveRoot) {
var res = ''; var res = '';
var lastSegmentLength = 0;
var lastSlash = -1; var lastSlash = -1;
var dots = 0; var dots = 0;
var code; var code;
var isAboveRoot = false;
for (var i = 0; i <= path.length; ++i) { for (var i = 0; i <= path.length; ++i) {
if (i < path.length) if (i < path.length)
code = path.charCodeAt(i); code = path.charCodeAt(i);
@ -47,7 +47,7 @@ function normalizeStringWin32(path, allowAboveRoot) {
if (lastSlash === i - 1 || dots === 1) { if (lastSlash === i - 1 || dots === 1) {
// NOOP // NOOP
} else if (lastSlash !== i - 1 && dots === 2) { } else if (lastSlash !== i - 1 && dots === 2) {
if (res.length < 2 || !isAboveRoot || if (res.length < 2 || lastSegmentLength !== 2 ||
res.charCodeAt(res.length - 1) !== 46/*.*/ || res.charCodeAt(res.length - 1) !== 46/*.*/ ||
res.charCodeAt(res.length - 2) !== 46/*.*/) { res.charCodeAt(res.length - 2) !== 46/*.*/) {
if (res.length > 2) { if (res.length > 2) {
@ -58,20 +58,22 @@ function normalizeStringWin32(path, allowAboveRoot) {
break; break;
} }
if (j !== start) { if (j !== start) {
if (j === -1) if (j === -1) {
res = ''; res = '';
else lastSegmentLength = 0;
} else {
res = res.slice(0, j); res = res.slice(0, j);
lastSegmentLength = j;
}
lastSlash = i; lastSlash = i;
dots = 0; dots = 0;
isAboveRoot = false;
continue; continue;
} }
} else if (res.length === 2 || res.length === 1) { } else if (res.length === 2 || res.length === 1) {
res = ''; res = '';
lastSegmentLength = 0;
lastSlash = i; lastSlash = i;
dots = 0; dots = 0;
isAboveRoot = false;
continue; continue;
} }
} }
@ -80,14 +82,14 @@ function normalizeStringWin32(path, allowAboveRoot) {
res += '\\..'; res += '\\..';
else else
res = '..'; res = '..';
isAboveRoot = true; lastSegmentLength = 2;
} }
} else { } else {
if (res.length > 0) if (res.length > 0)
res += '\\' + path.slice(lastSlash + 1, i); res += '\\' + path.slice(lastSlash + 1, i);
else else
res = path.slice(lastSlash + 1, i); res = path.slice(lastSlash + 1, i);
isAboveRoot = false; lastSegmentLength = i - lastSlash - 1;
} }
lastSlash = i; lastSlash = i;
dots = 0; dots = 0;
@ -103,10 +105,10 @@ function normalizeStringWin32(path, allowAboveRoot) {
// Resolves . and .. elements in a path with directory names // Resolves . and .. elements in a path with directory names
function normalizeStringPosix(path, allowAboveRoot) { function normalizeStringPosix(path, allowAboveRoot) {
var res = ''; var res = '';
var lastSegmentLength = 0;
var lastSlash = -1; var lastSlash = -1;
var dots = 0; var dots = 0;
var code; var code;
var isAboveRoot = false;
for (var i = 0; i <= path.length; ++i) { for (var i = 0; i <= path.length; ++i) {
if (i < path.length) if (i < path.length)
code = path.charCodeAt(i); code = path.charCodeAt(i);
@ -118,7 +120,7 @@ function normalizeStringPosix(path, allowAboveRoot) {
if (lastSlash === i - 1 || dots === 1) { if (lastSlash === i - 1 || dots === 1) {
// NOOP // NOOP
} else if (lastSlash !== i - 1 && dots === 2) { } else if (lastSlash !== i - 1 && dots === 2) {
if (res.length < 2 || !isAboveRoot || if (res.length < 2 || lastSegmentLength !== 2 ||
res.charCodeAt(res.length - 1) !== 46/*.*/ || res.charCodeAt(res.length - 1) !== 46/*.*/ ||
res.charCodeAt(res.length - 2) !== 46/*.*/) { res.charCodeAt(res.length - 2) !== 46/*.*/) {
if (res.length > 2) { if (res.length > 2) {
@ -129,20 +131,22 @@ function normalizeStringPosix(path, allowAboveRoot) {
break; break;
} }
if (j !== start) { if (j !== start) {
if (j === -1) if (j === -1) {
res = ''; res = '';
else lastSegmentLength = 0;
} else {
res = res.slice(0, j); res = res.slice(0, j);
lastSegmentLength = j;
}
lastSlash = i; lastSlash = i;
dots = 0; dots = 0;
isAboveRoot = false;
continue; continue;
} }
} else if (res.length === 2 || res.length === 1) { } else if (res.length === 2 || res.length === 1) {
res = ''; res = '';
lastSegmentLength = 0;
lastSlash = i; lastSlash = i;
dots = 0; dots = 0;
isAboveRoot = false;
continue; continue;
} }
} }
@ -151,14 +155,14 @@ function normalizeStringPosix(path, allowAboveRoot) {
res += '/..'; res += '/..';
else else
res = '..'; res = '..';
isAboveRoot = true; lastSegmentLength = 2;
} }
} else { } else {
if (res.length > 0) if (res.length > 0)
res += '/' + path.slice(lastSlash + 1, i); res += '/' + path.slice(lastSlash + 1, i);
else else
res = path.slice(lastSlash + 1, i); res = path.slice(lastSlash + 1, i);
isAboveRoot = false; lastSegmentLength = i - lastSlash - 1;
} }
lastSlash = i; lastSlash = i;
dots = 0; dots = 0;

7
test/parallel/test-path-normalize.js

@ -23,6 +23,10 @@ assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar');
assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz');
assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\');
assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..');
assert.strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'),
'..\\..\\bar');
assert.strictEqual(path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'),
'..\\..\\bar');
assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'),
'fixtures/b/c.js'); 'fixtures/b/c.js');
@ -37,3 +41,6 @@ assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar');
assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz');
assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../');
assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..');
assert.strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar');
assert.strictEqual(path.posix.normalize('../.../.././.../../../bar'),
'../../bar');

Loading…
Cancel
Save