-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from superindustries/feature/js-interpreter
feat: add basic sandbox JS execution based on VM2
- Loading branch information
Showing
4 changed files
with
82 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,5 +38,8 @@ | |
"rimraf": "^3.0.2", | ||
"ts-jest": "^26.1.0", | ||
"typescript": "^3.9.2" | ||
}, | ||
"dependencies": { | ||
"vm2": "^3.9.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { Sandbox } from "./Sandbox"; | ||
|
||
describe("sandbox", () => { | ||
let sandbox!: Sandbox; | ||
|
||
beforeEach(() => { | ||
process.env.SECRET = "MuchSecret"; | ||
sandbox = new Sandbox(); | ||
}); | ||
|
||
it("prevents string masking attackt", () => { | ||
const js = ` | ||
// Let x be any value not in | ||
// (null, undefined, Object.create(null)). | ||
var x = {}, | ||
// If the attacker can control three strings | ||
a = "constructor", | ||
b = "constructor", | ||
s = "process.env.SECRET = 'overwrite'"; | ||
// and trick code into doing two property lookups | ||
// they control, a call with a string they control, | ||
// and one more call with any argument | ||
x[a][b](s)(); | ||
// then they can cause any side-effect achievable | ||
// solely via objects reachable from the global scope. | ||
// This includes full access to any exported module APIs, | ||
// all declarations in the current module, and access | ||
// to builtin modules like child_process, fs, and net. | ||
`; | ||
|
||
expect(() => sandbox.evalJS(js)).toThrowError(); | ||
expect(process.env.SECRET).toEqual("MuchSecret"); | ||
}); | ||
|
||
it("prevents primodial types pollution", () => { | ||
const js = ` | ||
Array.prototype.toString = () => { | ||
console.log("I have been called!!!!"); | ||
return "Modified!!!!"; | ||
};`; | ||
sandbox.evalJS(js); | ||
expect([1, 2, 3].toString()).toEqual("1,2,3"); | ||
}); | ||
|
||
it("prevents quitting the process", () => { | ||
const js = `process.exit();`; | ||
expect(() => sandbox.evalJS(js)).toThrowError(); | ||
}); | ||
|
||
describe("Halting problem (Stalling the event loop)", () => { | ||
it("while(true)", () => { | ||
expect(() => sandbox.evalJS(`while(true){1+1}`)).toThrowError(/Script execution timed out/); | ||
|
||
}); | ||
|
||
it("prevents using async", () => { | ||
expect(() => sandbox.evalJS(`new Promise.resolve(5)`)).toThrowError(/Promise/); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { VM } from "vm2"; | ||
|
||
export class Sandbox { | ||
evalJS = (js: string): unknown => { | ||
const vm = new VM({ | ||
sandbox: {}, | ||
wasm: false, | ||
eval: false, | ||
timeout: 100, | ||
}); | ||
|
||
return vm.run(`'use strict'; ${js}`); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters