@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-BtCY9wfO.js";
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";
@@ -1,4 +1,4 @@
1
- import { t as SANDBOX_GLOBALS } from "./compile-BtCY9wfO.js";
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.36.0",
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
- "@rulvar/core": "1.36.0",
27
- "eslint-plugin-rulvar": "1.36.0"
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.36.0"
33
+ "@rulvar/testing": "1.38.0"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",