Skip to content

Commit

Permalink
fix(disk-util): All subdirs under approved dirs are also approved
Browse files Browse the repository at this point in the history
Fixes #831
  • Loading branch information
Göran Sander committed Nov 8, 2023
1 parent 24f54b0 commit 0ecc402
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/config/production_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,8 @@ Butler:
backgroundServerPort: 8081 # Port used internally by Butler's REST API. Any free port on the server where Butler is running can bse used.

# List of directories between which file copying via the REST API can be done.
# NOTE: All subdirectories of the specified from and to directories are also approved/allowed.
#
# Butler will try to clean up messy paths like this one, which resolves to /Users/goran/butler-test-dir1
# How? First you have /Users/goran/butler-test-dir1//abc which cleans up to /Users/goran/butler-test-dir1/abc,
# then up one level (..).
Expand All @@ -588,7 +590,8 @@ Butler:
toDirectory: /to/some/directory2

# List of directories between which file moves via the REST API can be done.
fileMoveApprovedDirectories:
# NOTE: All subdirectories of the specified from and to directories are also approved/allowed.
fileMoveApprovedDirectories:
- fromDirectory: /Users/goran/butler-test-dir1//abc//..
toDirectory: /Users/goran/butler-test-dir2
- fromDirectory: /Users/goran/butler-test-dir2
Expand All @@ -597,6 +600,7 @@ Butler:
toDirectory: /to/some/directory2

# List of directories in which file deletes via the REST API can be done.
# NOTE: All subdirectories of the specified directories are also approved/allowed.
fileDeleteApprovedDirectories:
- /Users/goran/butler-test-dir1
- /Users/goran/butler-test-dir1//abc//..
Expand Down
8 changes: 7 additions & 1 deletion src/lib/disk_utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// Function to check if a given directory (child) is a subdirectory of
// another directory (parent).
//
// This function assumes that both child and parent are normalized paths
// (i.e., they don't contain . or .. segments), and that they use
// forward slashes (/) as path separators.
const isDirectoryChildOf = (child, parent) => {
if (child === parent) return true;

const parentTokens = parent.split('/').filter((i) => i.length);

return parentTokens.every((t, i) => child.split('/')[i] === t);
return parentTokens.every((t, i) => child.split('/').filter((i) => i.length)[i] === t);
};

module.exports = {
Expand Down
152 changes: 152 additions & 0 deletions src/test/routes/disk_utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ process.env.NODE_CONFIG_DIR = upath.resolve('./src/config/');
process.env.NODE_ENV = 'production';
const config = require('config');

const { isDirectoryChildOf } = require('../../lib/disk_utils');

const instance = axios.create({
baseURL: `http://localhost:${config.get('Butler.restServerConfig.serverPort')}`,
timeout: 15000,
Expand Down Expand Up @@ -358,3 +360,153 @@ describe('E7: POST /v4/createdir', () => {
expect(result.data.directory).toEqual(p);
});
});

/**
* E8
* Ensure that isDirectoryChildOf works as expected
*/
const dirParent1 = './testDir1';
const dirParent2 = './testDir1/testDir2';
const dirParent3 = './testDir1/testDir2/';

const dirChild1 = './testDir1';
const dirChild2 = './testDir1/testDir2';
const dirChild3 = './testDir1/testDir2/';
const dirChild4 = './testDir1/testDir2/testDir3';
const dirChild5 = './testDir1/testDir2/testDir3/testDir4';
const dirChild6 = './testDir1/testDir2/testDir3/testDir4/';

describe('E8: isDirectoryChildOf', () => {
test(`"${dirChild1}" is a child of "${dirParent1}"`, async () => {
try {
result = isDirectoryChildOf(dirChild1, dirParent1);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild2}" is a child of "${dirParent2}"`, async () => {
try {
result = isDirectoryChildOf(dirChild2, dirParent2);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild3}" is a child of "${dirParent2}"`, async () => {
try {
result = isDirectoryChildOf(dirChild3, dirParent2);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild3}" is a child of "${dirParent3}"`, async () => {
try {
result = isDirectoryChildOf(dirChild3, dirParent3);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild2}" is a child of "${dirParent3}"`, async () => {
try {
result = isDirectoryChildOf(dirChild2, dirParent3);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild4}" is a child of "${dirParent2}"`, async () => {
try {
result = isDirectoryChildOf(dirChild4, dirParent2);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild5}" is a child of "${dirParent2}"`, async () => {
try {
result = isDirectoryChildOf(dirChild5, dirParent2);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

test(`"${dirChild6}" is a child of "${dirParent2}"`, async () => {
try {
result = isDirectoryChildOf(dirChild6, dirParent2);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(true);
}, 5000);

// Test failure cases where child directory is not a child of parent
test(`"${dirChild1}" is not a child of "${dirParent2}"`, async () => {
try {
result = isDirectoryChildOf(dirChild1, dirParent2);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(false);
}, 5000);

test(`"${dirChild4}" is not a child of "${dirParent1}"`, async () => {
try {
result = isDirectoryChildOf(dirChild4, dirParent1);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(false);
}, 5000);

test(`"${dirChild5}" is not a child of "${dirParent1}"`, async () => {
try {
result = isDirectoryChildOf(dirChild5, dirParent1);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(false);
}, 5000);

test(`"${dirChild6}" is not a child of "${dirParent1}"`, async () => {
try {
result = isDirectoryChildOf(dirChild6, dirParent1);
} catch (err) {
result = err.response;
console.log('Error testing isDirectoryChildOf');
}

expect(result).toBe(false);
}, 5000);
});

0 comments on commit 0ecc402

Please sign in to comment.