Skip to content

Commit

Permalink
Merge pull request #248 from kumavis/secure-eval
Browse files Browse the repository at this point in the history
feat: more safely evaluate llm generated code in a SES Compartment
  • Loading branch information
MaxRobinsonTheGreat authored Nov 2, 2024
2 parents 31db4e2 + c06e2ab commit 02232e2
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 60 deletions.
12 changes: 4 additions & 8 deletions bots/template.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import * as skills from '../../../src/agent/library/skills.js';
import * as world from '../../../src/agent/library/world.js';
import Vec3 from 'vec3';
(async (bot) => {

const log = skills.log;
/* CODE HERE */
log(bot, 'Code finished.');

export async function main(bot) {
/* CODE HERE */
log(bot, 'Code finished.');
}
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"prismarine-item": "^1.15.0",
"prismarine-viewer": "^1.28.0",
"replicate": "^0.29.4",
"ses": "^1.9.1",
"vec3": "^0.1.10",
"yargs": "^17.7.2"
},
Expand Down
38 changes: 24 additions & 14 deletions src/agent/coder.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { writeFile, readFile, mkdirSync } from 'fs';
import { checkSafe } from '../utils/safety.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 {
constructor(agent) {
Expand All @@ -21,7 +24,7 @@ export class Coder {
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) {
code = this.sanitizeCode(code);
let src = '';
Expand All @@ -47,13 +50,24 @@ export class Coder {
// } commented for now, useful to keep files for debugging
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) {
console.error('Error writing code execution file: ' + result);
return null;
}
return await import('../..' + this.fp + filename);

return { main: mainFn };
}

sanitizeCode(code) {
Expand Down Expand Up @@ -130,21 +144,17 @@ export class Coder {
}
code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```'));

if (!checkSafe(code)) {
console.warn(`Detected insecure generated code, not executing. Insecure code: \n\`${code}\``);
const message = 'Error: Code insecurity detected. Do not import, read/write files, execute dynamic code, or access the internet. Please try again:';
messages.push({ role: 'system', content: message });
continue;
}

const execution_file = await this.stageCode(code);
if (!execution_file) {
let codeStagingResult;
try {
codeStagingResult = await this.stageCode(code);
} catch (err) {
console.error('Error staging code:', err);
agent_history.add('system', 'Failed to stage code, something is wrong.');
return {success: false, message: null, interrupted: false, timedout: false};
}

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);
if (code_return.interrupted && !code_return.timedout)
return {success: false, message: null, interrupted: true, timedout: false};
Expand Down
26 changes: 26 additions & 0 deletions src/agent/library/lockdown.js
Original file line number Diff line number Diff line change
@@ -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
});
}
38 changes: 0 additions & 38 deletions src/utils/safety.js

This file was deleted.

0 comments on commit 02232e2

Please sign in to comment.