mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-29 01:39:24 +08:00
build: Move SSRF protection into @n8n/backend-network package (#32070)
Co-authored-by: Emilia <100027345+sovietspaceship@users.noreply.github.com>
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export { DnsResolver } from './dns-resolver';
|
||||
export { InMemoryDnsCache } from './in-memory-dns-cache.service';
|
||||
@@ -1 +1,2 @@
|
||||
export {};
|
||||
export * from './dns';
|
||||
export * from './ssrf';
|
||||
|
||||
+15
-15
@@ -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]),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export { SsrfProtectionService } from './ssrf-protection.service';
|
||||
export type { SsrfBridge } from './ssrf-protection.service';
|
||||
export { SsrfBlockedIpError } from './ssrf-blocked-ip.error';
|
||||
+1
-1
@@ -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';
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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:*",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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.
|
||||
|
||||
+1
-1
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
+2
-4
@@ -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<SsrfBridge> {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:*",
|
||||
|
||||
@@ -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.
|
||||
|
||||
+2
-2
@@ -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';
|
||||
|
||||
+1
-2
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-2
@@ -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,
|
||||
|
||||
+1
-2
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
@@ -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" },
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -869,6 +869,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/backend-network": "workspace:*",
|
||||
"@n8n/client-oauth2": "workspace:*",
|
||||
"@n8n/eslint-plugin-community-nodes": "workspace:*",
|
||||
"@n8n/playwright-janitor": "workspace:*",
|
||||
|
||||
@@ -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,
|
||||
|
||||
Generated
+33
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user