@rulvar/planner 1.36.0 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -164,6 +164,42 @@ function scanImports(source, blanked, allowImports) {
|
|
|
164
164
|
}
|
|
165
165
|
return diagnostics;
|
|
166
166
|
}
|
|
167
|
+
const CODEGEN_WORD = /\b(eval|Function)\b/g;
|
|
168
|
+
const CONSTRUCTOR_ACCESS = /\.\s*constructor\b/g;
|
|
169
|
+
/**
|
|
170
|
+
* Rejects dynamic code generation. `eval` and the Function constructor
|
|
171
|
+
* compile attacker supplied text in a fresh scope where the import
|
|
172
|
+
* expression, ambient time, and host capability are back in reach, so
|
|
173
|
+
* banning `import` while allowing `new Function("return import(x)")` closes
|
|
174
|
+
* nothing. `.constructor` access is the same vector by another name: every
|
|
175
|
+
* function value exposes the Function constructor as its `.constructor`
|
|
176
|
+
* ((function(){}).constructor === Function). Literals and comments are
|
|
177
|
+
* blanked before the scan, so a banned word inside a string never matches;
|
|
178
|
+
* the identifier form (not just calls) is rejected because a captured
|
|
179
|
+
* reference (`const f = Function`) is the trivial workaround.
|
|
180
|
+
*/
|
|
181
|
+
function scanCodegen(source, blanked) {
|
|
182
|
+
const diagnostics = [];
|
|
183
|
+
for (let match = CODEGEN_WORD.exec(blanked); match !== null; match = CODEGEN_WORD.exec(blanked)) {
|
|
184
|
+
const at = positionAt(source, match.index);
|
|
185
|
+
if (match[1] === "eval") diagnostics.push({
|
|
186
|
+
ruleId: "no-eval",
|
|
187
|
+
message: "eval is not part of the sandbox dialect; dynamically generated code reopens the import allowlist and the ambient capability surface the dialect closes",
|
|
188
|
+
...at
|
|
189
|
+
});
|
|
190
|
+
else diagnostics.push({
|
|
191
|
+
ruleId: "no-function-constructor",
|
|
192
|
+
message: "the Function constructor is not part of the sandbox dialect; it compiles arbitrary code that bypasses the import allowlist (use only the curated globals)",
|
|
193
|
+
...at
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
for (let match = CONSTRUCTOR_ACCESS.exec(blanked); match !== null; match = CONSTRUCTOR_ACCESS.exec(blanked)) diagnostics.push({
|
|
197
|
+
ruleId: "no-constructor-access",
|
|
198
|
+
message: ".constructor access is not part of the sandbox dialect; it reaches the Function constructor from any function value and reopens dynamic code generation",
|
|
199
|
+
...positionAt(source, match.index)
|
|
200
|
+
});
|
|
201
|
+
return diagnostics;
|
|
202
|
+
}
|
|
167
203
|
/**
|
|
168
204
|
* Validates and compiles planner-generated source into a CompiledWorkflow.
|
|
169
205
|
* The source is an async function body over the sandbox
|
|
@@ -180,6 +216,7 @@ function compileScript(source, o) {
|
|
|
180
216
|
}] } });
|
|
181
217
|
const blanked = blankLiterals(source);
|
|
182
218
|
diagnostics.push(...scanImports(source, blanked, allowImports));
|
|
219
|
+
diagnostics.push(...scanCodegen(source, blanked));
|
|
183
220
|
try {
|
|
184
221
|
new AsyncFunctionCtor(...SANDBOX_GLOBALS, source);
|
|
185
222
|
} catch (error) {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as compileScript, r as scriptDiagnosticsOf, t as SANDBOX_GLOBALS } from "./compile-
|
|
1
|
+
import { n as compileScript, r as scriptDiagnosticsOf, t as SANDBOX_GLOBALS } from "./compile-BOStPCrA.js";
|
|
2
2
|
import { ConfigError, InMemoryStore, SANDBOX_AGENT_OPT_KEYS, SandboxError, ScriptRejected, createEngine, createSandboxBridge, defineWorkflow, readRunMeta, toJournalValue } from "@rulvar/core";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { Linter } from "eslint";
|
package/dist/sandbox-worker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as SANDBOX_GLOBALS } from "./compile-
|
|
1
|
+
import { t as SANDBOX_GLOBALS } from "./compile-BOStPCrA.js";
|
|
2
2
|
import { NonSerializableValueError, toJournalValue } from "@rulvar/core";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { parentPort } from "node:worker_threads";
|
|
@@ -135,6 +135,8 @@ function main(port, init) {
|
|
|
135
135
|
Math.random = shimRandom;
|
|
136
136
|
Reflect.set(globalThis, "fetch", void 0);
|
|
137
137
|
Reflect.set(globalThis, "process", void 0);
|
|
138
|
+
Reflect.set(globalThis, "eval", void 0);
|
|
139
|
+
Reflect.set(globalThis, "Function", void 0);
|
|
138
140
|
const sandboxGlobals = {
|
|
139
141
|
agent: (prompt, opts) => callHost("agent", {
|
|
140
142
|
prompt,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/planner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.38.0",
|
|
4
4
|
"description": "Rulvar flagship hybrid mode: plan agent, compileScript, WorkerSandboxRunner, self-repair loop.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"eslint": "^9.39.4",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
26
|
+
"eslint-plugin-rulvar": "1.38.0",
|
|
27
|
+
"@rulvar/core": "1.38.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^22.20.0",
|
|
31
31
|
"tsdown": "^0.22.3",
|
|
32
32
|
"typescript": "~6.0.3",
|
|
33
|
-
"@rulvar/testing": "1.
|
|
33
|
+
"@rulvar/testing": "1.38.0"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|