@n8n/backend-network 1.11.0 → 1.12.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.
- package/README.md +53 -41
- package/dist/build.tsbuildinfo +1 -1
- package/dist/http/client-options.d.ts +2 -2
- package/dist/http/env-proxy-http-agent.js +1 -1
- package/dist/http/env-proxy-http-agent.js.map +1 -1
- package/dist/http/env-proxy-https-agent.js +1 -1
- package/dist/http/env-proxy-https-agent.js.map +1 -1
- package/dist/http/index.d.ts +7 -9
- package/dist/http/index.js +2 -2
- package/dist/http/index.js.map +1 -1
- package/dist/http/outbound-http.d.ts +7 -3
- package/dist/http/outbound-http.js +15 -4
- package/dist/http/outbound-http.js.map +1 -1
- package/dist/http/use-default-ssrf-policy.d.ts +1 -0
- package/dist/http/use-default-ssrf-policy.js +3 -0
- package/dist/http/use-default-ssrf-policy.js.map +1 -0
- package/dist/testing.d.ts +2 -1
- package/dist/testing.js +4 -1
- package/dist/testing.js.map +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ const data = await client.request({ method: 'GET', url: 'https://api.example.com
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
`HttpRequestClientOptions` carry policy that applies to every call on the
|
|
42
|
-
client —
|
|
42
|
+
client — the `useDefaultSsrfPolicy`, a `baseURL`, and default `headers`. Set them once at
|
|
43
43
|
creation instead of repeating them per request:
|
|
44
44
|
|
|
45
45
|
```ts
|
|
@@ -58,70 +58,82 @@ dictated by what the consuming library accepts.
|
|
|
58
58
|
|
|
59
59
|
## SSRF protection
|
|
60
60
|
|
|
61
|
-
###
|
|
61
|
+
### Safe by default
|
|
62
62
|
|
|
63
|
-
Both `requests()` and `transport()`
|
|
64
|
-
|
|
63
|
+
Both `requests()` and `transport()` guard every call — and every redirect hop —
|
|
64
|
+
by default. You do not pass a bridge, a service, or a config flag; the default
|
|
65
|
+
`useDefaultSsrfPolicy: 'safe'` is applied for you. The only way to skip the guard is to
|
|
66
|
+
opt out explicitly with `useDefaultSsrfPolicy: 'unsafe'`.
|
|
65
67
|
|
|
66
68
|
This default is deliberate. Outbound HTTP in n8n is frequently driven by
|
|
67
69
|
user-controlled input — credential URLs, workflow parameters, redirect targets
|
|
68
70
|
returned by a remote server. Without guarding, that input can be pointed at
|
|
69
71
|
internal-only addresses (cloud metadata endpoints, `localhost`, private ranges),
|
|
70
72
|
turning the n8n backend into a confused deputy (a Server-Side Request Forgery,
|
|
71
|
-
or SSRF).
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
or SSRF). Because the secure default lives in the factory rather than at each
|
|
74
|
+
call site, forgetting to add protection cannot silently introduce a
|
|
75
|
+
vulnerability — the unsafe choice is the explicit one, and a new call site is
|
|
76
|
+
safe unless someone consciously writes `useDefaultSsrfPolicy: 'unsafe'`.
|
|
77
|
+
|
|
78
|
+
### `enabled` is resolved inside `OutboundHttp`, not by callers
|
|
79
|
+
|
|
80
|
+
`useDefaultSsrfPolicy` answers one question: *is this destination trusted enough to skip
|
|
81
|
+
the guard?* Only the calling code knows that, so it is decided per call. Whether
|
|
82
|
+
the guard actually runs for a `'safe'` call is a separate, instance-wide
|
|
83
|
+
decision that `OutboundHttp` resolves internally from
|
|
84
|
+
`SsrfProtectionConfig.enabled` (`N8N_SSRF_PROTECTION_ENABLED`). Callers neither
|
|
85
|
+
read that flag nor inject the `SsrfProtectionService` — passing `'safe'` (or
|
|
86
|
+
nothing) is enough.
|
|
87
|
+
|
|
88
|
+
Concretely, a `'safe'` call is guarded when the instance enables protection and
|
|
89
|
+
passes through untouched when it does not; an `'enforced'` call is always
|
|
90
|
+
guarded and an `'unsafe'` call is never guarded, regardless of the flag.
|
|
91
|
+
|
|
92
|
+
`SsrfProtectionConfig` (env-driven) also configures *how* the guard behaves once
|
|
93
|
+
it runs — the blocked/allowed IP ranges (`N8N_SSRF_BLOCKED_IP_RANGES`,
|
|
91
94
|
`N8N_SSRF_ALLOWED_IP_RANGES`), the allowed and blocked hostnames
|
|
92
95
|
(`N8N_SSRF_ALLOWED_HOSTNAMES`, `N8N_SSRF_BLOCKED_HOSTNAMES`), and the DNS-cache
|
|
93
|
-
size.
|
|
96
|
+
size.
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
### Choosing a safety mode: when to opt out
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Classify the **destination**, then pick:
|
|
100
|
+
Leave the default `'safe'` unless the destination genuinely cannot be
|
|
101
|
+
user-controlled. Classify the **destination**, then pick:
|
|
100
102
|
|
|
101
103
|
| Destination | Risk | What to pass |
|
|
102
104
|
| --- | --- | --- |
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
105
|
+
| User- or remote-controlled URL (workflow import URL, credential/OAuth URLs, a discovery document's second hop, a user-supplied registry) | **High** — attacker-influenceable | nothing — the default `'safe'` guards it when the instance enables protection |
|
|
106
|
+
| URL an autonomous component picks on its own (an LLM/web-research target) | **High** — attacker-influenceable, and the operator never sees the URL | `useDefaultSsrfPolicy: 'enforced'` — guarded even when the instance leaves protection off |
|
|
107
|
+
| Fixed n8n-owned host, or a fixed public vendor API (Slack, Linear, npm registry default, AWS service endpoint) | **Low** — not user-controllable | `useDefaultSsrfPolicy: 'unsafe'` + a one-line "fixed host" comment |
|
|
108
|
+
| Admin-configured infrastructure that may legitimately be internal (SAML/OIDC IdP, OTLP collector, log-streaming destination, external-secrets manager) | **Low–medium** — operator-trusted | `useDefaultSsrfPolicy: 'unsafe'` + a "may point at internal X" comment. The `N8N_SSRF_ALLOWED_*` allowlists are the alternative when the instance runs with protection globally on. |
|
|
106
109
|
|
|
107
110
|
```ts
|
|
108
|
-
//
|
|
109
|
-
const client = this.outboundHttp.requests(
|
|
111
|
+
// HIGH risk — `url` comes from user input. Guarded by default.
|
|
112
|
+
const client = this.outboundHttp.requests();
|
|
110
113
|
|
|
111
|
-
//
|
|
112
|
-
const client = this.outboundHttp.requests({
|
|
113
|
-
ssrf: this.ssrfConfig.enabled ? this.ssrfProtectionService : 'disabled',
|
|
114
|
-
});
|
|
114
|
+
// LOW risk — fixed, n8n-owned host. Opt out explicitly.
|
|
115
|
+
const client = this.outboundHttp.requests({ useDefaultSsrfPolicy: 'unsafe' });
|
|
115
116
|
```
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
what a security reviewer reads. Reference
|
|
119
|
-
`packages/cli/src/oauth/oauth.service.ts` (config-gated high-risk) and
|
|
118
|
+
Whenever you opt out, **write a one-line comment stating why** — that comment is
|
|
119
|
+
what a security reviewer reads. Reference implementation:
|
|
120
120
|
`packages/nodes-base/credentials/common/token-request.ts`, which lifts the
|
|
121
121
|
low/high-risk choice into the type system with a
|
|
122
122
|
`'fixed-vendor' | 'user-controlled'` parameter so the caller is forced to
|
|
123
123
|
classify the destination.
|
|
124
124
|
|
|
125
|
+
### The node execution path
|
|
126
|
+
|
|
127
|
+
Node request helpers (`this.helpers.httpRequest`,
|
|
128
|
+
`httpRequestWithAuthentication`, and the deprecated `request`) go through
|
|
129
|
+
`OutboundHttp.requests()` with the default safe mode. `IHttpRequestOptions`
|
|
130
|
+
exposes no `useDefaultSsrfPolicy` field, so node code — community nodes included — cannot
|
|
131
|
+
opt out of the instance's policy.
|
|
132
|
+
|
|
133
|
+
The raw `httpRequest` function is not exported from the package barrel. It is
|
|
134
|
+
reachable only through the `@n8n/backend-network/testing` entry point, so
|
|
135
|
+
production code cannot reach an unguarded request function.
|
|
136
|
+
|
|
125
137
|
## The boundary rule
|
|
126
138
|
|
|
127
139
|
The `n8n-local-rules/no-uncentralized-http` ESLint rule enforces this.
|