* fix(plugin-workflow-javascript): fix security issue
* fix(plugin-workflow-javascript): fix path assertion under windows in test case
* fix(plugin-workflow-javascript): sanitize module function return values to close return-value bypass
Extend the sandbox hardening to also sanitize objects returned by wrapped
module functions (including Promise resolutions), so that prototype-chain
traversal via return values (e.g. crypto.createHash().update.constructor)
cannot reach the host Function constructor.
- Add sanitizeReturnValue() that sanitizes sync return values and
thenable resolutions before they re-enter the sandbox
- Wire sanitizeReturnValue() into the function wrapper inside
sanitizeForSandbox() so every module function call goes through it
- Walk the prototype chain up to MAX_PROTO_DEPTH=3 in the object branch
so methods defined on the object's own prototype (e.g.
Hash.prototype.update, Stats.prototype.isFile) are also exposed as
hardened null-proto wrappers rather than raw host-realm functions
- Add resolveProperty() helper that reads accessor-descriptor properties
(getters) by invoking desc.get — required for lazy-getter module
exports such as mathjs.evaluate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plugin-workflow-javascript): sanitize exceptions and Promises from module calls
Address two P1 bypasses reported in PR review:
P1.1 — Module function exceptions: the function wrapper now wraps fn(...args)
in try/catch and calls sanitizeError() before re-throwing, so a host-realm
Error caught inside the sandbox (e.g. from path.join(null)) no longer exposes
e.constructor.constructor → host Function.
P1.2 — Host Promise exposed to sandbox: sanitizeReturnValue() no longer returns
the raw host Promise (whose .constructor chain reaches host Function). It now
builds a null-prototype clean thenable that intercepts onFulfilled/onRejected
callbacks, sanitizing resolved values via sanitizeForSandbox() and errors via
sanitizeError() before they reach sandbox code.
Also adds sanitizeError() helper shared by both fixes, and extends the test
suite with:
- P1.1 security test: path.join(null) throw chain is blocked
- P1.2 security test: fs.promises.readdir Promise constructor chain is blocked
- Functional regression: fs.promises.readdir still returns usable data after
sanitization (note: result is a null-proto object; Array.isArray returns false
but indexed access and .length work — raw Arrays cannot be returned safely as
their __proto__.constructor chain leads back to host Function)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plugin-workflow-javascript): restore array iteration for sanitized module return values
Arrays returned by module functions were converted to null-prototype plain
objects, which broke for...of, spread ([...arr]), and destructuring — the
three most common ways to consume arrays in modern JavaScript.
Add a special-case Array branch in sanitizeForSandbox() that:
- copies indexed elements and length to a null-proto object (preserving
security: no Array.prototype.constructor chain exposure)
- attaches a safe Symbol.iterator backed by a null-proto iterator whose
.next() results are also null-proto objects — no host-realm chain at any
level, so the iterator cannot be used to reach the host Function constructor
After this change:
const entries = await fs.promises.readdir('.')
for (const e of entries) { ... } // works
const arr = [...entries] // works; arr is a real sandbox Array
const [first] = entries // works
entries.length / entries[0] // works (unchanged)
Array.isArray(entries) // still false (by design, host Array is unsafe)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plugin-workflow-javascript): json round-trip script return value before postMessage
The script node result is always stored in the database as JSON and re-parsed
by subsequent workflow nodes, so JavaScript-specific semantics (null prototypes,
Symbols, functions) carry no meaning at the postMessage boundary.
Add toJsonResult() that applies JSON.parse(JSON.stringify(value)) to the final
script return value before postMessage, ensuring:
- null-prototype objects produced by sanitizeForSandbox are converted to proper
plain objects / Arrays (Array.isArray works correctly in downstream nodes)
- function-valued properties are silently dropped (expected for serialized data)
- the result is always structured-clone compatible
Note: sanitizeForSandbox + Symbol.iterator are still required for values used
within the script during execution (e.g. iterating a module-returned array
before returning the result). toJsonResult() only normalises the boundary value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plugin-workflow-javascript): fix security issues
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>