Improve nested path detection across app

This commit is contained in:
Evan Hahn 2020-08-27 13:08:37 -05:00 committed by Josh Perez
parent 57d206a344
commit f8fc23a7a3
5 changed files with 169 additions and 12 deletions

View file

@ -0,0 +1,42 @@
import { assert } from 'chai';
import { isPathInside } from '../../util/isPathInside';
describe('isPathInside', () => {
it('returns false if the child path is not inside the parent path', () => {
assert.isFalse(isPathInside('x', 'a/b'));
assert.isFalse(isPathInside('a/b', '/a/b'));
assert.isFalse(isPathInside('/a/b', 'a/b'));
assert.isFalse(isPathInside('/x', '/a/b'));
assert.isFalse(isPathInside('/x/y', '/a/b'));
assert.isFalse(isPathInside('/a/x', '/a/b'));
assert.isFalse(isPathInside('/a/bad', '/a/b'));
assert.isFalse(isPathInside('/a/x', '/a/b'));
assert.isFalse(isPathInside('/a/b', '/a/b'));
assert.isFalse(isPathInside('/a/b/c/..', '/a/b'));
assert.isFalse(isPathInside('/', '/'));
assert.isFalse(isPathInside('/x/..', '/'));
if (process.platform === 'win32') {
assert.isFalse(isPathInside('C:\\a\\x\\y', 'C:\\a\\b'));
assert.isFalse(isPathInside('D:\\a\\b\\c', 'C:\\a\\b'));
}
});
it('returns true if the child path is inside the parent path', () => {
assert.isTrue(isPathInside('a/b/c', 'a/b'));
assert.isTrue(isPathInside('a/b/c/d', 'a/b'));
assert.isTrue(isPathInside('/a/b/c', '/a/b'));
assert.isTrue(isPathInside('/a/b/c', '/a/b/'));
assert.isTrue(isPathInside('/a/b/c/', '/a/b'));
assert.isTrue(isPathInside('/a/b/c/', '/a/b/'));
assert.isTrue(isPathInside('/a/b/c/d', '/a/b'));
assert.isTrue(isPathInside('/a/b/c/d/..', '/a/b'));
assert.isTrue(isPathInside('/x/y/z/z/y', '/'));
assert.isTrue(isPathInside('x/y/z/z/y', '/'));
if (process.platform === 'win32') {
assert.isTrue(isPathInside('C:\\a\\b\\c', 'C:\\a\\b'));
}
});
});

View file

@ -26,6 +26,7 @@ import { Dialogs } from '../types/Dialogs';
// @ts-ignore
import * as packageJson from '../../package.json';
import { getSignatureFileName } from './signature';
import { isPathInside } from '../util/isPathInside';
import { LocaleType } from '../types/I18N';
import { LoggerType } from '../types/Logging';
@ -71,7 +72,7 @@ export async function checkForUpdates(
export function validatePath(basePath: string, targetPath: string) {
const normalized = normalize(targetPath);
if (!normalized.startsWith(basePath)) {
if (!isPathInside(normalized, basePath)) {
throw new Error(
`validatePath: Path ${normalized} is not under base path ${basePath}`
);
@ -352,7 +353,7 @@ export async function deleteTempDir(targetDir: string) {
}
const baseTempDir = getBaseTempDir();
if (!targetDir.startsWith(baseTempDir)) {
if (!isPathInside(targetDir, baseTempDir)) {
throw new Error(
`deleteTempDir: Cannot delete path '${targetDir}' since it is not within base temp dir`
);

16
ts/util/isPathInside.ts Normal file
View file

@ -0,0 +1,16 @@
// This is inspired by the `is-path-inside` module on npm.
import * as path from 'path';
export function isPathInside(childPath: string, parentPath: string): boolean {
const childPathResolved = path.resolve(childPath);
let parentPathResolved = path.resolve(parentPath);
if (!parentPathResolved.endsWith(path.sep)) {
parentPathResolved += path.sep;
}
return (
childPathResolved !== parentPathResolved &&
childPathResolved.startsWith(parentPathResolved)
);
}