-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vulnerability #12
Comments
Hi, const compiler = require('@nx-js/compiler-util');
const sourceCode = '} { return global.process.env.LOGNAME';
const cleanSourceCode = sourceCode.replace(/^([\n\r\t\s;]*}[\n\r\t\s;]*{)*/, '').trim();
const code = compiler.compileCode(cleanSourceCode);
const user = code({});
console.log(user); |
@mgesmundo That will not be enough, you could use |
@saltimbanc this updated RegEx |
Would transpiling user code from typescript to JavaScript help mitigate such attacks? |
@mgesmundo That RegExp matches valid code, something like I don't think you can fix this with regex, you could use RegExp is not my forte, I think you could check for |
@natarius Probably. This exploit uses invalid syntax so I guess the code will not be transpiled. |
yeah, seems like typescript transpiling catches this! here are the options I used:
|
The TipeScript solution is interesting. The updated RegEx |
@mgesmundo will you commit the regex to the repo? |
@solkimicreb do you like a PR with this proposed fix and its tests? Do you have some other solution other than the RegEx and TypeScript? |
@mgesmundo You can break that RegExp using |
@saltimbanc For my needs I use an async wrapper: const compiler = require('@nx-js/compiler-util')
const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
compiler.expose = [ 'console', 'Promise' ]
const test = async (src) => {
// Fix vulnerability: https://github.com/nx-js/compiler-util/issues/12
const cleanSrc = src.replace(/^([\w\d\s\n\r\t]*}[\w\d\s\n\r\t]*{)*/, '').trim();
const source = `
const $wrapperFunction = async () => {
${cleanSrc}
}
return $wrapperFunction()
`
const code = compiler.compileCode(source);
return code({}, { sleep });
}
const source1 = `
} {
return async ms => {
await sleep(ms);
return 'Hello World!';
}
`
const source2 = `"{";}"";{ return global.process.env.LOGNAME`
const getResult = async () => {
try {
const r = await test(source1);
console.log('>>> result is', await r(1000));
} catch (e) {
console.log('>>> invalid source code')
}
try {
const r = await test(source2);
console.log('>>> result is', await r(1000));
} catch (e) {
console.log('>>> invalid source code')
}
const code = compiler.compileCode(source2);
console.log('>>> vulnerable', code({}))
}
getResult();
// after 1s
// >>> result is Hello World!
// >>> invalid source code
// >>> vulnerable marcello |
Wouldn't it be possible to prevent this vulnerability by simply changing the "wrapper" to include a return statement ? Right now what the wrapper does is essentially This way the code block can't inject a closing curly brace to escape the sandbox and then execute other code, because the other code after the closing brace would never be reached due to the return statement. Obviously this will change the semantics a bit, because to execute multiple statements you would have to wrap them in an immediately invoked function expression (IIFE), but it sounds like a minor price to pay for having a less vulnerable sandbox. If the worry is breaking existing code that relies on the existing behavior, a new "safer" method can be added that implements this, and people can choose which one to use (simpler + less safe) or (more boilerplate + safer) |
Also wouldn't it be also possible to escape the sandbox by simply getting hold of any function, reaching its constructor (i.e. Function), then using it to create a new un-sandboxed evaluation. |
You can escape the sandbox using something like this:
The text was updated successfully, but these errors were encountered: