mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-04 22:35:35 +02:00
Merge pull request #248 from kumavis/secure-eval
feat: more safely evaluate llm generated code in a SES Compartment
This commit is contained in:
commit
02232e2a85
5 changed files with 55 additions and 60 deletions
|
@ -1,10 +1,6 @@
|
||||||
import * as skills from '../../../src/agent/library/skills.js';
|
(async (bot) => {
|
||||||
import * as world from '../../../src/agent/library/world.js';
|
|
||||||
import Vec3 from 'vec3';
|
|
||||||
|
|
||||||
const log = skills.log;
|
|
||||||
|
|
||||||
export async function main(bot) {
|
|
||||||
/* CODE HERE */
|
/* CODE HERE */
|
||||||
log(bot, 'Code finished.');
|
log(bot, 'Code finished.');
|
||||||
}
|
|
||||||
|
})
|
|
@ -18,6 +18,7 @@
|
||||||
"prismarine-item": "^1.15.0",
|
"prismarine-item": "^1.15.0",
|
||||||
"prismarine-viewer": "^1.28.0",
|
"prismarine-viewer": "^1.28.0",
|
||||||
"replicate": "^0.29.4",
|
"replicate": "^0.29.4",
|
||||||
|
"ses": "^1.9.1",
|
||||||
"vec3": "^0.1.10",
|
"vec3": "^0.1.10",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { writeFile, readFile, mkdirSync } from 'fs';
|
import { writeFile, readFile, mkdirSync } from 'fs';
|
||||||
import { checkSafe } from '../utils/safety.js';
|
|
||||||
import settings from '../../settings.js';
|
import settings from '../../settings.js';
|
||||||
|
import { makeCompartment } from './library/lockdown.js';
|
||||||
|
import * as skills from './library/skills.js';
|
||||||
|
import * as world from './library/world.js';
|
||||||
|
import { Vec3 } from 'vec3';
|
||||||
|
|
||||||
export class Coder {
|
export class Coder {
|
||||||
constructor(agent) {
|
constructor(agent) {
|
||||||
|
@ -21,7 +24,7 @@ export class Coder {
|
||||||
mkdirSync('.' + this.fp, { recursive: true });
|
mkdirSync('.' + this.fp, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// write custom code to file and import it
|
// write custom code to file and prepare for evaluation
|
||||||
async stageCode(code) {
|
async stageCode(code) {
|
||||||
code = this.sanitizeCode(code);
|
code = this.sanitizeCode(code);
|
||||||
let src = '';
|
let src = '';
|
||||||
|
@ -47,13 +50,24 @@ export class Coder {
|
||||||
// } commented for now, useful to keep files for debugging
|
// } commented for now, useful to keep files for debugging
|
||||||
this.file_counter++;
|
this.file_counter++;
|
||||||
|
|
||||||
let write_result = await this.writeFilePromise('.' + this.fp + filename, src)
|
let write_result = await this.writeFilePromise('.' + this.fp + filename, src);
|
||||||
|
// This is where we determine the environment the agent's code should be exposed to.
|
||||||
|
// It will only have access to these things, (in addition to basic javascript objects like Array, Object, etc.)
|
||||||
|
// Note that the code may be able to modify the exposed objects.
|
||||||
|
const compartment = makeCompartment({
|
||||||
|
skills,
|
||||||
|
log: skills.log,
|
||||||
|
world,
|
||||||
|
Vec3,
|
||||||
|
});
|
||||||
|
const mainFn = compartment.evaluate(src);
|
||||||
|
|
||||||
if (write_result) {
|
if (write_result) {
|
||||||
console.error('Error writing code execution file: ' + result);
|
console.error('Error writing code execution file: ' + result);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return await import('../..' + this.fp + filename);
|
|
||||||
|
return { main: mainFn };
|
||||||
}
|
}
|
||||||
|
|
||||||
sanitizeCode(code) {
|
sanitizeCode(code) {
|
||||||
|
@ -130,21 +144,17 @@ export class Coder {
|
||||||
}
|
}
|
||||||
code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```'));
|
code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```'));
|
||||||
|
|
||||||
if (!checkSafe(code)) {
|
let codeStagingResult;
|
||||||
console.warn(`Detected insecure generated code, not executing. Insecure code: \n\`${code}\``);
|
try {
|
||||||
const message = 'Error: Code insecurity detected. Do not import, read/write files, execute dynamic code, or access the internet. Please try again:';
|
codeStagingResult = await this.stageCode(code);
|
||||||
messages.push({ role: 'system', content: message });
|
} catch (err) {
|
||||||
continue;
|
console.error('Error staging code:', err);
|
||||||
}
|
|
||||||
|
|
||||||
const execution_file = await this.stageCode(code);
|
|
||||||
if (!execution_file) {
|
|
||||||
agent_history.add('system', 'Failed to stage code, something is wrong.');
|
agent_history.add('system', 'Failed to stage code, something is wrong.');
|
||||||
return {success: false, message: null, interrupted: false, timedout: false};
|
return {success: false, message: null, interrupted: false, timedout: false};
|
||||||
}
|
}
|
||||||
|
|
||||||
code_return = await this.execute(async ()=>{
|
code_return = await this.execute(async ()=>{
|
||||||
return await execution_file.main(this.agent.bot);
|
return await codeStagingResult.main(this.agent.bot);
|
||||||
}, settings.code_timeout_mins);
|
}, settings.code_timeout_mins);
|
||||||
if (code_return.interrupted && !code_return.timedout)
|
if (code_return.interrupted && !code_return.timedout)
|
||||||
return {success: false, message: null, interrupted: true, timedout: false};
|
return {success: false, message: null, interrupted: true, timedout: false};
|
||||||
|
|
26
src/agent/library/lockdown.js
Normal file
26
src/agent/library/lockdown.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import 'ses';
|
||||||
|
|
||||||
|
// This sets up the secure environment
|
||||||
|
// We disable some of the taming to allow for more flexibility
|
||||||
|
|
||||||
|
// For configuration, see https://github.com/endojs/endo/blob/master/packages/ses/docs/lockdown.md
|
||||||
|
lockdown({
|
||||||
|
// basic devex and quality of life improvements
|
||||||
|
localeTaming: 'unsafe',
|
||||||
|
consoleTaming: 'unsafe',
|
||||||
|
errorTaming: 'unsafe',
|
||||||
|
stackFiltering: 'verbose',
|
||||||
|
// allow eval outside of created compartments
|
||||||
|
// (mineflayer dep "protodef" uses eval)
|
||||||
|
evalTaming: 'unsafeEval',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const makeCompartment = (endowments = {}) => {
|
||||||
|
return new Compartment({
|
||||||
|
// provide untamed Math, Date, etc
|
||||||
|
Math,
|
||||||
|
Date,
|
||||||
|
// standard endowments
|
||||||
|
...endowments
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,38 +0,0 @@
|
||||||
export function checkSafe(code) {
|
|
||||||
const dangerousPatterns = [
|
|
||||||
// Dynamic imports
|
|
||||||
/\bimport\s*\(/,
|
|
||||||
// Access to process and global
|
|
||||||
/\bprocess\b/,
|
|
||||||
/\bglobal\b/,
|
|
||||||
// Module manipulation
|
|
||||||
/\bmodule\b/,
|
|
||||||
/\bexports\b/,
|
|
||||||
// Require usage
|
|
||||||
/\brequire\s*\(/,
|
|
||||||
// Function constructors
|
|
||||||
/\bFunction\s*\(/,
|
|
||||||
/\beval\s*\(/,
|
|
||||||
// Access to __dirname and __filename
|
|
||||||
/\b__dirname\b/,
|
|
||||||
/\b__filename\b/,
|
|
||||||
|
|
||||||
// fetch
|
|
||||||
/\bfetch\s*\(/,
|
|
||||||
// XMLHttpRequest
|
|
||||||
/\bXMLHttpRequest\b/,
|
|
||||||
// Websockets
|
|
||||||
/\bWebSocket\b/,
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const pattern of dangerousPatterns) {
|
|
||||||
if (pattern.test(code)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// generated by o1
|
|
||||||
// Basic check for malicious code like dynamic imports, code exec, disk access, internet access, etc.
|
|
||||||
// Will not catch all, and can be bypassed by obfuscation.
|
|
Loading…
Add table
Reference in a new issue