# Risk map — n8n-io/n8n

> Canonical page: https://codevetter.com/unpack/n8n/risk-map

# Risk map — n8n-io/n8n

Scanned commit 1a370b965c1fe438dfa58f371a01080eb9cca776 (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Security-sensitive surface: community-package npm installs shell out via execFile; credential secrecy relies on Cipher key handling and N8N_ENCRYPTION_KEY; expression evaluation and JS/Python task runners execute user code (Tournament sandbox + prototype hardening + spawned processes); webhook/form routes intentionally serve user HTML outside the CSP middleware. The 3.1k-line WorkflowExecute and the multi-stage Start.init ordering are blast-radius hotspots, as are Redis pub/sub trust and the Postgres migration advisory lock.
- Community packages install third-party code at runtime: community-packages.service uses execFile/executeNpmCommand (npm pack/install) against registry.npmjs.org by default; verifyIntegrity and a vetted-package list mitigate but the attack surface (supply chain, postinstall) is inherent. — [packages/cli/src/modules/community-packages/community-packages.service.ts#L18-49](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/modules/community-packages/community-packages.service.ts#L18-L49) [packages/cli/src/modules/community-packages/npm-utils.ts](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/modules/community-packages/npm-utils.ts)
- LoadNodesAndCredentials mutates global module resolution: it rewrites NODE_PATH and calls node's private module._initPaths() so community packages resolve — fragile across Node upgrades and a code-loading vector for anything on NODE_PATH. — [packages/cli/src/load-nodes-and-credentials.ts#L78-101](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/load-nodes-and-credentials.ts#L78-L101)
- Credential encryption depends on the instance key: Cipher.decryptV2 throws UnexpectedError('Encryption key not found') for unknown keyIds and silently treats unprefixed data as legacy CBC; wrong/missing N8N_ENCRYPTION_KEY renders all stored credentials unreadable. — [packages/core/src/encryption/cipher.ts#L96-135](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/core/src/encryption/cipher.ts#L96-L135) [packages/core/src/encryption/cipher.ts#L20](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/core/src/encryption/cipher.ts#L20)
- WorkflowExecute is a 3168-line stateful class; its run() must remain non-async to preserve PCancelable cancellation (explicit comment), meaning a well-meaning refactor breaks execution cancellation. — [packages/core/src/execution-engine/workflow-execute.ts#L138-150](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/core/src/execution-engine/workflow-execute.ts#L138-L150)
- Webhook/form routes are registered before bodyParser AND before the CSP middleware on purpose — user-authored HTML responses must not get the instance CSP; the comment warns that reordering 'silently changes' the security posture. — [packages/cli/src/server.ts#L212-227](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/server.ts#L212-L227) [packages/cli/src/abstract-server.ts#L241-295](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/abstract-server.ts#L241-L295)
- Preset-credentials endpoint (/endpointPresetCredentials) can be configured without the overwrite middleware; in that case credentials can only be set once per boot — a misconfiguration could leave a credential-injection route unauthenticated. — [packages/cli/src/server.ts#L294-347](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/server.ts#L294-L347)
- Expression evaluation is user-controlled code: Tournament evaluator + expressionSandboxHooks exist to contain it; any escape in the proxy/hooks layer is direct RCE inside the n8n process. (inferred) — [packages/workflow/src/expression-evaluator-proxy.ts](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/workflow/src/expression-evaluator-proxy.ts) [packages/workflow/src/expression-sandboxing.ts#L568-572](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/workflow/src/expression-sandboxing.ts#L568-L572)
- JS task runners spawn 'node' subprocesses (JsTaskRunnerProcess.startProcess) with env-injected grant tokens; N8N_RUNNERS_INSECURE_MODE exists in config, and runners authenticate to a broker port — misconfiguration widens code-exec blast radius. — [packages/cli/src/task-runners/task-runner-process-js.ts#L34-53](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/task-runners/task-runner-process-js.ts#L34-L53) [packages/@n8n/config/src/configs/runners.config.ts#L24-89](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/%40n8n/config/src/configs/runners.config.ts#L24-L89) [packages/@n8n/task-runner/src/js-task-runner/prototype-hardening.ts](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/%40n8n/task-runner/src/js-task-runner/prototype-hardening.ts)
- Auth: session cookie is a JWT (AUTH_COOKIE_NAME) bound to a browserId header and MFA-gated; ApiKeyAuthStrategy is evaluated before cookie auth so a wrong explicit key fails fast; revocation uses InvalidAuthToken entity. — [packages/cli/src/auth/auth.service.ts](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/auth/auth.service.ts) [packages/cli/src/server.ts#L191-201](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/server.ts#L191-L201)
- Postgres migrations run inside an advisory lock scoped by schema+prefix hash with transaction:'none' — all pending migrations commit or roll back as one unit, so a mid-migration crash can leave a partially migrated schema for every co-starting instance. — [packages/@n8n/db/src/connection/db-connection.ts#L152-185](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/%40n8n/db/src/connection/db-connection.ts#L152-L185)
- Redis is a trust boundary in queue mode: commands flow over pub/sub channels to workers and webhooks relay through the same fabric; a compromised Redis can drive executions on all mains/workers. (inferred) — [packages/cli/src/commands/start.ts#L329-358](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/commands/start.ts#L329-L358) [packages/cli/src/scaling/pubsub/subscriber.service.ts](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/scaling/pubsub/subscriber.service.ts) [packages/cli/src/commands/worker.ts#L183-192](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/commands/worker.ts#L183-L192)
- Startup ordering is contractually fragile: entities must be registered before DataSource creation (loadModules), PubSubRegistry.init() must be re-invoked after modules/community-packages register @OnPubSubEvent handlers, and JwtService/BinaryDataConfig init require DeploymentKeyRepository — comments flag each of these ordering constraints. — [packages/@n8n/backend-common/src/modules/module-registry.ts#L96-120](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/%40n8n/backend-common/src/modules/module-registry.ts#L96-L120) [packages/cli/src/commands/start.ts#L260-264](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/commands/start.ts#L260-L264) [packages/cli/src/commands/start.ts#L455-462](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/commands/start.ts#L455-L462)
- External secrets feature pulls secrets from AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, 1Password (SDK deps present); provider credentials and sync are handled in modules/external-secrets.ee — a leak here exposes all downstream credentials. — [packages/cli/package.json#L133-146](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/package.json#L133-L146) [packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts](https://github.com/n8n-io/n8n/blob/1a370b965c1fe438dfa58f371a01080eb9cca776/packages/cli/src/modules/external-secrets.ee/external-secrets-manager.ee.ts)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
