@rulvar/planner 1.37.0 → 1.39.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.
@@ -1,4 +1,6 @@
1
1
  import { ScriptRejected } from "@rulvar/core";
2
+ import * as acorn from "acorn";
3
+ import { scanDialect } from "eslint-plugin-rulvar";
2
4
  //#region src/compile.ts
3
5
  /**
4
6
  * The exact curated sandbox global set, in canonical order.
@@ -164,6 +166,63 @@ function scanImports(source, blanked, allowImports) {
164
166
  }
165
167
  return diagnostics;
166
168
  }
169
+ const CODEGEN_DIAGNOSTIC = {
170
+ eval: {
171
+ ruleId: "no-eval",
172
+ message: "eval is not part of the sandbox dialect; dynamically generated code reopens the import allowlist and the ambient capability surface the dialect closes"
173
+ },
174
+ "function-constructor": {
175
+ ruleId: "no-function-constructor",
176
+ 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)"
177
+ },
178
+ "constructor-access": {
179
+ ruleId: "no-constructor-access",
180
+ message: "constructor access is not part of the sandbox dialect; it reaches the Function constructor from any function value and reopens dynamic code generation"
181
+ }
182
+ };
183
+ /**
184
+ * Rejects dynamic code generation. `eval` and the Function constructor
185
+ * compile attacker supplied text in a fresh scope where the import
186
+ * expression, ambient time, and host capability are back in reach, so
187
+ * banning `import` while allowing `new Function("return import(x)")` closes
188
+ * nothing. Constructor reconstruction is the same vector by another name:
189
+ * every function value exposes the Function constructor through its prototype
190
+ * ((function(){}).constructor === Function), reachable as `.constructor`,
191
+ * `["constructor"]`, a computed key that folds to the constant, a
192
+ * `{ constructor: x }` destructuring, or `Reflect.get(fn, "constructor")`.
193
+ *
194
+ * The scan runs the SAME shared AST policy the `no-code-generation` ESLint
195
+ * rule uses (`scanDialect`), so the linter and this structural gate reach one
196
+ * decision for every statically visible form. A truly dynamic key
197
+ * (`fn[valueFromAnAgent]`) cannot be resolved statically without rejecting
198
+ * every dynamic property access, so it is left to the worker realm, which
199
+ * neutralizes the runtime reconstruction path. A syntax error is reported by
200
+ * the AsyncFunction construction below, so a parse failure here yields no
201
+ * diagnostics rather than a duplicate.
202
+ */
203
+ function scanCodegen(source) {
204
+ let program;
205
+ try {
206
+ program = acorn.parse(source, {
207
+ ecmaVersion: "latest",
208
+ sourceType: "module",
209
+ allowReturnOutsideFunction: true,
210
+ allowAwaitOutsideFunction: true,
211
+ locations: true
212
+ });
213
+ } catch {
214
+ return [];
215
+ }
216
+ return scanDialect(program).map((finding) => {
217
+ const { ruleId, message } = CODEGEN_DIAGNOSTIC[finding.kind];
218
+ return {
219
+ ruleId,
220
+ message,
221
+ line: finding.line,
222
+ column: finding.column
223
+ };
224
+ });
225
+ }
167
226
  /**
168
227
  * Validates and compiles planner-generated source into a CompiledWorkflow.
169
228
  * The source is an async function body over the sandbox
@@ -180,6 +239,7 @@ function compileScript(source, o) {
180
239
  }] } });
181
240
  const blanked = blankLiterals(source);
182
241
  diagnostics.push(...scanImports(source, blanked, allowImports));
242
+ diagnostics.push(...scanCodegen(source));
183
243
  try {
184
244
  new AsyncFunctionCtor(...SANDBOX_GLOBALS, source);
185
245
  } catch (error) {
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
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-DjzdBuva.js";
2
2
  import { ConfigError, InMemoryStore, SANDBOX_AGENT_OPT_KEYS, SandboxError, ScriptRejected, createEngine, createSandboxBridge, defineWorkflow, readRunMeta, toJournalValue } from "@rulvar/core";
3
+ import { toJsonDiagnostics, workflowsConfig } from "eslint-plugin-rulvar";
3
4
  import { createHash } from "node:crypto";
4
5
  import { Linter } from "eslint";
5
- import { toJsonDiagnostics, workflowsConfig } from "eslint-plugin-rulvar";
6
6
  import { MessageChannel, Worker } from "node:worker_threads";
7
7
  //#region src/api-card.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { t as SANDBOX_GLOBALS } from "./compile-BtCY9wfO.js";
1
+ import { t as SANDBOX_GLOBALS } from "./compile-DjzdBuva.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,24 @@ 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);
140
+ const asyncProto = Object.getPrototypeOf(shimAsyncMarker);
141
+ const AsyncFunctionCtor = asyncProto.constructor;
142
+ const denyCodegen = function constructor() {
143
+ throw new EvalError("dynamic code generation is not available in the sandbox dialect");
144
+ };
145
+ for (const proto of [
146
+ Object.getPrototypeOf(shimRegularMarker),
147
+ asyncProto,
148
+ Object.getPrototypeOf(shimGeneratorMarker),
149
+ Object.getPrototypeOf(shimAsyncGeneratorMarker)
150
+ ]) Object.defineProperty(proto, "constructor", {
151
+ value: denyCodegen,
152
+ writable: false,
153
+ enumerable: false,
154
+ configurable: false
155
+ });
138
156
  const sandboxGlobals = {
139
157
  agent: (prompt, opts) => callHost("agent", {
140
158
  prompt,
@@ -268,7 +286,6 @@ function main(port, init) {
268
286
  default: return;
269
287
  }
270
288
  });
271
- const AsyncFunctionCtor = Object.getPrototypeOf(shimAsyncMarker).constructor;
272
289
  enterFrame();
273
290
  (async () => {
274
291
  try {
@@ -286,8 +303,13 @@ function main(port, init) {
286
303
  }
287
304
  })();
288
305
  }
289
- /** Marker reaching the AsyncFunction constructor; never invoked. */
306
+ /** Markers reaching each Function family constructor prototype; never invoked. */
290
307
  const shimAsyncMarker = async () => void 0;
308
+ const shimRegularMarker = () => void 0;
309
+ function* shimGeneratorMarker() {}
310
+ async function* shimAsyncGeneratorMarker() {
311
+ return void 0;
312
+ }
291
313
  parentPort?.once("message", (init) => {
292
314
  main(init.port, init);
293
315
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/planner",
3
- "version": "1.37.0",
3
+ "version": "1.39.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",
@@ -22,15 +22,16 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
+ "acorn": "^8.17.0",
25
26
  "eslint": "^9.39.4",
26
- "@rulvar/core": "1.37.0",
27
- "eslint-plugin-rulvar": "1.37.0"
27
+ "eslint-plugin-rulvar": "1.39.0",
28
+ "@rulvar/core": "1.39.0"
28
29
  },
29
30
  "devDependencies": {
30
31
  "@types/node": "^22.20.0",
31
32
  "tsdown": "^0.22.3",
32
33
  "typescript": "~6.0.3",
33
- "@rulvar/testing": "1.37.0"
34
+ "@rulvar/testing": "1.39.0"
34
35
  },
35
36
  "repository": {
36
37
  "type": "git",