From f3140da18c336d903c3adbceca05e6a1721668c0 Mon Sep 17 00:00:00 2001 From: Joseph Dylan Stewart Date: Tue, 27 Sep 2022 10:40:04 -0700 Subject: [PATCH] [TEST] Security related tests Explicit tests to check for available apis that should not be accessible --- test/UserCodeRunner.spec.ts | 119 +++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/test/UserCodeRunner.spec.ts b/test/UserCodeRunner.spec.ts index 1bc309c..7e39927 100644 --- a/test/UserCodeRunner.spec.ts +++ b/test/UserCodeRunner.spec.ts @@ -9,6 +9,121 @@ import * as vm from "vm"; import * as fs from "fs"; describe('behavior', () => { + describe('SECURITY', () => { + + it('should have limited globals available for use', async () => { + const userCode = ` + export default function MyDSLFunction() { + // @ts-ignore + return Object.keys(globalThis).join(','); + } + `.trimTemplate(); + + const runner = new UserCodeRunner(); + + const result = await runner.executeUserCode( + userCode, + [], + 'string', + [], + ); + + expect(result.isOk()).toBe(true); + expect(result.unwrap()).toBe('__args,__result'); + }); + + it('process should not be accessible', async () => { + const userCode = ` + export default function MyDSLFunction() { + // @ts-ignore + process.exit(1); + } + `.trimTemplate(); + + const runner = new UserCodeRunner(); + + const result = await runner.executeUserCode( + userCode, + [], + 'void', + [], + ); + + expect(result.isErr()).toBe(true); + expect(result.unwrapErr().length).toBe(1); + + expect(result.unwrapErr()[0].message).toBe('Error: process is not defined'); + }); + + it('globalThis.process should not be accessible', async () => { + const userCode = ` + export default function MyDSLFunction() { + // @ts-ignore + globalThis.process.exit(1); + } + `.trimTemplate(); + + const runner = new UserCodeRunner(); + + const result = await runner.executeUserCode( + userCode, + [], + 'void', + [], + ); + + expect(result.isErr()).toBe(true); + expect(result.unwrapErr().length).toBe(1); + + expect(result.unwrapErr()[0].message).toBe("Error: Cannot read properties of undefined (reading 'exit')"); + }); + + it('fetch should not be accessible', async () => { + const userCode = ` + export default function MyDSLFunction() { + // @ts-ignore + fetch('http://example.com'); + } + `.trimTemplate(); + + const runner = new UserCodeRunner(); + + const result = await runner.executeUserCode( + userCode, + [], + 'void', + [], + ); + + expect(result.isErr()).toBe(true); + expect(result.unwrapErr().length).toBe(1); + + expect(result.unwrapErr()[0].message).toBe('Error: fetch is not defined'); + }); + + it('globalThis.fetch should not be accessible', async () => { + const userCode = ` + export default function MyDSLFunction() { + globalThis.fetch('http://example.com'); + } + `.trimTemplate(); + + const runner = new UserCodeRunner(); + + const result = await runner.executeUserCode( + userCode, + [], + 'void', + [], + ); + + expect(result.isErr()).toBe(true); + expect(result.unwrapErr().length).toBe(1); + + expect(result.unwrapErr()[0].message).toBe('Error: globalThis.fetch is not a function'); + }); + }); + it('should produce runtime errors', async () => { const userCode = ` export default function MyDSLFunction(thing: string): string { @@ -633,9 +748,9 @@ describe('behavior', () => { expect(err.stack).toContain(` Error: This is a test error at additionalFile:1:7 - at SourceTextModule.evaluate (node:internal/vm/module:226:23) `.trimTemplate()); - expect(err.stack).toMatch(/at UserCodeRunner\.executeUserCodeFromArtifacts \(\S+src\/UserCodeRunner\.ts:226:24/); + expect(err.stack).toMatch(/at SourceTextModule.evaluate \(node:internal\/vm\/module:\d+:\d+\)/); + expect(err.stack).toMatch(/at UserCodeRunner\.executeUserCodeFromArtifacts \(\S+src\/UserCodeRunner\.ts:\d+:\d+/); } });