From a63b31931fc21e561f2c96a51c1274508c323dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Tue, 10 Feb 2026 10:16:51 +0100 Subject: [PATCH] Extract SecureHttpClientService into its own module (#17828) ## Summary - **Extract `SecureHttpClientService`** from `tool` module into a dedicated `core-modules/secure-http-client/` module with proper NestJS module encapsulation - **Fix module hygiene**: 12 modules that incorrectly listed `SecureHttpClientService` as a direct provider now properly import `SecureHttpClientModule` - **Add structured logging** for outbound HTTP requests with workspace/user context (for GuardDuty alert correlation) - **Rename type files** to follow one-export-per-file convention (`get-secure-axios-adapter.types.ts` -> `secure-adapter-dependencies.type.ts`, new `outbound-request-context.type.ts` / `outbound-request-source.type.ts`) ### Why `SecureHttpClientService` is a cross-cutting concern (used by auth, captcha, file upload, geo-map, telemetry, admin-panel, REST API, contact creation, webhooks, and workflow tools) but was bundled inside the `tool` module. Most consumers worked around this by listing it as a direct provider instead of importing a module, which is fragile and not idiomatic NestJS. ## Test plan - [x] All 60 unit tests pass (`secure-http-client.service.spec.ts`, `get-secure-axios-adapter.util.spec.ts`, `is-private-ip.util.spec.ts`) - [x] Related module tests pass (admin-panel, contact-creation, tool) - [x] `npx nx typecheck twenty-server` passes - [x] `npx nx lint:diff-with-main twenty-server` passes - [x] Server compiles and bootstraps successfully Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor --- .../api/rest/core/rest-api-core.module.ts | 4 +- .../src/engine/api/rest/rest-api.module.ts | 5 +- .../src/engine/api/rest/rest-api.service.ts | 2 +- .../__tests__/admin-panel.service.spec.ts | 2 +- .../admin-panel/admin-panel.module.ts | 4 +- .../admin-panel/admin-panel.service.ts | 2 +- .../engine/core-modules/auth/auth.module.ts | 4 +- .../auth/services/google-apis-scopes.ts | 2 +- .../auth/services/sign-in-up.service.ts | 2 +- .../core-modules/captcha/captcha.module.ts | 6 +- .../file/file-upload/file-upload.module.ts | 6 +- .../services/file-upload.service.ts | 2 +- .../engine/core-modules/file/file.module.ts | 4 +- .../core-modules/geo-map/geo-map-module.ts | 6 +- .../geo-map/services/geo-map.service.ts | 2 +- .../secure-http-client.service.spec.ts | 120 ++++++++++++++++++ .../outbound-request-context.type.ts | 7 + .../outbound-request-source.type.ts | 4 + .../secure-http-client.module.ts | 9 ++ .../secure-http-client.service.ts | 51 ++++++++ .../get-secure-axios-adapter.util.spec.ts | 4 +- .../__tests__/is-private-ip.util.spec.ts | 2 +- .../utils/get-secure-axios-adapter.util.ts | 8 +- .../utils/is-private-ip.util.ts | 0 .../secure-adapter-dependencies.type.ts} | 0 .../telemetry/telemetry.module.ts | 5 +- .../telemetry/telemetry.service.ts | 2 +- .../services/secure-http-client.service.ts | 28 ---- .../engine/core-modules/tool/tool.module.ts | 12 +- .../code-interpreter-tool.ts | 2 +- .../tool/tools/http-tool/http-tool.ts | 13 +- .../search-help-center-tool.ts | 2 +- .../webhook/jobs/call-webhook.job.ts | 11 +- .../webhook/jobs/webhook-job.module.ts | 4 +- .../contact-creation-manager.module.ts | 4 +- .../__tests__/create-company.service.spec.ts | 2 +- .../services/create-company.service.ts | 2 +- 37 files changed, 260 insertions(+), 85 deletions(-) create mode 100644 packages/twenty-server/src/engine/core-modules/secure-http-client/__tests__/secure-http-client.service.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-context.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-source.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.module.ts create mode 100644 packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.service.ts rename packages/twenty-server/src/engine/core-modules/{tool => secure-http-client}/utils/__tests__/get-secure-axios-adapter.util.spec.ts (98%) rename packages/twenty-server/src/engine/core-modules/{tool => secure-http-client}/utils/__tests__/is-private-ip.util.spec.ts (97%) rename packages/twenty-server/src/engine/core-modules/{tool => secure-http-client}/utils/get-secure-axios-adapter.util.ts (91%) rename packages/twenty-server/src/engine/core-modules/{tool => secure-http-client}/utils/is-private-ip.util.ts (100%) rename packages/twenty-server/src/engine/core-modules/{tool/utils/get-secure-axios-adapter.types.ts => secure-http-client/utils/secure-adapter-dependencies.type.ts} (100%) delete mode 100644 packages/twenty-server/src/engine/core-modules/tool/services/secure-http-client.service.ts diff --git a/packages/twenty-server/src/engine/api/rest/core/rest-api-core.module.ts b/packages/twenty-server/src/engine/api/rest/core/rest-api-core.module.ts index 8853686b5e..4e06dd73d4 100644 --- a/packages/twenty-server/src/engine/api/rest/core/rest-api-core.module.ts +++ b/packages/twenty-server/src/engine/api/rest/core/rest-api-core.module.ts @@ -21,12 +21,12 @@ import { restToCommonArgsHandlers } from 'src/engine/api/rest/core/rest-to-commo import { RestApiCoreService } from 'src/engine/api/rest/core/services/rest-api-core.service'; import { RestApiService } from 'src/engine/api/rest/rest-api.service'; import { ActorModule } from 'src/engine/core-modules/actor/actor.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module'; import { AuthModule } from 'src/engine/core-modules/auth/auth.module'; import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { RecordTransformerModule } from 'src/engine/core-modules/record-transformer/record-transformer.module'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; @@ -66,12 +66,12 @@ const restApiCoreResolvers = [ CoreCommonApiModule, WorkspaceDomainsModule, WorkspaceCacheModule, + SecureHttpClientModule, ], controllers: [RestApiCoreController], providers: [ RestApiService, RestApiCoreService, - SecureHttpClientService, ...restApiCoreResolvers, ...restToCommonArgsHandlers, ], diff --git a/packages/twenty-server/src/engine/api/rest/rest-api.module.ts b/packages/twenty-server/src/engine/api/rest/rest-api.module.ts index 08123785a5..5deb9c9c58 100644 --- a/packages/twenty-server/src/engine/api/rest/rest-api.module.ts +++ b/packages/twenty-server/src/engine/api/rest/rest-api.module.ts @@ -3,7 +3,7 @@ import { Module } from '@nestjs/common'; import { MetadataQueryBuilderModule } from 'src/engine/api/rest/metadata/query-builder/metadata-query-builder.module'; import { RestApiMetadataService } from 'src/engine/api/rest/metadata/rest-api-metadata.service'; import { AuthModule } from 'src/engine/core-modules/auth/auth.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; import { RestApiCoreModule } from 'src/engine/api/rest/core/rest-api-core.module'; import { RestApiService } from 'src/engine/api/rest/rest-api.service'; @@ -17,9 +17,10 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi AuthModule, RestApiCoreModule, PermissionsModule, + SecureHttpClientModule, ], controllers: [RestApiMetadataController], - providers: [RestApiService, RestApiMetadataService, SecureHttpClientService], + providers: [RestApiService, RestApiMetadataService], exports: [RestApiMetadataService, RestApiService], }) export class RestApiModule {} diff --git a/packages/twenty-server/src/engine/api/rest/rest-api.service.ts b/packages/twenty-server/src/engine/api/rest/rest-api.service.ts index 7cac7a61b9..5992aa217c 100644 --- a/packages/twenty-server/src/engine/api/rest/rest-api.service.ts +++ b/packages/twenty-server/src/engine/api/rest/rest-api.service.ts @@ -5,7 +5,7 @@ import { type AxiosResponse } from 'axios'; import { type Query } from 'src/engine/api/rest/core/types/query.type'; import { RestApiException } from 'src/engine/api/rest/errors/RestApiException'; import { type RequestContext } from 'src/engine/api/rest/types/RequestContext'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; export enum GraphqlApiType { CORE = 'core', diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts index 11a029ff86..24db2aefed 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts @@ -6,7 +6,7 @@ import { AuditService } from 'src/engine/core-modules/audit/services/audit.servi import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.module.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.module.ts index 7db521a3b0..185596edcf 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.module.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.module.ts @@ -7,7 +7,6 @@ import { AdminPanelQueueService } from 'src/engine/core-modules/admin-panel/admi import { AdminPanelResolver } from 'src/engine/core-modules/admin-panel/admin-panel.resolver'; import { AdminPanelService } from 'src/engine/core-modules/admin-panel/admin-panel.service'; import { AuditModule } from 'src/engine/core-modules/audit/audit.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; import { AuthModule } from 'src/engine/core-modules/auth/auth.module'; import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; @@ -20,6 +19,7 @@ import { WorkerHealthIndicator } from 'src/engine/core-modules/admin-panel/indic import { ImpersonationModule } from 'src/engine/core-modules/impersonation/impersonation.module'; import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { RedisClientModule } from 'src/engine/core-modules/redis-client/redis-client.module'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -39,13 +39,13 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi TelemetryModule, ImpersonationModule, PermissionsModule, + SecureHttpClientModule, ], providers: [ AdminPanelResolver, AdminPanelService, AdminPanelHealthService, AdminPanelQueueService, - SecureHttpClientService, DatabaseHealthIndicator, RedisHealthIndicator, WorkerHealthIndicator, diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts index 0c5bec1e60..101efc08c8 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.service.ts @@ -20,7 +20,7 @@ import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspac import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { type FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { type ConfigVariables } from 'src/engine/core-modules/twenty-config/config-variables'; import { CONFIG_VARIABLES_GROUP_METADATA } from 'src/engine/core-modules/twenty-config/constants/config-variables-group-metadata'; import { type ConfigVariablesGroup } from 'src/engine/core-modules/twenty-config/enums/config-variables-group.enum'; diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts index e22f104e61..45f710f6c3 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts @@ -44,8 +44,8 @@ import { KeyValuePairEntity } from 'src/engine/core-modules/key-value-pair/key-v import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module'; import { WorkspaceSSOModule } from 'src/engine/core-modules/sso/sso.module'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { WorkspaceSSOIdentityProviderEntity } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; import { TwoFactorAuthenticationMethodEntity } from 'src/engine/core-modules/two-factor-authentication/entities/two-factor-authentication-method.entity'; import { TwoFactorAuthenticationModule } from 'src/engine/core-modules/two-factor-authentication/two-factor-authentication.module'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; @@ -116,6 +116,7 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy'; DomainServerConfigModule, ApplicationModule, WorkspaceCacheModule, + SecureHttpClientModule, ], controllers: [ GoogleAuthController, @@ -150,7 +151,6 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy'; UpdateConnectedAccountOnReconnectService, TransientTokenService, AuthSsoService, - SecureHttpClientService, ], exports: [ AccessTokenService, diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis-scopes.ts b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis-scopes.ts index 7e779e1ff5..b3849a2563 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis-scopes.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis-scopes.ts @@ -6,7 +6,7 @@ import { } from 'src/engine/core-modules/auth/auth.exception'; import { includesExpectedScopes } from 'src/engine/core-modules/auth/services/google-apis-scopes.service.util'; import { getGoogleApisOauthScopes } from 'src/engine/core-modules/auth/utils/get-google-apis-oauth-scopes'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; interface TokenInfoResponse { scope: string; diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts index 34139ee48c..1b129528ad 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts @@ -30,7 +30,7 @@ import { SubdomainManagerService } from 'src/engine/core-modules/domain/subdomai import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserService } from 'src/engine/core-modules/user/services/user.service'; diff --git a/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts b/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts index ba480d3e35..6988ea5c8b 100644 --- a/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts +++ b/packages/twenty-server/src/engine/core-modules/captcha/captcha.module.ts @@ -8,7 +8,8 @@ import { CaptchaDriverType, type CaptchaModuleAsyncOptions, } from 'src/engine/core-modules/captcha/interfaces'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; @Global() export class CaptchaModule { @@ -51,7 +52,8 @@ export class CaptchaModule { return { module: CaptchaModule, - providers: [CaptchaService, SecureHttpClientService, provider], + imports: [SecureHttpClientModule], + providers: [CaptchaService, provider], exports: [CaptchaService], }; } diff --git a/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts b/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts index 9661fc1c14..b0ccf20bba 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file-upload/file-upload.module.ts @@ -3,12 +3,12 @@ import { Module } from '@nestjs/common'; import { FileUploadResolver } from 'src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileModule } from 'src/engine/core-modules/file/file.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; @Module({ - imports: [FileModule, PermissionsModule], - providers: [FileUploadService, FileUploadResolver, SecureHttpClientService], + imports: [FileModule, PermissionsModule, SecureHttpClientModule], + providers: [FileUploadService, FileUploadResolver], exports: [FileUploadService, FileUploadResolver], }) export class FileUploadModule {} diff --git a/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts b/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts index bca2344112..9dd65f1fbb 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts @@ -10,7 +10,7 @@ import { FileStorageService } from 'src/engine/core-modules/file-storage/file-st import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { buildFileInfo } from 'src/engine/core-modules/file/utils/build-file-info.utils'; import { sanitizeFile } from 'src/engine/core-modules/file/utils/sanitize-file.utils'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { getCropSize, getImageBufferFromUrl } from 'src/utils/image'; export type SignedFile = { path: string; token: string }; diff --git a/packages/twenty-server/src/engine/core-modules/file/file.module.ts b/packages/twenty-server/src/engine/core-modules/file/file.module.ts index e50ba8311b..1fb3646c65 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file.module.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file.module.ts @@ -9,7 +9,7 @@ import { FileWorkspaceFolderDeletionJob } from 'src/engine/core-modules/file/job import { FileAttachmentListener } from 'src/engine/core-modules/file/listeners/file-attachment.listener'; import { FileWorkspaceMemberListener } from 'src/engine/core-modules/file/listeners/file-workspace-member.listener'; import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; @@ -28,6 +28,7 @@ import { FileService } from './services/file.service'; PermissionsModule, FileStorageModule, FilesFieldModule, + SecureHttpClientModule, ], providers: [ FileService, @@ -39,7 +40,6 @@ import { FileService } from './services/file.service'; FileWorkspaceFolderDeletionJob, FileDeletionJob, FileUploadService, - SecureHttpClientService, ], exports: [FileService, FileMetadataService], controllers: [FileController], diff --git a/packages/twenty-server/src/engine/core-modules/geo-map/geo-map-module.ts b/packages/twenty-server/src/engine/core-modules/geo-map/geo-map-module.ts index a4def72d6d..d64c138809 100644 --- a/packages/twenty-server/src/engine/core-modules/geo-map/geo-map-module.ts +++ b/packages/twenty-server/src/engine/core-modules/geo-map/geo-map-module.ts @@ -3,12 +3,12 @@ import { Module } from '@nestjs/common'; import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; import { GeoMapResolver } from 'src/engine/core-modules/geo-map/resolver/geo-map.resolver'; import { GeoMapService } from 'src/engine/core-modules/geo-map/services/geo-map.service'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; @Module({ - imports: [WorkspaceCacheStorageModule, TokenModule], - providers: [GeoMapService, GeoMapResolver, SecureHttpClientService], + imports: [WorkspaceCacheStorageModule, TokenModule, SecureHttpClientModule], + providers: [GeoMapService, GeoMapResolver], exports: [], }) export class GeoMapModule {} diff --git a/packages/twenty-server/src/engine/core-modules/geo-map/services/geo-map.service.ts b/packages/twenty-server/src/engine/core-modules/geo-map/services/geo-map.service.ts index 6d6968ec91..50c0b4a5d1 100644 --- a/packages/twenty-server/src/engine/core-modules/geo-map/services/geo-map.service.ts +++ b/packages/twenty-server/src/engine/core-modules/geo-map/services/geo-map.service.ts @@ -11,7 +11,7 @@ import { type AddressFields, sanitizePlaceDetailsResults, } from 'src/engine/core-modules/geo-map/utils/sanitize-place-details-results.util'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; @Injectable() diff --git a/packages/twenty-server/src/engine/core-modules/secure-http-client/__tests__/secure-http-client.service.spec.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/__tests__/secure-http-client.service.spec.ts new file mode 100644 index 0000000000..8358e390a4 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/__tests__/secure-http-client.service.spec.ts @@ -0,0 +1,120 @@ +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; +import { type TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; + +const createMockConfigService = ( + overrides: Record = {}, +): TwentyConfigService => { + const defaults: Record = { + OUTBOUND_HTTP_SAFE_MODE_ENABLED: false, + }; + const config = { ...defaults, ...overrides }; + + return { + get: jest.fn((key: string) => config[key]), + } as unknown as TwentyConfigService; +}; + +describe('SecureHttpClientService', () => { + describe('getHttpClient', () => { + it('should return a plain axios instance when safe mode is off', () => { + const service = new SecureHttpClientService(createMockConfigService()); + const client = service.getHttpClient(); + + expect(client).toBeDefined(); + expect(client.defaults.httpAgent).toBeUndefined(); + expect(client.defaults.httpsAgent).toBeUndefined(); + }); + + it('should return an axios instance with secure adapter when safe mode is on', () => { + const service = new SecureHttpClientService( + createMockConfigService({ OUTBOUND_HTTP_SAFE_MODE_ENABLED: true }), + ); + const client = service.getHttpClient(); + + expect(client).toBeDefined(); + expect(typeof client.defaults.adapter).toBe('function'); + }); + + it('should pass through axios config like baseURL', () => { + const service = new SecureHttpClientService(createMockConfigService()); + const client = service.getHttpClient({ + baseURL: 'https://example.com/api', + }); + + expect(client.defaults.baseURL).toBe('https://example.com/api'); + }); + }); + + describe('getInternalHttpClient', () => { + it('should return a plain axios instance regardless of safe mode', () => { + const service = new SecureHttpClientService( + createMockConfigService({ OUTBOUND_HTTP_SAFE_MODE_ENABLED: true }), + ); + const client = service.getInternalHttpClient(); + + expect(client).toBeDefined(); + }); + + it('should pass through axios config like baseURL', () => { + const service = new SecureHttpClientService(createMockConfigService()); + const client = service.getInternalHttpClient({ + baseURL: 'http://localhost:3000', + }); + + expect(client.defaults.baseURL).toBe('http://localhost:3000'); + }); + }); + + describe('logging interceptor', () => { + it('should add a request interceptor when context is provided', () => { + const service = new SecureHttpClientService(createMockConfigService()); + const client = service.getHttpClient(undefined, { + workspaceId: 'ws-123', + source: 'webhook', + }); + + const interceptorHandlers = ( + client.interceptors.request as unknown as { + handlers: Array<{ fulfilled: Function }>; + } + ).handlers; + + expect(interceptorHandlers.length).toBe(1); + }); + + it('should not add a request interceptor when context is not provided', () => { + const service = new SecureHttpClientService(createMockConfigService()); + const client = service.getHttpClient(); + + const interceptorHandlers = ( + client.interceptors.request as unknown as { + handlers: Array<{ fulfilled: Function }>; + } + ).handlers; + + expect(interceptorHandlers.length).toBe(0); + }); + + it('should pass config through the interceptor and return it', () => { + const service = new SecureHttpClientService(createMockConfigService()); + const client = service.getHttpClient(undefined, { + workspaceId: 'ws-456', + source: 'workflow-http', + userId: 'user-789', + }); + + const interceptorHandlers = ( + client.interceptors.request as unknown as { + handlers: Array<{ fulfilled: Function }>; + } + ).handlers; + + const interceptorFn = interceptorHandlers[0].fulfilled; + const mockConfig = { method: 'GET', url: 'https://example.com/api' }; + + const result = interceptorFn(mockConfig); + + expect(result).toBe(mockConfig); + }); + }); +}); diff --git a/packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-context.type.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-context.type.ts new file mode 100644 index 0000000000..684ab4af8f --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-context.type.ts @@ -0,0 +1,7 @@ +import { type OutboundRequestSource } from './outbound-request-source.type'; + +export type OutboundRequestContext = { + workspaceId: string; + source: OutboundRequestSource; + userId?: string; +}; diff --git a/packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-source.type.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-source.type.ts new file mode 100644 index 0000000000..e60d31d8ea --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/outbound-request-source.type.ts @@ -0,0 +1,4 @@ +export type OutboundRequestSource = + | 'webhook' + | 'workflow-http' + | 'logic-function'; diff --git a/packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.module.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.module.ts new file mode 100644 index 0000000000..478c4364aa --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { SecureHttpClientService } from './secure-http-client.service'; + +@Module({ + providers: [SecureHttpClientService], + exports: [SecureHttpClientService], +}) +export class SecureHttpClientModule {} diff --git a/packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.service.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.service.ts new file mode 100644 index 0000000000..4699cef0bc --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/secure-http-client.service.ts @@ -0,0 +1,51 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import axios, { type AxiosInstance, type CreateAxiosDefaults } from 'axios'; + +import { getSecureAxiosAdapter } from 'src/engine/core-modules/secure-http-client/utils/get-secure-axios-adapter.util'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; + +import { type OutboundRequestContext } from './outbound-request-context.type'; + +@Injectable() +export class SecureHttpClientService { + private readonly logger = new Logger(SecureHttpClientService.name); + + constructor(private readonly twentyConfigService: TwentyConfigService) {} + + // Returns an SSRF-protected HTTP client for external requests. + // When context is provided, outbound requests are logged with + // workspace/user info for GuardDuty correlation. + getHttpClient( + config?: CreateAxiosDefaults, + context?: OutboundRequestContext, + ): AxiosInstance { + const isSafeModeEnabled = this.twentyConfigService.get( + 'OUTBOUND_HTTP_SAFE_MODE_ENABLED', + ); + + const client = isSafeModeEnabled + ? axios.create({ ...config, adapter: getSecureAxiosAdapter() }) + : axios.create(config); + + if (context) { + client.interceptors.request.use((requestConfig) => { + this.logger.log( + `Outbound HTTP request: ${requestConfig.method?.toUpperCase()} ${requestConfig.url} ` + + `[workspace=${context.workspaceId}, source=${context.source}` + + `${context.userId ? `, user=${context.userId}` : ''}]`, + ); + + return requestConfig; + }); + } + + return client; + } + + // Returns a plain HTTP client for requests to trusted internal URLs + // (e.g., the server's own API endpoints). Not SSRF-protected. + getInternalHttpClient(config?: CreateAxiosDefaults): AxiosInstance { + return axios.create(config); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/tool/utils/__tests__/get-secure-axios-adapter.util.spec.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/__tests__/get-secure-axios-adapter.util.spec.ts similarity index 98% rename from packages/twenty-server/src/engine/core-modules/tool/utils/__tests__/get-secure-axios-adapter.util.spec.ts rename to packages/twenty-server/src/engine/core-modules/secure-http-client/utils/__tests__/get-secure-axios-adapter.util.spec.ts index 54af1a6770..e17a5d48db 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/utils/__tests__/get-secure-axios-adapter.util.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/__tests__/get-secure-axios-adapter.util.spec.ts @@ -3,8 +3,8 @@ import * as https from 'https'; import { AxiosHeaders, type InternalAxiosRequestConfig } from 'axios'; -import { type SecureAdapterDependencies } from 'src/engine/core-modules/tool/utils/get-secure-axios-adapter.types'; -import { getSecureAxiosAdapter } from 'src/engine/core-modules/tool/utils/get-secure-axios-adapter.util'; +import { type SecureAdapterDependencies } from 'src/engine/core-modules/secure-http-client/utils/secure-adapter-dependencies.type'; +import { getSecureAxiosAdapter } from 'src/engine/core-modules/secure-http-client/utils/get-secure-axios-adapter.util'; describe('getSecureAxiosAdapter', () => { let mockDnsLookup: jest.Mock; diff --git a/packages/twenty-server/src/engine/core-modules/tool/utils/__tests__/is-private-ip.util.spec.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/__tests__/is-private-ip.util.spec.ts similarity index 97% rename from packages/twenty-server/src/engine/core-modules/tool/utils/__tests__/is-private-ip.util.spec.ts rename to packages/twenty-server/src/engine/core-modules/secure-http-client/utils/__tests__/is-private-ip.util.spec.ts index e2ec1bfb09..093def525a 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/utils/__tests__/is-private-ip.util.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/__tests__/is-private-ip.util.spec.ts @@ -1,4 +1,4 @@ -import { isPrivateIp } from 'src/engine/core-modules/tool/utils/is-private-ip.util'; +import { isPrivateIp } from 'src/engine/core-modules/secure-http-client/utils/is-private-ip.util'; describe('isPrivateIp', () => { describe('loopback addresses', () => { diff --git a/packages/twenty-server/src/engine/core-modules/tool/utils/get-secure-axios-adapter.util.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/get-secure-axios-adapter.util.ts similarity index 91% rename from packages/twenty-server/src/engine/core-modules/tool/utils/get-secure-axios-adapter.util.ts rename to packages/twenty-server/src/engine/core-modules/secure-http-client/utils/get-secure-axios-adapter.util.ts index 085c562914..feb9a53a67 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/utils/get-secure-axios-adapter.util.ts +++ b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/get-secure-axios-adapter.util.ts @@ -8,9 +8,9 @@ import axios, { type InternalAxiosRequestConfig, } from 'axios'; -import { isPrivateIp } from 'src/engine/core-modules/tool/utils/is-private-ip.util'; +import { isPrivateIp } from 'src/engine/core-modules/secure-http-client/utils/is-private-ip.util'; -import { type SecureAdapterDependencies } from './get-secure-axios-adapter.types'; +import { type SecureAdapterDependencies } from './secure-adapter-dependencies.type'; const defaultDependencies: SecureAdapterDependencies = { dnsLookup: dns.lookup, @@ -62,14 +62,14 @@ export const getSecureAxiosAdapter = ( optionsOrCallback, maybeCallback, ) => { - const options = + const lookupOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback; - if (options.all) { + if (lookupOptions.all) { (callback as Function)(null, [ { address: resolvedIp, family: ipFamily }, ]); diff --git a/packages/twenty-server/src/engine/core-modules/tool/utils/is-private-ip.util.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/is-private-ip.util.ts similarity index 100% rename from packages/twenty-server/src/engine/core-modules/tool/utils/is-private-ip.util.ts rename to packages/twenty-server/src/engine/core-modules/secure-http-client/utils/is-private-ip.util.ts diff --git a/packages/twenty-server/src/engine/core-modules/tool/utils/get-secure-axios-adapter.types.ts b/packages/twenty-server/src/engine/core-modules/secure-http-client/utils/secure-adapter-dependencies.type.ts similarity index 100% rename from packages/twenty-server/src/engine/core-modules/tool/utils/get-secure-axios-adapter.types.ts rename to packages/twenty-server/src/engine/core-modules/secure-http-client/utils/secure-adapter-dependencies.type.ts diff --git a/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts index be20641633..643af137a5 100644 --- a/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts +++ b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { TelemetryService } from './telemetry.service'; @Module({ - providers: [TelemetryService, SecureHttpClientService], + imports: [SecureHttpClientModule], + providers: [TelemetryService], exports: [TelemetryService], }) export class TelemetryModule {} diff --git a/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts index 550eb556d1..adfd52468a 100644 --- a/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts +++ b/packages/twenty-server/src/engine/core-modules/telemetry/telemetry.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; type CreateEventInput = { diff --git a/packages/twenty-server/src/engine/core-modules/tool/services/secure-http-client.service.ts b/packages/twenty-server/src/engine/core-modules/tool/services/secure-http-client.service.ts deleted file mode 100644 index 1acbfb5093..0000000000 --- a/packages/twenty-server/src/engine/core-modules/tool/services/secure-http-client.service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import axios, { type AxiosInstance, type CreateAxiosDefaults } from 'axios'; - -import { getSecureAxiosAdapter } from 'src/engine/core-modules/tool/utils/get-secure-axios-adapter.util'; -import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; - -@Injectable() -export class SecureHttpClientService { - constructor(private readonly twentyConfigService: TwentyConfigService) {} - - // Returns an SSRF-protected HTTP client for external requests - getHttpClient(config?: CreateAxiosDefaults): AxiosInstance { - const isSafeModeEnabled = this.twentyConfigService.get( - 'OUTBOUND_HTTP_SAFE_MODE_ENABLED', - ); - - return isSafeModeEnabled - ? axios.create({ ...config, adapter: getSecureAxiosAdapter() }) - : axios.create(config); - } - - // Returns a plain HTTP client for requests to trusted internal URLs - // (e.g., the server's own API endpoints). Not SSRF-protected. - getInternalHttpClient(config?: CreateAxiosDefaults): AxiosInstance { - return axios.create(config); - } -} diff --git a/packages/twenty-server/src/engine/core-modules/tool/tool.module.ts b/packages/twenty-server/src/engine/core-modules/tool/tool.module.ts index a90922dfbe..9698fa9c57 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tool.module.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tool.module.ts @@ -4,7 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; import { FileModule } from 'src/engine/core-modules/file/file.module'; import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { CodeInterpreterTool } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool'; import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool'; import { SearchHelpCenterTool } from 'src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool'; @@ -17,20 +17,14 @@ import { MessagingImportManagerModule } from 'src/modules/messaging/message-impo TypeOrmModule.forFeature([FileEntity]), FileModule, JwtModule, + SecureHttpClientModule, ], providers: [ HttpTool, SendEmailTool, SearchHelpCenterTool, CodeInterpreterTool, - SecureHttpClientService, - ], - exports: [ - HttpTool, - SendEmailTool, - SearchHelpCenterTool, - CodeInterpreterTool, - SecureHttpClientService, ], + exports: [HttpTool, SendEmailTool, SearchHelpCenterTool, CodeInterpreterTool], }) export class ToolModule {} diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts index 51b628e2be..7f8727ff1b 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts @@ -23,7 +23,7 @@ import { CodeInterpreterService } from 'src/engine/core-modules/code-interpreter import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { CodeInterpreterInputZodSchema } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.schema'; import { TWENTY_MCP_HELPER } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/twenty-mcp-helper.const'; import { type CodeInterpreterInput } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/types/code-interpreter-input.type'; diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts index 2601973425..bba9e8940f 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts @@ -4,7 +4,7 @@ import { type AxiosRequestConfig, isAxiosError } from 'axios'; import { isDefined } from 'twenty-shared/utils'; import { parseDataFromContentType } from 'twenty-shared/workflow'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { HttpRequestInputZodSchema } from 'src/engine/core-modules/tool/tools/http-tool/http-tool.schema'; import { type HttpRequestInput } from 'src/engine/core-modules/tool/tools/http-tool/types/http-request-input.type'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; @@ -26,7 +26,7 @@ export class HttpTool implements Tool { async execute( parameters: ToolInput, - _context: ToolExecutionContext, + context: ToolExecutionContext, ): Promise { const { url, method, headers, body } = parameters as HttpRequestInput; const headersCopy = { ...headers }; @@ -48,7 +48,14 @@ export class HttpTool implements Tool { } } - const axiosClient = this.secureHttpClientService.getHttpClient(); + const axiosClient = this.secureHttpClientService.getHttpClient( + undefined, + { + workspaceId: context.workspaceId, + userId: context.userId, + source: 'workflow-http', + }, + ); const response = await axiosClient(axiosConfig); return { diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts index 86094eb879..32a5cf20af 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { isAxiosError } from 'axios'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { SearchHelpCenterInputZodSchema } from 'src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.schema'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; diff --git a/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/call-webhook.job.ts b/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/call-webhook.job.ts index 9bd598e36d..cfa3ae74c1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/call-webhook.job.ts +++ b/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/call-webhook.job.ts @@ -9,7 +9,7 @@ import { Processor } from 'src/engine/core-modules/message-queue/decorators/proc import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'; import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; export type CallWebhookJobData = { targetUrl: string; @@ -83,7 +83,14 @@ export class CallWebhookJob { .toString('hex'); } - const axiosClient = this.secureHttpClientService.getHttpClient(); + const axiosClient = this.secureHttpClientService.getHttpClient( + undefined, + { + workspaceId: data.workspaceId, + userId: data.userId, + source: 'webhook', + }, + ); const response = await axiosClient.post( getAbsoluteUrl(data.targetUrl), diff --git a/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/webhook-job.module.ts b/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/webhook-job.module.ts index 2abc0a00d1..1d7d90e619 100644 --- a/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/webhook-job.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/webhook/jobs/webhook-job.module.ts @@ -3,7 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AuditModule } from 'src/engine/core-modules/audit/audit.module'; import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; -import { ToolModule } from 'src/engine/core-modules/tool/tool.module'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { WebhookEntity } from 'src/engine/metadata-modules/webhook/entities/webhook.entity'; import { CallWebhookJobsJob } from 'src/engine/metadata-modules/webhook/jobs/call-webhook-jobs.job'; import { CallWebhookJob } from 'src/engine/metadata-modules/webhook/jobs/call-webhook.job'; @@ -13,7 +13,7 @@ import { CallWebhookJob } from 'src/engine/metadata-modules/webhook/jobs/call-we TypeOrmModule.forFeature([WebhookEntity]), AuditModule, MetricsModule, - ToolModule, + SecureHttpClientModule, ], providers: [CallWebhookJobsJob, CallWebhookJob], }) diff --git a/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts b/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts index 06ed28cb13..852b36f5b3 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; @@ -15,12 +15,12 @@ import { CreatePersonService } from 'src/modules/contact-creation-manager/servic WorkspaceDataSourceModule, TypeOrmModule.forFeature([FeatureFlagEntity]), TypeOrmModule.forFeature([ObjectMetadataEntity, FieldMetadataEntity]), + SecureHttpClientModule, ], providers: [ CreateCompanyService, CreatePersonService, CreateCompanyAndPersonService, - SecureHttpClientService, ], exports: [CreateCompanyAndPersonService], }) diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts index ffd97db08e..ea887bd5b9 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts @@ -8,7 +8,7 @@ import { import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { type CompanyToCreate, diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts index ef20ce4ec4..f655b19efe 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts @@ -13,7 +13,7 @@ import { } from 'twenty-shared/utils'; import { type DeepPartial, ILike } from 'typeorm'; -import { SecureHttpClientService } from 'src/engine/core-modules/tool/services/secure-http-client.service'; +import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';