diff --git a/backend/src/services/app-connection/gitlab/gitlab-connection-fns.ts b/backend/src/services/app-connection/gitlab/gitlab-connection-fns.ts index 858e83ee5..93bbbc594 100644 --- a/backend/src/services/app-connection/gitlab/gitlab-connection-fns.ts +++ b/backend/src/services/app-connection/gitlab/gitlab-connection-fns.ts @@ -4,7 +4,9 @@ import { AxiosError, AxiosResponse } from "axios"; import { getConfig } from "@app/lib/config/env"; import { request } from "@app/lib/config/request"; import { BadRequestError, InternalServerError } from "@app/lib/errors"; +import { removeTrailingSlash } from "@app/lib/fn"; import { logger } from "@app/lib/logger"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { encryptAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns"; import { IntegrationUrls } from "@app/services/integration-auth/integration-list"; @@ -37,6 +39,14 @@ export const getGitLabConnectionListItem = () => { }; }; +export const getGitLabInstanceUrl = async (instanceUrl?: string) => { + const gitLabInstanceUrl = instanceUrl ? removeTrailingSlash(instanceUrl) : IntegrationUrls.GITLAB_URL; + + await blockLocalAndPrivateIpAddresses(gitLabInstanceUrl); + + return gitLabInstanceUrl; +}; + export const refreshGitLabToken = async ( refreshToken: string, appId: string, @@ -61,16 +71,13 @@ export const refreshGitLabToken = async ( }); try { - const { data } = await request.post( - `${instanceUrl ? `${instanceUrl}/oauth/token` : IntegrationUrls.GITLAB_TOKEN_URL}`, - payload.toString(), - { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json" - } + const url = await getGitLabInstanceUrl(instanceUrl); + const { data } = await request.post(`${url}/oauth/token`, payload.toString(), { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json" } - ); + }); const expiresAt = new Date(Date.now() + data.expires_in * 1000 - 60000); @@ -118,17 +125,14 @@ export const exchangeGitLabOAuthCode = async ( client_secret: CLIENT_SECRET_GITLAB_LOGIN, redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback` }); + const url = await getGitLabInstanceUrl(instanceUrl); - const response = await request.post( - instanceUrl ? `${instanceUrl}/oauth/token` : IntegrationUrls.GITLAB_TOKEN_URL, - payload.toString(), - { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json" - } + const response = await request.post(`${url}/oauth/token`, payload.toString(), { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json" } - ); + }); if (!response.data) { throw new InternalServerError({ @@ -169,15 +173,13 @@ export const validateGitLabConnectionCredentials = async (config: TGitLabConnect let response: AxiosResponse | null = null; try { - response = await request.get( - `${inputCredentials.instanceUrl ? `${inputCredentials.instanceUrl}/api` : IntegrationUrls.GITLAB_API_URL}/v4/groups`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + const url = await getGitLabInstanceUrl(inputCredentials.instanceUrl); + response = await request.get(`${url}/api/v4/groups`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); } catch (error: unknown) { if (error instanceof AxiosError) { throw new BadRequestError({ @@ -236,9 +238,8 @@ export const listGitLabProjects = async ({ ); } - const gitLabApiUrl = appConnection.credentials.instanceUrl - ? `${appConnection.credentials.instanceUrl}/api/v4` - : `${IntegrationUrls.GITLAB_API_URL}/v4`; + const url = await getGitLabInstanceUrl(appConnection.credentials.instanceUrl); + const gitLabApiUrl = `${url}/api/v4`; const projects: TGitLabProject[] = []; let page = 1; @@ -433,9 +434,8 @@ export const listGitLabGroups = async ({ ); } - const gitLabApiUrl = appConnection.credentials.instanceUrl - ? `${appConnection.credentials.instanceUrl}/api/v4` - : `${IntegrationUrls.GITLAB_API_URL}/v4`; + const url = await getGitLabInstanceUrl(appConnection.credentials.instanceUrl); + const gitLabApiUrl = `${url}/api/v4`; const groups: TGitLabGroup[] = []; let page = 1; diff --git a/backend/src/services/secret-sync/gitlab/gitlab-sync-fns.ts b/backend/src/services/secret-sync/gitlab/gitlab-sync-fns.ts index fea0092be..b9995436e 100644 --- a/backend/src/services/secret-sync/gitlab/gitlab-sync-fns.ts +++ b/backend/src/services/secret-sync/gitlab/gitlab-sync-fns.ts @@ -1,5 +1,6 @@ /* eslint-disable no-await-in-loop */ import { request } from "@app/lib/config/request"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal"; import { GitLabConnectionMethod, refreshGitLabToken, TGitLabConnection } from "@app/services/app-connection/gitlab"; import { IntegrationUrls } from "@app/services/integration-auth/integration-list"; @@ -55,8 +56,9 @@ const getValidAccessToken = async ( return connection.credentials.accessToken; }; -const getGitLabApiUrl = (connection: TGitLabConnection): string => { +const getGitLabApiUrl = async (connection: TGitLabConnection): Promise => { const baseUrl = connection.credentials.instanceUrl || IntegrationUrls.GITLAB_API_URL; + await blockLocalAndPrivateIpAddresses(baseUrl); return baseUrl.includes("/api") ? baseUrl : `${baseUrl}/api`; }; @@ -76,7 +78,7 @@ const getGitLabVariables = async ({ targetEnvironment?: string; }): Promise => { try { - const apiUrl = getGitLabApiUrl(connection); + const apiUrl = await getGitLabApiUrl(connection); const baseEndpoint = buildVariablesEndpoint(apiUrl, projectId); const headers = { @@ -131,7 +133,7 @@ const createGitLabVariable = async ({ variable: TGitLabVariableCreate; }): Promise => { try { - const apiUrl = getGitLabApiUrl(connection); + const apiUrl = await getGitLabApiUrl(connection); const endpoint = buildVariablesEndpoint(apiUrl, projectId); const payload = { @@ -177,7 +179,7 @@ const updateGitLabVariable = async ({ targetEnvironment?: string; }): Promise => { try { - const apiUrl = getGitLabApiUrl(connection); + const apiUrl = await getGitLabApiUrl(connection); const baseEndpoint = buildVariablesEndpoint(apiUrl, projectId); let url = `${baseEndpoint}/${encodeURIComponent(key)}`; @@ -224,7 +226,7 @@ const deleteGitLabVariable = async ({ targetEnvironment?: string; }): Promise => { try { - const apiUrl = getGitLabApiUrl(connection); + const apiUrl = await getGitLabApiUrl(connection); const baseEndpoint = buildVariablesEndpoint(apiUrl, projectId); let url = `${baseEndpoint}/${encodeURIComponent(key)}`; diff --git a/docs/integrations/secret-syncs/gitlab.mdx b/docs/integrations/secret-syncs/gitlab.mdx index a304571a5..bc60c6245 100644 --- a/docs/integrations/secret-syncs/gitlab.mdx +++ b/docs/integrations/secret-syncs/gitlab.mdx @@ -5,8 +5,8 @@ description: "Learn how to configure a GitLab Sync for Infisical." **Prerequisites:** - - Set up and add secrets to [Infisical Cloud](https://app.infisical.com) - - Create a [GitLab Connection](/integrations/app-connections/gitlab) + - Set up and add secrets to [Infisical Cloud](https://app.infisical.com) + - Create a [GitLab Connection](/integrations/app-connections/gitlab) diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/GitLabSyncFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/GitLabSyncFields.tsx index 146d75b22..b4af0291c 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/GitLabSyncFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/GitLabSyncFields.tsx @@ -65,7 +65,7 @@ const SecretProtectionOption = ({ export const GitLabSyncFields = () => { const { control, setValue } = useFormContext< - TSecretSyncForm & { destination: SecretSync.Gitlab } + TSecretSyncForm & { destination: SecretSync.GitLab } >(); const connectionId = useWatch({ name: "connection.id", control }); @@ -259,10 +259,10 @@ export const GitLabSyncFields = () => { control={control} name="destinationConfig.shouldHideSecrets" render={({ field: { onChange, value } }) => ( -
+
{ return ; case SecretSync.Flyio: return ; - case SecretSync.Gitlab: + case SecretSync.GitLab: return ; default: throw new Error(`Unhandled Destination Config Field: ${destination}`); diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx index 87115868e..5958b49c0 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/SecretSyncOptionsFields.tsx @@ -55,7 +55,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => { case SecretSync.Heroku: case SecretSync.Render: case SecretSync.Flyio: - case SecretSync.Gitlab: + case SecretSync.GitLab: AdditionalSyncOptionsFieldsComponent = null; break; default: diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/GitLabSyncReviewFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/GitLabSyncReviewFields.tsx index 17ece3586..03c422df5 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/GitLabSyncReviewFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/GitLabSyncReviewFields.tsx @@ -5,7 +5,7 @@ import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas"; import { SecretSync } from "@app/hooks/api/secretSyncs"; export const GitLabSyncReviewFields = () => { - const { watch } = useFormContext(); + const { watch } = useFormContext(); const projectId = watch("destinationConfig.projectId"); const targetEnvironment = watch("destinationConfig.targetEnvironment"); const groupId = watch("destinationConfig.groupId"); diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx index 3a55b6fc9..82aedabc6 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncReviewFields/SecretSyncReviewFields.tsx @@ -117,7 +117,7 @@ export const SecretSyncReviewFields = () => { case SecretSync.Flyio: DestinationFieldsComponent = ; break; - case SecretSync.Gitlab: + case SecretSync.GitLab: DestinationFieldsComponent = ; break; default: diff --git a/frontend/src/components/secret-syncs/forms/schemas/gitlab-sync-destination-schema.ts b/frontend/src/components/secret-syncs/forms/schemas/gitlab-sync-destination-schema.ts index 8019b0f26..09c9a8b7a 100644 --- a/frontend/src/components/secret-syncs/forms/schemas/gitlab-sync-destination-schema.ts +++ b/frontend/src/components/secret-syncs/forms/schemas/gitlab-sync-destination-schema.ts @@ -6,7 +6,7 @@ import { GitlabSyncScope } from "@app/hooks/api/secretSyncs/types/gitlab-sync"; export const GitlabSyncDestinationSchema = BaseSecretSyncSchema().merge( z.object({ - destination: z.literal(SecretSync.Gitlab), + destination: z.literal(SecretSync.GitLab), destinationConfig: z.discriminatedUnion("scope", [ z.object({ scope: z.literal(GitlabSyncScope.Individual), diff --git a/frontend/src/helpers/appConnections.ts b/frontend/src/helpers/appConnections.ts index db679c8a5..048826357 100644 --- a/frontend/src/helpers/appConnections.ts +++ b/frontend/src/helpers/appConnections.ts @@ -86,7 +86,7 @@ export const APP_CONNECTION_MAP: Record< [AppConnection.Heroku]: { name: "Heroku", image: "Heroku.png" }, [AppConnection.Render]: { name: "Render", image: "Render.png" }, [AppConnection.Flyio]: { name: "Fly.io", image: "Flyio.svg" }, - [AppConnection.Gitlab]: { name: "Gitlab", image: "GitLab.png" } + [AppConnection.Gitlab]: { name: "GitLab", image: "GitLab.png" } }; export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => { diff --git a/frontend/src/helpers/secretSyncs.ts b/frontend/src/helpers/secretSyncs.ts index 8c12556d7..a77732af3 100644 --- a/frontend/src/helpers/secretSyncs.ts +++ b/frontend/src/helpers/secretSyncs.ts @@ -74,7 +74,7 @@ export const SECRET_SYNC_MAP: Record = { [SecretSync.Heroku]: AppConnection.Heroku, [SecretSync.Render]: AppConnection.Render, [SecretSync.Flyio]: AppConnection.Flyio, - [SecretSync.Gitlab]: AppConnection.Gitlab + [SecretSync.GitLab]: AppConnection.Gitlab }; export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record< diff --git a/frontend/src/hooks/api/secretSyncs/enums.ts b/frontend/src/hooks/api/secretSyncs/enums.ts index 26c9d6391..bfd45d541 100644 --- a/frontend/src/hooks/api/secretSyncs/enums.ts +++ b/frontend/src/hooks/api/secretSyncs/enums.ts @@ -19,7 +19,7 @@ export enum SecretSync { Heroku = "heroku", Render = "render", Flyio = "flyio", - Gitlab = "gitlab" + GitLab = "gitlab" } export enum SecretSyncStatus { diff --git a/frontend/src/hooks/api/secretSyncs/types/gitlab-sync.ts b/frontend/src/hooks/api/secretSyncs/types/gitlab-sync.ts index 8738fa606..2bedd5232 100644 --- a/frontend/src/hooks/api/secretSyncs/types/gitlab-sync.ts +++ b/frontend/src/hooks/api/secretSyncs/types/gitlab-sync.ts @@ -8,7 +8,7 @@ export enum GitlabSyncScope { } export type TGitlabSync = TRootSecretSync & { - destination: SecretSync.Gitlab; + destination: SecretSync.GitLab; destinationConfig: | { scope: GitlabSyncScope.Individual; diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/SecretSyncDestinationCol/SecretSyncDestinationCol.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/SecretSyncDestinationCol/SecretSyncDestinationCol.tsx index 4f16534eb..c674c6f9a 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/SecretSyncDestinationCol/SecretSyncDestinationCol.tsx +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/SecretSyncDestinationCol/SecretSyncDestinationCol.tsx @@ -68,7 +68,7 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => { return ; case SecretSync.Flyio: return ; - case SecretSync.Gitlab: + case SecretSync.GitLab: return ; default: throw new Error( diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/helpers/index.ts b/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/helpers/index.ts index ff29ad282..1850a109c 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/helpers/index.ts +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/helpers/index.ts @@ -128,7 +128,7 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => { primaryText = destinationConfig.appId; secondaryText = "App ID"; break; - case SecretSync.Gitlab: + case SecretSync.GitLab: primaryText = destinationConfig.projectName; secondaryText = destinationConfig.projectId; break; diff --git a/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/SecretSyncDestinatonSection.tsx b/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/SecretSyncDestinatonSection.tsx index 1bf3aa418..b2ab49b96 100644 --- a/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/SecretSyncDestinatonSection.tsx +++ b/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/SecretSyncDestinatonSection.tsx @@ -107,7 +107,7 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }: case SecretSync.Flyio: DestinationComponents = ; break; - case SecretSync.Gitlab: + case SecretSync.GitLab: DestinationComponents = ; break; default: diff --git a/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncOptionsSection/SecretSyncOptionsSection.tsx b/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncOptionsSection/SecretSyncOptionsSection.tsx index b37085b31..457e3f6ac 100644 --- a/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncOptionsSection/SecretSyncOptionsSection.tsx +++ b/frontend/src/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncOptionsSection/SecretSyncOptionsSection.tsx @@ -58,7 +58,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) = case SecretSync.Heroku: case SecretSync.Render: case SecretSync.Flyio: - case SecretSync.Gitlab: + case SecretSync.GitLab: AdditionalSyncOptionsComponent = null; break; default: diff --git a/frontend/src/pages/secret-manager/integrations/GitlabOauthCallbackPage/GitlabOauthCallbackPage.tsx b/frontend/src/pages/secret-manager/integrations/GitlabOauthCallbackPage/GitlabOauthCallbackPage.tsx index d6bf83f03..53fe03449 100644 --- a/frontend/src/pages/secret-manager/integrations/GitlabOauthCallbackPage/GitlabOauthCallbackPage.tsx +++ b/frontend/src/pages/secret-manager/integrations/GitlabOauthCallbackPage/GitlabOauthCallbackPage.tsx @@ -33,7 +33,6 @@ export const GitLabOAuthCallbackPage = () => { localStorage.removeItem("latestCSRFToken"); - // Retrieve stored form dataAdd commentMore actions const storedFormData = localStorage.getItem("gitlabConnectionFormData"); if (!storedFormData) { console.error("No stored form data found");