diff --git a/src/common/compute/interactive/message.js b/src/common/compute/interactive/message.js index 50299f963..e13050b9e 100644 --- a/src/common/compute/interactive/message.js +++ b/src/common/compute/interactive/message.js @@ -9,7 +9,7 @@ } }(this, function() { const Constants = makeEnum('STDOUT', 'STDERR', 'RUN', 'ADD_ARTIFACT', - 'ADD_FILE', 'ADD_USER_DATA', 'COMPLETE', 'ERROR', 'SET_ENV'); + 'ADD_FILE', 'REMOVE_FILE', 'ADD_USER_DATA', 'COMPLETE', 'ERROR', 'SET_ENV'); function makeEnum() { const names = Array.prototype.slice.call(arguments); diff --git a/src/common/compute/interactive/session.js b/src/common/compute/interactive/session.js index 9ec70b820..943b88d73 100644 --- a/src/common/compute/interactive/session.js +++ b/src/common/compute/interactive/session.js @@ -141,6 +141,13 @@ define([ await this.runTask(task); } + async removeFile(filepath) { + this.ensureIdle('remove file'); + const msg = new Message(Message.REMOVE_FILE, [filepath]); + const task = new Task(this.ws, msg); + await this.runTask(task); + } + async setEnvVar(name, value) { this.ensureIdle('set env var'); const msg = new Message(Message.SET_ENV, [name, value]); diff --git a/src/routers/InteractiveCompute/job-files/start.js b/src/routers/InteractiveCompute/job-files/start.js index 0477e427d..327539008 100644 --- a/src/routers/InteractiveCompute/job-files/start.js +++ b/src/routers/InteractiveCompute/job-files/start.js @@ -57,6 +57,7 @@ class InteractiveClient { } else if (msg.type === Message.ADD_FILE) { this.runTask(() => { const [filepath, content] = msg.data; + this.ensureValidPath(filepath); this.writeFile(filepath, content); }); } else if (msg.type === Message.SET_ENV) { @@ -64,6 +65,25 @@ class InteractiveClient { const [name, value] = msg.data; process.env[name] = value; }); + } else if (msg.type === Message.REMOVE_FILE) { + this.runTask(async () => { + const [filepath] = msg.data; + this.ensureValidPath(filepath); + await fsp.unlink(filepath); + }); + } else { + this.sendMessage(Message.COMPLETE, 2); + } + } + + ensureValidPath(filepath) { + const isOutsideWorkspace = path.relative( + path.resolve(__dirname), + path.resolve(filepath) + ).startsWith('..'); + + if (isOutsideWorkspace) { + throw new Error('Cannot edit files outside workspace: ' + filepath); } } diff --git a/test/integration/InteractiveCompute.js b/test/integration/InteractiveCompute.js index 87cd62521..0127542be 100644 --- a/test/integration/InteractiveCompute.js +++ b/test/integration/InteractiveCompute.js @@ -42,4 +42,15 @@ describe('InteractiveCompute', function() { const {stdout} = await session.exec('cat test.txt'); assert.equal(stdout, 'hello world'); }); + + it('should remove file', async function() { + try { + await session.addFile('test.txt', 'hello world'); + await session.removeFile('test.txt'); + const {stdout} = await session.exec('cat test.txt'); + assert(false); + } catch (err) { + assert(err.jobResult.stderr.includes('No such file')); + } + }); });