Skip to content

Security

Masterplanner25 edited this page Sep 25, 2026 · 6 revisions

Security

Supported Versions

Security fixes are applied to the latest stable version only.

Version Supported
v5.15.x (latest) ✅ Yes
v5.0.x – v5.14.x ⚠️ Upgrade first — see the note below
v4.x and earlier ❌ No

Two 5.x releases to skip. 5.0.3 breaks nodus-sdk at construction (fixed in 5.0.4) and 5.7.0 makes nodus check reject correct code (fixed in 5.7.1).

Anything embedding a capability policy should be on 5.6.0 or later: before it, a policy could be bypassed by writing the async form of a call.

Reporting a Vulnerability

If you discover a security vulnerability, do not open a public GitHub issue.

Instead:

  1. Email the maintainer, or
  2. Open a private security report via GitHub's private vulnerability reporting

What to Include

  • Description of the vulnerability
  • Steps to reproduce (minimal .nd script if applicable)
  • Potential impact
  • Suggested mitigation if known

The default posture changed in v5.0.0

An embedded NodusRuntime denies subprocess, network and environment access by default. Advice written against v4.x — including "a bare runtime can shell out" — is now backwards. Grant explicitly:

NodusRuntime(allow_subprocess=True, allow_network=True)

allowed_paths was already a jail on the current working directory and still is.

nodus run and the rest of the CLI are deliberately unaffected, and a test pins both halves so the asymmetry is not tidied away by a later reader. What deny-by-default protects is work you did not fully author; a developer running a script they just wrote is not that.

nodus serve is confined too, since v5.10.0 — code submitted to the server cannot run subprocesses, open sockets or read the environment unless granted with --allow-subprocess / --allow-network / --allow-env, narrowed by --allowed-commands / --allowed-hosts.

Since v5.13.0 the filesystem is confined as well. Submitted code is jailed to the server's working directory; --allow-paths widens or narrows it. Before that it could read and write anywhere the server process could — and --allow-paths, the documented mitigation, compared roots it had not normalised and so refused every path including the ones it was given. If you run nodus serve on v5.12.0 or earlier, treat it as having no filesystem boundary.

Since v5.15.0 a resume cannot exceed the bounds the host set. A workflow that parks at workflow_wait and resumes runs on a derived VM, and through v5.14.0 that VM was given no limits at all — not the step budget, not the wall-clock deadline, not the memory ceiling, and a tighter frame cap reverted to the default. So a guest escaped every bound by parking and resuming, and the caller's own deadline did not catch it: a deadline is only consulted every 100 instructions, and a program doing a handful of resume_workflow calls never executes 100 more. Measured against v5.14.0 — 8 resumes and roughly 800 ms of work inside a 300 ms budget, and about 270,000 instructions inside a budget of 5,000 (#873). Ceilings are inherited now, and the step budget is passed down as the caller's remainder and charged back when the resume returns, so repeated resumes accumulate rather than each receiving a fresh allowance.

Also in v5.15.0: a module function's agent_call no longer reaches the process-global agent registry. A NodusRuntime given its own agent_registry isolates agents per tenant (#185), and crossing into a module function undid it — the same call answered from the tenant's registry at top level and from the shared one inside a module. If you run more than one tenant in a process and your tools call agents from module code, upgrade (#868).

Since v5.14.0 the execution budget is bounded and settable. Every program submitted to the server runs under a wall-clock budget; nodus serve --time-limit SECS sets the ceiling, and a request may pass timeout_ms to ask for less — never more. Through v5.13.0 the budget was the CLI's 200 ms with no way to raise it, and a step that exceeded it under the server did not time out: it hung the request forever, so while (true) in a workflow step was a one-line denial of service against any nodus serve instance (#862). A breach now returns ok: false with Execution timed out. If you run nodus serve on v5.13.0 or earlier, do not accept workflow submissions from callers you do not trust to terminate.

What Qualifies as a Security Issue

Issue Type Report As
Arbitrary code execution through the runtime Security vulnerability
Sandbox escape (bypassing allowed_paths, step limits, etc.) Security vulnerability
Unsafe filesystem access bypassing the allowlist Security vulnerability
VM state corruption Security vulnerability
Unsafe interpreter behavior (memory, stack) Security vulnerability
General crashes and incorrect behavior Normal GitHub issue
Feature requests Normal GitHub issue

Responsible Disclosure Process

Once a vulnerability is reported:

  1. It will be investigated by the maintainer
  2. A fix will be developed
  3. A patch release will be published
  4. Contributors who report vulnerabilities responsibly will be credited in the release notes where appropriate

Security Philosophy

Nodus prioritizes:

  • Predictable runtime behavior — explicit execution semantics, no hidden side effects
  • Explicit sandboxing — sandbox limits (steps, time, stdout, filesystem, input, call stack) are enforced by the runtime and configurable by the host
  • Minimal attack surface — the language core is small; advanced capabilities live in runtime services that can be restricted or omitted

Sandbox Controls

When embedding Nodus, configure appropriate limits for untrusted code:

from nodus import NodusRuntime

rt = NodusRuntime(
    max_steps=50_000,              # instruction count cap
    timeout_ms=2000,               # wall-clock timeout
    max_stdout_chars=10_000,       # output cap
    allowed_paths=["/safe/dir"],   # filesystem allowlist
    allow_input=False,             # disallow input()
    max_frames=100,                # call stack depth cap
)

A sandbox error is raised as err.kind = "sandbox" inside scripts and as RuntimeLimitExceeded in the Python host.

One remaining gap is a memory limit — the amount of data a script can allocate in its VM state. All other sandbox dimensions (steps, time, stdout, filesystem, input, call stack) are enforced.

Runtime Trust Model

  • Embedded mode — the host controls all sandbox parameters; untrusted scripts should always run with limits configured
  • CLI mode — nodus run --step-limit N --time-limit N --allow-paths <paths> for sandboxed execution from the command line
  • Server mode — nodus serve --auth-token <token> requires an Authorization header on all HTTP requests; --allow-paths restricts filesystem access; --time-limit SECS bounds each submitted program (v5.14.0)

Known Considerations

Because Nodus executes code, careful review of runtime and VM behavior is important when accepting scripts from untrusted sources. Always:

  • Set max_steps and timeout_ms to prevent runaway scripts
  • Set allowed_paths to prevent unauthorized filesystem access
  • Set allow_input=False to prevent scripts from reading stdin
  • Set max_frames to prevent stack overflow from pathological recursion

Clone this wiki locally