- Only treat imports as unrestricted when BOTH allowlists are wildcards, which is
the condition the runner's own analyzer short-circuits on. With one wildcard the
runner still checks the other category, and classifying a module needs Python's
stdlib list, so the linter now abstains explicitly instead of implying the
import is fine.
- Render the two allowlists as separate categories in the prompt and the lint
message. Merging them presented a module filed under the wrong one as
importable, though the runner rejects it.
- Stop claiming "this deployment allowlists nothing" when no policy was supplied.
The sandbox CLI cannot see the configuration, and now says so.
- Report a wildcard combined with named modules as a misconfiguration rather than
as a permissive allowlist. The runner refuses to start on it, so the previous
reading told the builder its imports were fine when no Python could run at all.
- Only trust the policy in the build-time check when it is authoritative. In
external runner mode the runner may be stricter than n8n thinks, so a
non-authoritative policy must not silence a warning.
- Skip a stale `pythonCode` body on a node whose language is JavaScript.
- Replace a `toContain('re')` assertion that also matched "runner" in the
surrounding prompt text, and correct the fixture's path to its Python checker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
n8n now parses the same allowlist the Python runner enforces, so the two can
drift: the runner's suite and n8n's unit tests could both stay green while the
builder describes a policy that is not applied.
- Add a shared fixture, read by a test on each side, pinning how
N8N_RUNNERS_STDLIB_ALLOW / N8N_RUNNERS_EXTERNAL_ALLOW parse. It caught a real
divergence on the first run: the runner returns a set and collapses
duplicates, n8n's parser did not. Fixed.
- Add an integration test that starts the Python runner for real and drives a
Code node through the broker, covering config -> spawned runner env ->
enforcement in one path: import-free code runs, an allowlisted import runs, a
non-allowlisted one is rejected, and the rejection names the configured list
(so it cannot pass against an empty default).
The integration test needs the runner's virtualenv and skips without it, which
is most Node CI lanes today; verified it skips rather than fails. Making
`getVenvPath` public is what lets it decide that synchronously.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Co-authored-by: yehorkardash <yehor.kardash@n8n.io>
Co-authored-by: James Gee <1285296+geemanjs@users.noreply.github.com>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Stephen Wright <sjw948@gmail.com>
Co-authored-by: oleg <me@olegivaniv.com>
Co-authored-by: Albert Alises <albert.alises@gmail.com>
Co-authored-by: Danny Martini <danny@n8n.io>