diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 16278c3b1..1637b266a 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -677,6 +677,8 @@ export const INTEGRATION = { secretAWSTag: "The tags for AWS secrets.", kmsKeyId: "The ID of the encryption key from AWS KMS.", shouldDisableDelete: "The flag to disable deletion of secrets in AWS Parameter Store.", + shouldMaskSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Masked'.", + shouldProtectSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Protected'.", shouldEnableDelete: "The flag to enable deletion of secrets" } }, diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 36228f03f..6351b4d82 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -1921,13 +1921,13 @@ const syncSecretsGitLab = async ({ return allEnvVariables; }; + const metadata = IntegrationMetadataSchema.parse(integration.metadata); const allEnvVariables = await getAllEnvVariables(integration?.appId as string, accessToken); const getSecretsRes: GitLabSecret[] = allEnvVariables .filter((secret: GitLabSecret) => secret.environment_scope === integration.targetEnvironment) .filter((gitLabSecret) => { let isValid = true; - const metadata = z.record(z.any()).parse(integration.metadata); if (metadata.secretPrefix && !gitLabSecret.key.startsWith(metadata.secretPrefix)) { isValid = false; } @@ -1947,8 +1947,8 @@ const syncSecretsGitLab = async ({ { key, value: secrets[key].value, - protected: false, - masked: false, + protected: Boolean(metadata.shouldProtectSecrets), + masked: Boolean(metadata.shouldMaskSecrets), raw: false, environment_scope: integration.targetEnvironment }, @@ -1965,7 +1965,9 @@ const syncSecretsGitLab = async ({ `${gitLabApiUrl}/v4/projects/${integration?.appId}/variables/${existingSecret.key}?filter[environment_scope]=${integration.targetEnvironment}`, { ...existingSecret, - value: secrets[existingSecret.key].value + value: secrets[existingSecret.key].value, + protected: Boolean(metadata.shouldProtectSecrets), + masked: Boolean(metadata.shouldMaskSecrets) }, { headers: { diff --git a/backend/src/services/integration/integration-schema.ts b/backend/src/services/integration/integration-schema.ts index d1b6bcc77..1ea01e56a 100644 --- a/backend/src/services/integration/integration-schema.ts +++ b/backend/src/services/integration/integration-schema.ts @@ -31,5 +31,7 @@ export const IntegrationMetadataSchema = z.object({ .describe(INTEGRATION.CREATE.metadata.secretAWSTag), kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId), shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete), - shouldEnableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldEnableDelete) + shouldEnableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldEnableDelete), + shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets), + shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets) }); diff --git a/backend/src/services/integration/integration-types.ts b/backend/src/services/integration/integration-types.ts index db136c1ed..abbccbe90 100644 --- a/backend/src/services/integration/integration-types.ts +++ b/backend/src/services/integration/integration-types.ts @@ -29,6 +29,8 @@ export type TCreateIntegrationDTO = { }[]; kmsKeyId?: string; shouldDisableDelete?: boolean; + shouldMaskSecrets?: boolean; + shouldProtectSecrets?: boolean; shouldEnableDelete?: boolean; }; } & Omit; diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index d40a18e5e..07db646f8 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -1,4 +1,6 @@ /* eslint-disable no-await-in-loop */ +import { AxiosError } from "axios"; + import { getConfig } from "@app/lib/config/env"; import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { daysToMillisecond, secondsToMillis } from "@app/lib/dates"; @@ -567,11 +569,14 @@ export const secretQueueFactory = ({ isSynced: true }); } catch (err: unknown) { - logger.info("Secret integration sync error:", err); + logger.info("Secret integration sync error: %o", err); + const message = + err instanceof AxiosError ? JSON.stringify((err as AxiosError)?.response?.data) : (err as Error)?.message; + await integrationDAL.updateById(integration.id, { lastSyncJobId: job.id, lastUsed: new Date(), - syncMessage: (err as Error)?.message, + syncMessage: message, isSynced: false }); } diff --git a/frontend/src/hooks/api/integrations/queries.tsx b/frontend/src/hooks/api/integrations/queries.tsx index e561280c6..81d0f00ca 100644 --- a/frontend/src/hooks/api/integrations/queries.tsx +++ b/frontend/src/hooks/api/integrations/queries.tsx @@ -73,6 +73,8 @@ export const useCreateIntegration = () => { }[]; kmsKeyId?: string; shouldDisableDelete?: boolean; + shouldMaskSecrets?: boolean; + shouldProtectSecrets?: boolean; shouldEnableDelete?: boolean; }; }) => { diff --git a/frontend/src/pages/integrations/gitlab/create.tsx b/frontend/src/pages/integrations/gitlab/create.tsx index 529fd037e..c1c94471c 100644 --- a/frontend/src/pages/integrations/gitlab/create.tsx +++ b/frontend/src/pages/integrations/gitlab/create.tsx @@ -25,6 +25,7 @@ import { ModalContent, Select, SelectItem, + Switch, Tab, TabList, TabPanel, @@ -58,7 +59,9 @@ const schema = yup.object({ targetAppId: yup.string().required("GitLab project is required"), targetEnvironment: yup.string(), secretPrefix: yup.string(), - secretSuffix: yup.string() + secretSuffix: yup.string(), + shouldMaskSecrets: yup.boolean(), + shouldProtectSecrets: yup.boolean() }); type FormData = yup.InferType; @@ -138,7 +141,9 @@ export default function GitLabCreateIntegrationPage() { targetAppId, targetEnvironment, secretPrefix, - secretSuffix + secretSuffix, + shouldMaskSecrets, + shouldProtectSecrets }: FormData) => { try { setIsLoading(true); @@ -156,7 +161,9 @@ export default function GitLabCreateIntegrationPage() { secretPath, metadata: { secretPrefix, - secretSuffix + secretSuffix, + shouldMaskSecrets, + shouldProtectSecrets } }); @@ -390,6 +397,36 @@ export default function GitLabCreateIntegrationPage() { exit={{ opacity: 0, translateX: 30 }} className="pb-[14.25rem]" > +
+ ( + onChange(isChecked)} + isChecked={value} + > +
Mark Infisical secrets in Gitlab as 'Masked' secrets
+
+ )} + /> +
+
+ ( + onChange(isChecked)} + isChecked={value} + > + Mark Infisical secrets in Gitlab as 'Protected' secrets + + )} + /> +