From 10f398dd5604e5f376a5b7db9d850f07a91ef55e Mon Sep 17 00:00:00 2001 From: Lorent Lempereur Date: Thu, 11 Jun 2026 16:23:51 +0200 Subject: [PATCH] build: Move SSRF protection into @n8n/backend-network package (#32070) Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com> --- packages/@n8n/backend-network/package.json | 16 ++++-- .../src/dns}/__tests__/dns-resolver.test.ts | 0 .../in-memory-dns-cache.service.test.ts | 0 .../backend-network/src/dns}/dns-resolver.ts | 0 .../src/dns}/in-memory-dns-cache.service.ts | 0 .../@n8n/backend-network/src/dns/index.ts | 2 + packages/@n8n/backend-network/src/index.ts | 3 +- .../ssrf/__tests__/hostname-matcher.test.ts | 0 .../ssrf/__tests__/ip-range-builder.test.ts | 0 .../__tests__/ssrf-protection.service.test.ts | 30 +++++----- .../src/ssrf/hostname-matcher.ts | 0 .../@n8n/backend-network/src/ssrf/index.ts | 3 + .../src/ssrf/ip-range-builder.ts | 0 .../src/ssrf/ssrf-blocked-ip.error.ts | 0 .../src/ssrf/ssrf-protection.service.ts | 2 +- packages/@n8n/backend-network/tsconfig.json | 8 ++- packages/cli/package.json | 1 + .../dns-cache-metrics.service.test.ts | 2 +- .../__tests__/ssrf-metrics.service.test.ts | 2 +- .../prometheus/dns-cache-metrics.service.ts | 2 +- .../prometheus/ssrf-metrics.service.ts | 2 +- ...stance-ai.adapter.service.security.test.ts | 2 +- .../instance-ai.adapter.service.ts | 3 +- .../instance-ai/instance-ai.service.ts | 3 +- .../__tests__/fetch-and-extract.test.ts | 6 +- .../web-research/fetch-and-extract.ts | 4 +- .../ai-workflow-builder.service.test.ts | 2 +- .../services/ai-workflow-builder.service.ts | 3 +- .../src/workflow-execute-additional-data.ts | 55 ++++++++++--------- .../__tests__/workflows.controller.test.ts | 4 +- .../cli/src/workflows/workflows.controller.ts | 2 +- packages/core/package.json | 1 + packages/core/src/execution-engine/index.ts | 29 +++++----- .../utils/__tests__/ssrf-integration.test.ts | 4 +- .../__tests__/http-request.test.ts | 3 +- .../__tests__/legacy-request-adapter.test.ts | 2 +- .../utils/request-helpers/axios-utils.ts | 2 +- .../utils/request-helpers/http-request.ts | 3 +- .../request-helpers/legacy-request-adapter.ts | 3 +- packages/core/src/index.ts | 1 - packages/core/src/ssrf/index.ts | 7 --- packages/core/tsconfig.json | 1 + packages/core/vite.config.ts | 11 ++++ packages/nodes-base/package.json | 1 + .../nodes-base/test/nodes/TriggerHelpers.ts | 3 +- pnpm-lock.yaml | 33 +++++++++++ 46 files changed, 160 insertions(+), 101 deletions(-) rename packages/{core/src/ssrf => @n8n/backend-network/src/dns}/__tests__/dns-resolver.test.ts (100%) rename packages/{core/src/ssrf => @n8n/backend-network/src/dns}/__tests__/in-memory-dns-cache.service.test.ts (100%) rename packages/{core/src/ssrf => @n8n/backend-network/src/dns}/dns-resolver.ts (100%) rename packages/{core/src/ssrf => @n8n/backend-network/src/dns}/in-memory-dns-cache.service.ts (100%) create mode 100644 packages/@n8n/backend-network/src/dns/index.ts rename packages/{core => @n8n/backend-network}/src/ssrf/__tests__/hostname-matcher.test.ts (100%) rename packages/{core => @n8n/backend-network}/src/ssrf/__tests__/ip-range-builder.test.ts (100%) rename packages/{core => @n8n/backend-network}/src/ssrf/__tests__/ssrf-protection.service.test.ts (96%) rename packages/{core => @n8n/backend-network}/src/ssrf/hostname-matcher.ts (100%) create mode 100644 packages/@n8n/backend-network/src/ssrf/index.ts rename packages/{core => @n8n/backend-network}/src/ssrf/ip-range-builder.ts (100%) rename packages/{core => @n8n/backend-network}/src/ssrf/ssrf-blocked-ip.error.ts (100%) rename packages/{core => @n8n/backend-network}/src/ssrf/ssrf-protection.service.ts (99%) delete mode 100644 packages/core/src/ssrf/index.ts diff --git a/packages/@n8n/backend-network/package.json b/packages/@n8n/backend-network/package.json index 37a4981e8fb..45a1d64adf5 100644 --- a/packages/@n8n/backend-network/package.json +++ b/packages/@n8n/backend-network/package.json @@ -12,8 +12,8 @@ "lint": "eslint . --quiet", "lint:fix": "eslint . --fix", "watch": "tsc -p tsconfig.build.json --watch", - "test": "vitest run --passWithNoTests", - "test:unit": "vitest run --passWithNoTests", + "test": "vitest run", + "test:unit": "vitest run", "test:dev": "vitest --silent=false" }, "main": "dist/index.js", @@ -23,13 +23,21 @@ "dist/**/*" ], "dependencies": { - "@n8n/backend-common": "workspace:*" + "@n8n/backend-common": "workspace:*", + "@n8n/config": "workspace:*", + "@n8n/constants": "workspace:*", + "@n8n/di": "workspace:*", + "cache-manager": "catalog:", + "n8n-workflow": "workspace:*", + "reflect-metadata": "catalog:", + "zod": "catalog:" }, "devDependencies": { "@n8n/typescript-config": "workspace:*", "@n8n/vitest-config": "workspace:*", "@vitest/coverage-v8": "catalog:", - "vitest": "catalog:" + "vitest": "catalog:", + "vitest-mock-extended": "catalog:" }, "license": "LicenseRef-n8n-sustainable-use" } diff --git a/packages/core/src/ssrf/__tests__/dns-resolver.test.ts b/packages/@n8n/backend-network/src/dns/__tests__/dns-resolver.test.ts similarity index 100% rename from packages/core/src/ssrf/__tests__/dns-resolver.test.ts rename to packages/@n8n/backend-network/src/dns/__tests__/dns-resolver.test.ts diff --git a/packages/core/src/ssrf/__tests__/in-memory-dns-cache.service.test.ts b/packages/@n8n/backend-network/src/dns/__tests__/in-memory-dns-cache.service.test.ts similarity index 100% rename from packages/core/src/ssrf/__tests__/in-memory-dns-cache.service.test.ts rename to packages/@n8n/backend-network/src/dns/__tests__/in-memory-dns-cache.service.test.ts diff --git a/packages/core/src/ssrf/dns-resolver.ts b/packages/@n8n/backend-network/src/dns/dns-resolver.ts similarity index 100% rename from packages/core/src/ssrf/dns-resolver.ts rename to packages/@n8n/backend-network/src/dns/dns-resolver.ts diff --git a/packages/core/src/ssrf/in-memory-dns-cache.service.ts b/packages/@n8n/backend-network/src/dns/in-memory-dns-cache.service.ts similarity index 100% rename from packages/core/src/ssrf/in-memory-dns-cache.service.ts rename to packages/@n8n/backend-network/src/dns/in-memory-dns-cache.service.ts diff --git a/packages/@n8n/backend-network/src/dns/index.ts b/packages/@n8n/backend-network/src/dns/index.ts new file mode 100644 index 00000000000..1367c42cc9c --- /dev/null +++ b/packages/@n8n/backend-network/src/dns/index.ts @@ -0,0 +1,2 @@ +export { DnsResolver } from './dns-resolver'; +export { InMemoryDnsCache } from './in-memory-dns-cache.service'; diff --git a/packages/@n8n/backend-network/src/index.ts b/packages/@n8n/backend-network/src/index.ts index cb0ff5c3b54..f21b0d32e81 100644 --- a/packages/@n8n/backend-network/src/index.ts +++ b/packages/@n8n/backend-network/src/index.ts @@ -1 +1,2 @@ -export {}; +export * from './dns'; +export * from './ssrf'; diff --git a/packages/core/src/ssrf/__tests__/hostname-matcher.test.ts b/packages/@n8n/backend-network/src/ssrf/__tests__/hostname-matcher.test.ts similarity index 100% rename from packages/core/src/ssrf/__tests__/hostname-matcher.test.ts rename to packages/@n8n/backend-network/src/ssrf/__tests__/hostname-matcher.test.ts diff --git a/packages/core/src/ssrf/__tests__/ip-range-builder.test.ts b/packages/@n8n/backend-network/src/ssrf/__tests__/ip-range-builder.test.ts similarity index 100% rename from packages/core/src/ssrf/__tests__/ip-range-builder.test.ts rename to packages/@n8n/backend-network/src/ssrf/__tests__/ip-range-builder.test.ts diff --git a/packages/core/src/ssrf/__tests__/ssrf-protection.service.test.ts b/packages/@n8n/backend-network/src/ssrf/__tests__/ssrf-protection.service.test.ts similarity index 96% rename from packages/core/src/ssrf/__tests__/ssrf-protection.service.test.ts rename to packages/@n8n/backend-network/src/ssrf/__tests__/ssrf-protection.service.test.ts index 865bce9e231..843bd5d6e07 100644 --- a/packages/core/src/ssrf/__tests__/ssrf-protection.service.test.ts +++ b/packages/@n8n/backend-network/src/ssrf/__tests__/ssrf-protection.service.test.ts @@ -3,7 +3,7 @@ import { SsrfProtectionConfig } from '@n8n/config'; import type { LookupAddress } from 'node:dns'; import { mock } from 'vitest-mock-extended'; -import type { DnsResolver } from '../dns-resolver'; +import type { DnsResolver } from '../../dns'; import { SsrfBlockedIpError } from '../ssrf-blocked-ip.error'; import { SsrfProtectionService } from '../ssrf-protection.service'; @@ -317,8 +317,8 @@ describe('SsrfProtectionService', () => { const [error, address, family] = await new Promise< [Error | null, string, number | undefined] >((resolve) => - lookup('example.com', { all: false }, (err, addr, fam) => - resolve([err, addr as string, fam]), + lookup('example.com', { all: false }, (lookupError, addr, fam) => + resolve([lookupError, addr as string, fam]), ), ); @@ -355,8 +355,8 @@ describe('SsrfProtectionService', () => { const lookup = service.createSecureLookup(); const [error, addresses] = await new Promise<[Error | null, LookupAddress[]]>((resolve) => - lookup('multi.example.com', { all: true }, (err, addrs) => - resolve([err, addrs as LookupAddress[]]), + lookup('multi.example.com', { all: true }, (lookupError, addrs) => + resolve([lookupError, addrs as LookupAddress[]]), ), ); @@ -377,8 +377,8 @@ describe('SsrfProtectionService', () => { const [error, address, family] = await new Promise< [Error | null, string, number | undefined] >((resolve) => - lookup('dualstack.example.com', { all: false, family: 6 }, (err, addr, fam) => - resolve([err, addr as string, fam]), + lookup('dualstack.example.com', { all: false, family: 6 }, (lookupError, addr, fam) => + resolve([lookupError, addr as string, fam]), ), ); @@ -401,8 +401,8 @@ describe('SsrfProtectionService', () => { const [error, address, family] = await new Promise< [Error | null, string, number | undefined] >((resolve) => - lookup('ipv4-only.example.com', { all: false, family: 6 }, (err, addr, fam) => - resolve([err, addr as string, fam]), + lookup('ipv4-only.example.com', { all: false, family: 6 }, (lookupError, addr, fam) => + resolve([lookupError, addr as string, fam]), ), ); @@ -430,8 +430,8 @@ describe('SsrfProtectionService', () => { const lookup = service.createSecureLookup(); const [error, address] = await new Promise<[Error | null, string]>((resolve) => - lookup('api.internal.n8n.io', { all: false }, (err, addr) => - resolve([err, addr as string]), + lookup('api.internal.n8n.io', { all: false }, (lookupError, addr) => + resolve([lookupError, addr as string]), ), ); @@ -449,8 +449,8 @@ describe('SsrfProtectionService', () => { const [error, address, family] = await new Promise< [Error | null, string, number | undefined] >((resolve) => - lookup('failing.example.com', { all: false }, (err, addr, fam) => - resolve([err, addr as string, fam]), + lookup('failing.example.com', { all: false }, (lookupError, addr, fam) => + resolve([lookupError, addr as string, fam]), ), ); @@ -469,8 +469,8 @@ describe('SsrfProtectionService', () => { const [error, addresses, family] = await new Promise< [Error | null, LookupAddress[], number | undefined] >((resolve) => - lookup('failing.example.com', { all: true }, (err, addrs, fam) => - resolve([err, addrs as LookupAddress[], fam]), + lookup('failing.example.com', { all: true }, (lookupError, addrs, fam) => + resolve([lookupError, addrs as LookupAddress[], fam]), ), ); diff --git a/packages/core/src/ssrf/hostname-matcher.ts b/packages/@n8n/backend-network/src/ssrf/hostname-matcher.ts similarity index 100% rename from packages/core/src/ssrf/hostname-matcher.ts rename to packages/@n8n/backend-network/src/ssrf/hostname-matcher.ts diff --git a/packages/@n8n/backend-network/src/ssrf/index.ts b/packages/@n8n/backend-network/src/ssrf/index.ts new file mode 100644 index 00000000000..91e0dfa9058 --- /dev/null +++ b/packages/@n8n/backend-network/src/ssrf/index.ts @@ -0,0 +1,3 @@ +export { SsrfProtectionService } from './ssrf-protection.service'; +export type { SsrfBridge } from './ssrf-protection.service'; +export { SsrfBlockedIpError } from './ssrf-blocked-ip.error'; diff --git a/packages/core/src/ssrf/ip-range-builder.ts b/packages/@n8n/backend-network/src/ssrf/ip-range-builder.ts similarity index 100% rename from packages/core/src/ssrf/ip-range-builder.ts rename to packages/@n8n/backend-network/src/ssrf/ip-range-builder.ts diff --git a/packages/core/src/ssrf/ssrf-blocked-ip.error.ts b/packages/@n8n/backend-network/src/ssrf/ssrf-blocked-ip.error.ts similarity index 100% rename from packages/core/src/ssrf/ssrf-blocked-ip.error.ts rename to packages/@n8n/backend-network/src/ssrf/ssrf-blocked-ip.error.ts diff --git a/packages/core/src/ssrf/ssrf-protection.service.ts b/packages/@n8n/backend-network/src/ssrf/ssrf-protection.service.ts similarity index 99% rename from packages/core/src/ssrf/ssrf-protection.service.ts rename to packages/@n8n/backend-network/src/ssrf/ssrf-protection.service.ts index 469aa7d1b00..ff4b30de4d9 100644 --- a/packages/core/src/ssrf/ssrf-protection.service.ts +++ b/packages/@n8n/backend-network/src/ssrf/ssrf-protection.service.ts @@ -7,7 +7,7 @@ import type { LookupAddress, LookupOptions } from 'node:dns'; import type { BlockList, LookupFunction } from 'node:net'; import { isIP } from 'node:net'; -import { DnsResolver } from './dns-resolver'; +import { DnsResolver } from '../dns'; import { HostnameMatcher } from './hostname-matcher'; import { buildIpRangeList } from './ip-range-builder'; import { SsrfBlockedIpError } from './ssrf-blocked-ip.error'; diff --git a/packages/@n8n/backend-network/tsconfig.json b/packages/@n8n/backend-network/tsconfig.json index a1c9e44a1a7..a831509d190 100644 --- a/packages/@n8n/backend-network/tsconfig.json +++ b/packages/@n8n/backend-network/tsconfig.json @@ -7,5 +7,11 @@ "emitDecoratorMetadata": true }, "include": ["src/**/*.ts"], - "references": [{ "path": "../backend-common/tsconfig.build.json" }] + "references": [ + { "path": "../../workflow/tsconfig.build.cjs.json" }, + { "path": "../backend-common/tsconfig.build.json" }, + { "path": "../config/tsconfig.build.json" }, + { "path": "../constants/tsconfig.build.json" }, + { "path": "../di/tsconfig.build.json" } + ] } diff --git a/packages/cli/package.json b/packages/cli/package.json index 0d6a64222a7..3a7e23e7852 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -118,6 +118,7 @@ "@n8n/ai-workflow-builder": "workspace:*", "@n8n/api-types": "workspace:*", "@n8n/backend-common": "workspace:*", + "@n8n/backend-network": "workspace:*", "@n8n/chat-hub": "workspace:*", "@n8n/client-oauth2": "workspace:*", "@n8n/config": "workspace:*", diff --git a/packages/cli/src/metrics/prometheus/__tests__/dns-cache-metrics.service.test.ts b/packages/cli/src/metrics/prometheus/__tests__/dns-cache-metrics.service.test.ts index d07134c00c9..dac2ed15046 100644 --- a/packages/cli/src/metrics/prometheus/__tests__/dns-cache-metrics.service.test.ts +++ b/packages/cli/src/metrics/prometheus/__tests__/dns-cache-metrics.service.test.ts @@ -1,6 +1,6 @@ +import type { InMemoryDnsCache } from '@n8n/backend-network'; import { mockInstance } from '@n8n/backend-test-utils'; import { PrometheusMetricsConfig, SsrfProtectionConfig } from '@n8n/config'; -import type { InMemoryDnsCache } from 'n8n-core'; import promClient from 'prom-client'; import { PrometheusDnsCacheMetricsService } from '../dns-cache-metrics.service'; diff --git a/packages/cli/src/metrics/prometheus/__tests__/ssrf-metrics.service.test.ts b/packages/cli/src/metrics/prometheus/__tests__/ssrf-metrics.service.test.ts index 58730ac31df..91bc7d2c59d 100644 --- a/packages/cli/src/metrics/prometheus/__tests__/ssrf-metrics.service.test.ts +++ b/packages/cli/src/metrics/prometheus/__tests__/ssrf-metrics.service.test.ts @@ -1,6 +1,6 @@ +import type { SsrfProtectionService } from '@n8n/backend-network'; import { mockInstance } from '@n8n/backend-test-utils'; import { PrometheusMetricsConfig, SsrfProtectionConfig } from '@n8n/config'; -import type { SsrfProtectionService } from 'n8n-core'; import promClient from 'prom-client'; import { PrometheusSsrfMetricsService } from '../ssrf-metrics.service'; diff --git a/packages/cli/src/metrics/prometheus/dns-cache-metrics.service.ts b/packages/cli/src/metrics/prometheus/dns-cache-metrics.service.ts index 0cede578f63..78a9ba315f5 100644 --- a/packages/cli/src/metrics/prometheus/dns-cache-metrics.service.ts +++ b/packages/cli/src/metrics/prometheus/dns-cache-metrics.service.ts @@ -1,6 +1,6 @@ +import { InMemoryDnsCache } from '@n8n/backend-network'; import { PrometheusMetricsConfig, SsrfProtectionConfig } from '@n8n/config'; import { Service } from '@n8n/di'; -import { InMemoryDnsCache } from 'n8n-core'; import promClient from 'prom-client'; import type { PrometheusMetricsCollector } from './base'; diff --git a/packages/cli/src/metrics/prometheus/ssrf-metrics.service.ts b/packages/cli/src/metrics/prometheus/ssrf-metrics.service.ts index d31d236ffde..23442df8c0b 100644 --- a/packages/cli/src/metrics/prometheus/ssrf-metrics.service.ts +++ b/packages/cli/src/metrics/prometheus/ssrf-metrics.service.ts @@ -1,10 +1,10 @@ import { PrometheusMetricsConfig, SsrfProtectionConfig } from '@n8n/config'; import { Service } from '@n8n/di'; -import { SsrfProtectionService } from 'n8n-core'; import promClient from 'prom-client'; import type { PrometheusMetricsCollector } from './base'; import { DURATION_BUCKETS_SECONDS } from './constant'; +import { SsrfProtectionService } from '@n8n/backend-network'; /** * Tracks SSRF check results as counters and duration as a histogram. diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts index 0c9e0d56b5f..01ab17e82c5 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.adapter.service.security.test.ts @@ -56,7 +56,7 @@ import type { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee' import type { ExecutionPersistence } from '@/executions/execution-persistence'; import type { EventService } from '@/events/event.service'; import type { RoleService } from '@/services/role.service'; -import type { SsrfProtectionService } from 'n8n-core'; +import type { SsrfProtectionService } from '@n8n/backend-network'; import type { Telemetry } from '@/telemetry'; jest.mock('@/permissions.ee/check-access'); diff --git a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts index 059201feec3..acc9048d408 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.adapter.service.ts @@ -63,6 +63,7 @@ import { WorkflowRepository, } from '@n8n/db'; import { Logger } from '@n8n/backend-common'; +import { SsrfProtectionService } from '@n8n/backend-network'; import { Container, Service } from '@n8n/di'; import { hasGlobalScope, PROJECT_OWNER_ROLE_SLUG, type Scope } from '@n8n/permissions'; // eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import @@ -117,7 +118,7 @@ import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters import { FolderService } from '@/services/folder.service'; import { ProjectService } from '@/services/project.service.ee'; import { RoleService } from '@/services/role.service'; -import { InstanceSettings, SsrfProtectionService } from 'n8n-core'; +import { InstanceSettings } from 'n8n-core'; import { TagService } from '@/services/tag.service'; import { WorkflowFinderService } from '@/workflows/workflow-finder.service'; import { WorkflowHistoryService } from '@/workflows/workflow-history/workflow-history.service'; diff --git a/packages/cli/src/modules/instance-ai/instance-ai.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.service.ts index c657363a8bb..68dad120cbc 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.service.ts @@ -87,7 +87,7 @@ import { ThreadTaskStorage, } from '@n8n/instance-ai'; import { setSchemaBaseDirs } from '@n8n/workflow-sdk'; -import { ErrorReporter, InstanceSettings, SsrfProtectionService } from 'n8n-core'; +import { ErrorReporter, InstanceSettings } from 'n8n-core'; import { OperationalError, UnexpectedError, UserError } from 'n8n-workflow'; import { nanoid } from 'nanoid'; import type * as Undici from 'undici'; @@ -139,6 +139,7 @@ import { AiService } from '@/services/ai.service'; import { ProxyTokenManager } from '@/services/proxy-token-manager'; import { UrlService } from '@/services/url.service'; import { Telemetry } from '@/telemetry'; +import { SsrfProtectionService } from '@n8n/backend-network'; function getErrorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); diff --git a/packages/cli/src/modules/instance-ai/web-research/__tests__/fetch-and-extract.test.ts b/packages/cli/src/modules/instance-ai/web-research/__tests__/fetch-and-extract.test.ts index 3c96ed6b188..de7f4988e9b 100644 --- a/packages/cli/src/modules/instance-ai/web-research/__tests__/fetch-and-extract.test.ts +++ b/packages/cli/src/modules/instance-ai/web-research/__tests__/fetch-and-extract.test.ts @@ -1,13 +1,11 @@ import type { Logger } from '@n8n/backend-common'; +import type { DnsResolver, SsrfBridge } from '@n8n/backend-network'; +import { SsrfProtectionService } from '@n8n/backend-network'; import { SsrfProtectionConfig } from '@n8n/config'; import { mock } from 'jest-mock-extended'; -import type { SsrfBridge } from 'n8n-core'; import { createResultOk } from 'n8n-workflow'; import type { LookupFunction } from 'node:net'; -import type { DnsResolver } from 'n8n-core'; -import { SsrfProtectionService } from 'n8n-core'; - import { fetchAndExtract } from '../fetch-and-extract'; function createSsrfMock(): jest.Mocked { diff --git a/packages/cli/src/modules/instance-ai/web-research/fetch-and-extract.ts b/packages/cli/src/modules/instance-ai/web-research/fetch-and-extract.ts index aefad5bb49e..afd866eced9 100644 --- a/packages/cli/src/modules/instance-ai/web-research/fetch-and-extract.ts +++ b/packages/cli/src/modules/instance-ai/web-research/fetch-and-extract.ts @@ -1,11 +1,11 @@ import type * as JoplinTurndownGfm from '@joplin/turndown-plugin-gfm'; import type { Readability as TReadability } from '@mozilla/readability'; +import type * as ReadabilityMod from '@mozilla/readability'; +import type { SsrfBridge } from '@n8n/backend-network'; import type { FetchedPage } from '@n8n/instance-ai'; import type * as LinkedomMod from 'linkedom'; import type { parseHTML as TParseHtml } from 'linkedom'; -import type { SsrfBridge } from 'n8n-core'; import type TTurndownService from 'turndown'; -import type * as ReadabilityMod from '@mozilla/readability'; import type * as TurndownMod from 'turndown'; import { Agent } from 'undici'; diff --git a/packages/cli/src/services/__tests__/ai-workflow-builder.service.test.ts b/packages/cli/src/services/__tests__/ai-workflow-builder.service.test.ts index 1fa98af7f10..8d54bf94275 100644 --- a/packages/cli/src/services/__tests__/ai-workflow-builder.service.test.ts +++ b/packages/cli/src/services/__tests__/ai-workflow-builder.service.test.ts @@ -14,7 +14,7 @@ import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import type { Push } from '@/push'; import { WorkflowBuilderService } from '@/services/ai-workflow-builder.service'; import type { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; -import type { SsrfProtectionService } from 'n8n-core'; +import type { SsrfProtectionService } from '@n8n/backend-network'; import type { UrlService } from '@/services/url.service'; import type { Telemetry } from '@/telemetry'; import type { WorkflowBuilderSessionRepository } from '@/modules/workflow-builder'; diff --git a/packages/cli/src/services/ai-workflow-builder.service.ts b/packages/cli/src/services/ai-workflow-builder.service.ts index b046afe911d..0556c8af98d 100644 --- a/packages/cli/src/services/ai-workflow-builder.service.ts +++ b/packages/cli/src/services/ai-workflow-builder.service.ts @@ -2,6 +2,7 @@ import { AiWorkflowBuilderService, createPassthroughSsrfGuard } from '@n8n/ai-wo import type { ResourceLocatorCallbackFactory } from '@n8n/ai-workflow-builder'; import { ChatPayload } from '@n8n/ai-workflow-builder/dist/workflow-builder-agent'; import { Logger } from '@n8n/backend-common'; +import { SsrfProtectionService } from '@n8n/backend-network'; import { GlobalConfig, SsrfProtectionConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import { AiAssistantClient } from '@n8n_io/ai-assistant-sdk'; @@ -21,7 +22,7 @@ import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { WorkflowBuilderSessionRepository } from '@/modules/workflow-builder'; import { Push } from '@/push'; import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; -import { InstanceSettings, SsrfProtectionService } from 'n8n-core'; +import { InstanceSettings } from 'n8n-core'; import { UrlService } from '@/services/url.service'; import { Telemetry } from '@/telemetry'; import { getBase } from '@/workflow-execute-additional-data'; diff --git a/packages/cli/src/workflow-execute-additional-data.ts b/packages/cli/src/workflow-execute-additional-data.ts index 66b228fc05e..3aa7b5ad044 100644 --- a/packages/cli/src/workflow-execute-additional-data.ts +++ b/packages/cli/src/workflow-execute-additional-data.ts @@ -2,44 +2,45 @@ import type { PushMessage, PushType } from '@n8n/api-types'; import { Logger, ModuleRegistry } from '@n8n/backend-common'; +import { SsrfProtectionService } from '@n8n/backend-network'; import { ExecutionsConfig, GlobalConfig, SsrfProtectionConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; import { ExecutionRepository, WorkflowRepository } from '@n8n/db'; -import { Container } from '@n8n/di'; import type { ServiceIdentifier } from '@n8n/di'; +import { Container } from '@n8n/di'; import type { JSONSchema7 } from 'json-schema'; -import { ExternalSecretsProxy, SsrfProtectionService, WorkflowExecute } from 'n8n-core'; +import { ExternalSecretsProxy, WorkflowExecute } from 'n8n-core'; +import type { + AiEvent, + EnvProviderState, + ExecuteAgentData, + ExecuteWorkflowData, + ExecuteWorkflowOptions, + ExecutionError, + ExecutionStatus, + IDataObject, + IExecuteData, + IExecuteFunctions, + IExecuteWorkflowInfo, + INode, + INodeExecutionData, + INodeParameters, + IRun, + IRunExecutionData, + ITaskDataConnections, + IWorkflowBase, + IWorkflowExecuteAdditionalData, + IWorkflowExecutionDataProcess, + IWorkflowSettings, + RelatedExecution, + WorkflowExecuteMode, +} from 'n8n-workflow'; import { UnexpectedError, Workflow, createRunExecutionData, mergeRunsPerBranch, } from 'n8n-workflow'; -import type { - AiEvent, - IDataObject, - IExecuteData, - IExecuteWorkflowInfo, - INode, - INodeExecutionData, - INodeParameters, - IWorkflowBase, - IWorkflowExecuteAdditionalData, - IWorkflowSettings, - WorkflowExecuteMode, - ExecutionStatus, - ExecutionError, - IExecuteFunctions, - ITaskDataConnections, - ExecuteWorkflowOptions, - IWorkflowExecutionDataProcess, - EnvProviderState, - ExecuteWorkflowData, - ExecuteAgentData, - RelatedExecution, - IRun, - IRunExecutionData, -} from 'n8n-workflow'; import { ActiveExecutions } from '@/active-executions'; import { CredentialsHelper } from '@/credentials-helper'; diff --git a/packages/cli/src/workflows/__tests__/workflows.controller.test.ts b/packages/cli/src/workflows/__tests__/workflows.controller.test.ts index 914a34f1f83..9b86f540bbf 100644 --- a/packages/cli/src/workflows/__tests__/workflows.controller.test.ts +++ b/packages/cli/src/workflows/__tests__/workflows.controller.test.ts @@ -1,5 +1,7 @@ import type { ImportWorkflowFromUrlDto } from '@n8n/api-types'; import type { Logger } from '@n8n/backend-common'; +import type { SsrfProtectionService } from '@n8n/backend-network'; +import { SsrfBlockedIpError } from '@n8n/backend-network'; import type { SsrfProtectionConfig } from '@n8n/config'; import type { AuthenticatedRequest, IExecutionResponse } from '@n8n/db'; import axios from 'axios'; @@ -13,8 +15,6 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ForbiddenError } from '@/errors/response-errors/forbidden.error'; import type { ExecutionService } from '@/executions/execution.service'; import type { ProjectService } from '@/services/project.service.ee'; -import { SsrfBlockedIpError } from 'n8n-core'; -import type { SsrfProtectionService } from 'n8n-core'; jest.mock('axios'); diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 1b57b1abc70..5fd95b33fa8 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -60,9 +60,9 @@ import { AuthService } from '@/auth/auth.service'; import * as ResponseHelper from '@/response-helper'; import { NamingService } from '@/services/naming.service'; import { ProjectService } from '@/services/project.service.ee'; -import { SsrfBlockedIpError, SsrfProtectionService } from 'n8n-core'; import { UserManagementMailer } from '@/user-management/email'; import * as utils from '@/utils'; +import { SsrfBlockedIpError, SsrfProtectionService } from '@n8n/backend-network'; @RestController('/workflows') export class WorkflowsController { diff --git a/packages/core/package.json b/packages/core/package.json index 6ba86b992c3..513053fb71c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -55,6 +55,7 @@ "@aws-sdk/client-s3": "3.808.0", "@langchain/core": "catalog:", "@n8n/backend-common": "workspace:*", + "@n8n/backend-network": "workspace:*", "@n8n/client-oauth2": "workspace:*", "@n8n/config": "workspace:*", "@n8n/constants": "workspace:*", diff --git a/packages/core/src/execution-engine/index.ts b/packages/core/src/execution-engine/index.ts index ded61645acc..c45870773fa 100644 --- a/packages/core/src/execution-engine/index.ts +++ b/packages/core/src/execution-engine/index.ts @@ -1,3 +1,4 @@ +import type { SsrfBridge } from '@n8n/backend-network'; import type { DataTableProxyProvider, DynamicCredentialCheckProxyProvider, @@ -8,8 +9,6 @@ import type { OauthJweProxyProvider, } from 'n8n-workflow'; -import type { SsrfBridge } from '@/ssrf'; - import type { ExecutionLifecycleHooks } from './execution-lifecycle-hooks'; import type { ExternalSecretsProxy } from './external-secrets-proxy'; @@ -85,24 +84,24 @@ declare module 'n8n-workflow' { } export * from './active-workflow-triggers'; -export * from './scheduled-task-manager'; -export type * from './interfaces'; -export * from './routing-node'; -export * from './node-execution-context'; -export * from './partial-execution-utils'; -export * from './node-execution-context/utils/execution-metadata'; -export * from './workflow-execute'; -export * from './execution-context-hook-registry.service'; -export { ExecutionLifecycleHooks } from './execution-lifecycle-hooks'; -export { ExternalSecretsProxy, type IExternalSecretsManager } from './external-secrets-proxy'; -export { ExecutionContextService } from './execution-context.service'; -export { establishExecutionContext } from './execution-context'; -export { isEngineRequest } from './requests-response'; export { synthesizeBinaryFixture, type FixtureSizeHint, type SynthesizeBinaryFixtureOptions, } from './eval-mock-fixtures'; +export { establishExecutionContext } from './execution-context'; +export * from './execution-context-hook-registry.service'; +export { ExecutionContextService } from './execution-context.service'; +export { ExecutionLifecycleHooks } from './execution-lifecycle-hooks'; +export { ExternalSecretsProxy, type IExternalSecretsManager } from './external-secrets-proxy'; +export type * from './interfaces'; +export * from './node-execution-context'; +export * from './node-execution-context/utils/execution-metadata'; +export * from './partial-execution-utils'; +export { isEngineRequest } from './requests-response'; +export * from './routing-node'; +export * from './scheduled-task-manager'; +export * from './workflow-execute'; // Exposed so eval-mode credential helpers (e.g. `EvalMockedCredentialsHelper`) // can reuse the same schema-driven cred synthesizer the wire-server URL // rewrite expects. See its `getDecrypted` catch path for the consumer. diff --git a/packages/core/src/execution-engine/node-execution-context/utils/__tests__/ssrf-integration.test.ts b/packages/core/src/execution-engine/node-execution-context/utils/__tests__/ssrf-integration.test.ts index 475554b2db7..3c6f2326dbf 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/__tests__/ssrf-integration.test.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/__tests__/ssrf-integration.test.ts @@ -1,4 +1,6 @@ import type { Logger } from '@n8n/backend-common'; +import type { DnsResolver, SsrfBridge } from '@n8n/backend-network'; +import { SsrfProtectionService } from '@n8n/backend-network'; import { SsrfProtectionConfig } from '@n8n/config'; import type { IHttpRequestOptions, @@ -13,8 +15,6 @@ import type { MockProxy } from 'vitest-mock-extended'; import { mock } from 'vitest-mock-extended'; import type { ExecutionLifecycleHooks } from '@/execution-engine/execution-lifecycle-hooks'; -import type { DnsResolver, SsrfBridge } from '@/ssrf'; -import { SsrfProtectionService } from '@/ssrf'; import { getRequestHelperFunctions } from '../request-helper-functions'; import { httpRequest } from '../request-helpers/http-request'; diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/http-request.test.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/http-request.test.ts index 960f9f376d5..e1dc09003ba 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/http-request.test.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/http-request.test.ts @@ -1,3 +1,4 @@ +import type { SsrfBridge } from '@n8n/backend-network'; import { AiConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import FormData from 'form-data'; @@ -6,8 +7,6 @@ import type { IHttpRequestMethods, IHttpRequestOptions, IRequestOptions } from ' import nock from 'nock'; import { mock } from 'vitest-mock-extended'; -import type { SsrfBridge } from '@/ssrf'; - // Imported for side effects: sets axios defaults and registers the vendor-header interceptor import '../axios-config'; import { diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/legacy-request-adapter.test.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/legacy-request-adapter.test.ts index 0386acf6c10..ce4a8f9dc4f 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/legacy-request-adapter.test.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/__tests__/legacy-request-adapter.test.ts @@ -1,3 +1,4 @@ +import type { SsrfBridge } from '@n8n/backend-network'; import FormData from 'form-data'; import type { Agent as HttpsAgent } from 'https'; import type { @@ -12,7 +13,6 @@ import type { SecureContextOptions } from 'tls'; import { mock } from 'vitest-mock-extended'; import type { ExecutionLifecycleHooks } from '@/execution-engine/execution-lifecycle-hooks'; -import type { SsrfBridge } from '@/ssrf'; import { parseRequestObject, proxyRequestToAxios } from '../legacy-request-adapter'; diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/axios-utils.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/axios-utils.ts index 9d003a9bcba..1311cbf694a 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/axios-utils.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/axios-utils.ts @@ -1,4 +1,5 @@ import { Logger } from '@n8n/backend-common'; +import type { SsrfBridge } from '@n8n/backend-network'; import { Container } from '@n8n/di'; import type { AxiosRequestConfig, AxiosResponse } from 'axios'; import axios from 'axios'; @@ -13,7 +14,6 @@ import { } from 'n8n-workflow'; import { createHttpProxyAgent, createHttpsProxyAgent } from '@/http-proxy'; -import type { SsrfBridge } from '@/ssrf'; export function throwIfDomainNotAllowed( configOrUrl: AxiosRequestConfig | string, diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/http-request.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/http-request.ts index 2b019fcd853..a9afb1faa0b 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/http-request.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/http-request.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ +import type { SsrfBridge } from '@n8n/backend-network'; import type { AxiosRequestConfig } from 'axios'; import axios from 'axios'; import type { AgentOptions } from 'https'; @@ -13,8 +14,6 @@ import type { import { isObjectEmpty } from 'n8n-workflow'; import { stringify } from 'qs'; -import type { SsrfBridge } from '@/ssrf'; - import { buildTargetUrl, digestAuthAxiosConfig, diff --git a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.ts b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.ts index d6490704267..397bb6ee17e 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.ts @@ -7,6 +7,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Logger } from '@n8n/backend-common'; +import type { SsrfBridge } from '@n8n/backend-network'; import { Container } from '@n8n/di'; import type { AxiosHeaders, AxiosRequestConfig } from 'axios'; import crypto from 'crypto'; @@ -25,8 +26,6 @@ import { NodeSslError } from 'n8n-workflow'; import { stringify } from 'qs'; import { Readable } from 'stream'; -import type { SsrfBridge } from '@/ssrf'; - import { binaryToString } from '../binary-helper-functions'; import { parseIncomingMessage } from '../parse-incoming-message'; import { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 65a4f1b00bf..c227a8bae15 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -8,7 +8,6 @@ export * from './data-deduplication-service'; export * from './encryption'; export * from './errors'; export * from './execution-engine'; -export * from './ssrf'; export * from './html-sandbox'; export * from './instance-settings'; export * from './nodes-loader'; diff --git a/packages/core/src/ssrf/index.ts b/packages/core/src/ssrf/index.ts deleted file mode 100644 index 7a2c85afe10..00000000000 --- a/packages/core/src/ssrf/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { DnsResolver } from './dns-resolver'; -export type { DnsLookupOptions } from './dns-resolver'; -export { InMemoryDnsCache } from './in-memory-dns-cache.service'; -export type { DnsCacheEventMap } from './in-memory-dns-cache.service'; -export { SsrfBlockedIpError } from './ssrf-blocked-ip.error'; -export { SsrfProtectionService } from './ssrf-protection.service'; -export type { SsrfBridge, SsrfCheckResult, SsrfEventMap } from './ssrf-protection.service'; diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 32d0383c268..d477f4d23fe 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -20,6 +20,7 @@ { "path": "../workflow/tsconfig.build.esm.json" }, { "path": "../@n8n/decorators/tsconfig.build.json" }, { "path": "../@n8n/backend-common/tsconfig.build.json" }, + { "path": "../@n8n/backend-network/tsconfig.build.json" }, { "path": "../@n8n/config/tsconfig.build.json" }, { "path": "../@n8n/constants/tsconfig.build.json" }, { "path": "../@n8n/di/tsconfig.build.json" }, diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts index 3aa65505a45..619e5ee57b7 100644 --- a/packages/core/vite.config.ts +++ b/packages/core/vite.config.ts @@ -23,6 +23,17 @@ export default mergeConfig( find: /^zod$/, replacement: require.resolve('zod'), }, + // `n8n-workflow` has dual ESM/CJS builds (`./dist/esm/index.js` for `import`, + // `./dist/cjs/index.js` for `require`), each with its own `UserError` class. + // `@n8n/backend-network` is loaded from its CJS dist and `require`s the CJS copy, + // so `SsrfBlockedIpError extends UserError` (CJS), while test files ESM-import + // `UserError` (ESM) — and `instanceof` fails between them. Pin the top-level + // `n8n-workflow` import to the CJS file so both code paths share one module + // instance. `require.resolve` follows the `require` export condition. + { + find: /^n8n-workflow$/, + replacement: require.resolve('n8n-workflow'), + }, ], }, oxc: { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a473dbd3005..c52a692bf33 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -869,6 +869,7 @@ ] }, "devDependencies": { + "@n8n/backend-network": "workspace:*", "@n8n/client-oauth2": "workspace:*", "@n8n/eslint-plugin-community-nodes": "workspace:*", "@n8n/playwright-janitor": "workspace:*", diff --git a/packages/nodes-base/test/nodes/TriggerHelpers.ts b/packages/nodes-base/test/nodes/TriggerHelpers.ts index ee076d66eaa..33441a02031 100644 --- a/packages/nodes-base/test/nodes/TriggerHelpers.ts +++ b/packages/nodes-base/test/nodes/TriggerHelpers.ts @@ -4,8 +4,9 @@ import { mock } from 'jest-mock-extended'; import get from 'lodash/get'; import merge from 'lodash/merge'; import set from 'lodash/set'; +import type { SsrfBridge } from '@n8n/backend-network'; import { PollContext, returnJsonArray } from 'n8n-core'; -import type { InstanceSettings, ExecutionLifecycleHooks, SsrfBridge } from 'n8n-core'; +import type { InstanceSettings, ExecutionLifecycleHooks } from 'n8n-core'; import { ScheduledTaskManager } from 'n8n-core/dist/execution-engine/scheduled-task-manager'; import { createDeferredPromise, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a2c708cadd..f7d014b468b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1141,6 +1141,27 @@ importers: '@n8n/backend-common': specifier: workspace:* version: link:../backend-common + '@n8n/config': + specifier: workspace:* + version: link:../config + '@n8n/constants': + specifier: workspace:* + version: link:../constants + '@n8n/di': + specifier: workspace:* + version: link:../di + cache-manager: + specifier: 'catalog:' + version: 5.2.3 + n8n-workflow: + specifier: workspace:* + version: link:../../workflow + reflect-metadata: + specifier: 'catalog:' + version: 0.2.2 + zod: + specifier: 3.25.67 + version: 3.25.67 devDependencies: '@n8n/typescript-config': specifier: workspace:* @@ -1154,6 +1175,9 @@ importers: vitest: specifier: 'catalog:' version: 4.1.1(@opentelemetry/api@1.9.0)(@types/node@20.19.41)(@vitest/browser-playwright@4.0.16)(jsdom@23.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(vite@8.0.2(@types/node@20.19.41)(esbuild@0.25.10)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3)) + vitest-mock-extended: + specifier: 'catalog:' + version: 3.1.0(typescript@6.0.2)(vitest@4.1.1(@opentelemetry/api@1.9.0)(@types/node@20.19.41)(jsdom@23.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(vite@8.0.2(@types/node@20.19.41)(esbuild@0.25.10)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))) packages/@n8n/backend-test-utils: dependencies: @@ -3077,6 +3101,9 @@ importers: '@n8n/backend-common': specifier: workspace:* version: link:../@n8n/backend-common + '@n8n/backend-network': + specifier: workspace:* + version: link:../@n8n/backend-network '@n8n/chat-hub': specifier: workspace:* version: link:../@n8n/chat-hub @@ -3549,6 +3576,9 @@ importers: '@n8n/backend-common': specifier: workspace:* version: link:../@n8n/backend-common + '@n8n/backend-network': + specifier: workspace:* + version: link:../@n8n/backend-network '@n8n/client-oauth2': specifier: workspace:* version: link:../@n8n/client-oauth2 @@ -4985,6 +5015,9 @@ importers: specifier: 3.1.0 version: 3.1.0 devDependencies: + '@n8n/backend-network': + specifier: workspace:* + version: link:../@n8n/backend-network '@n8n/client-oauth2': specifier: workspace:* version: link:../@n8n/client-oauth2