Merge branch 'Infisical:main' into main

This commit is contained in:
Thalles Passos
2025-10-08 10:13:19 -03:00
committed by GitHub
270 changed files with 17394 additions and 1703 deletions
@@ -204,5 +204,19 @@ export const secretSyncDALFactory = (
}
};
return { ...secretSyncOrm, findById, findOne, find, create, updateById };
const findByDestinationAndOrgId = async (destination: string, orgId: string, tx?: Knex) => {
try {
const response = await (tx || db.replicaNode())(TableName.SecretSync)
.join(TableName.Project, `${TableName.SecretSync}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.SecretSync}.destination`, destination)
.where(`${TableName.Project}.orgId`, orgId)
.select(selectAllTableCols(TableName.SecretSync));
return response;
} catch (error) {
throw new DatabaseError({ error, name: "Find By Destination And Org ID - Secret Sync" });
}
};
return { ...secretSyncOrm, findById, findOne, find, create, updateById, findByDestinationAndOrgId };
};
@@ -1,5 +1,6 @@
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync, SecretSyncPlanType } from "@app/services/secret-sync/secret-sync-enums";
import { DestinationDuplicateCheckFn } from "@app/services/secret-sync/secret-sync-types";
export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
[SecretSync.AWSParameterStore]: "AWS Parameter Store",
@@ -99,3 +100,104 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
[SecretSync.Netlify]: SecretSyncPlanType.Regular,
[SecretSync.Bitbucket]: SecretSyncPlanType.Regular
};
export const SECRET_SYNC_SKIP_FIELDS_MAP: Record<SecretSync, string[]> = {
[SecretSync.AWSParameterStore]: [],
[SecretSync.AWSSecretsManager]: ["mappingBehavior", "secretName"],
[SecretSync.GitHub]: [],
[SecretSync.GCPSecretManager]: [],
[SecretSync.AzureKeyVault]: [],
[SecretSync.AzureAppConfiguration]: ["label"],
[SecretSync.AzureDevOps]: ["devopsProjectName"],
[SecretSync.Databricks]: [],
[SecretSync.Humanitec]: [],
[SecretSync.TerraformCloud]: ["variableSetName", "workspaceName"],
[SecretSync.Camunda]: [],
[SecretSync.Vercel]: ["appName"],
[SecretSync.Windmill]: [],
[SecretSync.HCVault]: [],
[SecretSync.TeamCity]: [],
[SecretSync.OCIVault]: [],
[SecretSync.OnePass]: ["valueLabel"],
[SecretSync.Heroku]: ["appName"],
[SecretSync.Render]: [],
[SecretSync.Flyio]: [],
[SecretSync.GitLab]: [
"projectName",
"shouldProtectSecrets",
"shouldMaskSecrets",
"shouldHideSecrets",
"targetEnvironment",
"groupName",
"groupId",
"projectId"
],
[SecretSync.CloudflarePages]: [],
[SecretSync.CloudflareWorkers]: [],
[SecretSync.Supabase]: ["projectName"],
[SecretSync.Zabbix]: ["hostName", "macroType"],
[SecretSync.Railway]: ["projectName", "environmentName", "serviceName"],
[SecretSync.Checkly]: ["groupName", "accountName"],
[SecretSync.DigitalOceanAppPlatform]: ["appName"],
[SecretSync.Netlify]: ["accountName", "siteName"],
[SecretSync.Bitbucket]: []
};
const defaultDuplicateCheck: DestinationDuplicateCheckFn = () => true;
export const DESTINATION_DUPLICATE_CHECK_MAP: Record<SecretSync, DestinationDuplicateCheckFn> = {
[SecretSync.AWSParameterStore]: defaultDuplicateCheck,
[SecretSync.AWSSecretsManager]: defaultDuplicateCheck,
[SecretSync.GitHub]: defaultDuplicateCheck,
[SecretSync.GCPSecretManager]: defaultDuplicateCheck,
[SecretSync.AzureKeyVault]: defaultDuplicateCheck,
[SecretSync.AzureAppConfiguration]: defaultDuplicateCheck,
[SecretSync.AzureDevOps]: defaultDuplicateCheck,
[SecretSync.Databricks]: defaultDuplicateCheck,
[SecretSync.Humanitec]: defaultDuplicateCheck,
[SecretSync.TerraformCloud]: defaultDuplicateCheck,
[SecretSync.Camunda]: defaultDuplicateCheck,
[SecretSync.Vercel]: defaultDuplicateCheck,
[SecretSync.Windmill]: defaultDuplicateCheck,
[SecretSync.HCVault]: defaultDuplicateCheck,
[SecretSync.TeamCity]: defaultDuplicateCheck,
[SecretSync.OCIVault]: defaultDuplicateCheck,
[SecretSync.OnePass]: defaultDuplicateCheck,
[SecretSync.Heroku]: defaultDuplicateCheck,
[SecretSync.Render]: defaultDuplicateCheck,
[SecretSync.Flyio]: defaultDuplicateCheck,
[SecretSync.GitLab]: (existingConfig, newConfig) => {
const existingTargetEnv = existingConfig.targetEnvironment as string | undefined;
const newTargetEnv = newConfig.targetEnvironment as string | undefined;
const wildcardValues = ["*", ""];
if (
(newConfig.scope as string) === "group"
? existingConfig.groupId !== newConfig.groupId
: existingConfig.projectId !== newConfig.projectId
)
return false;
// If either has wildcard, it conflicts with any targetEnvironment
if (
!existingTargetEnv ||
!newTargetEnv ||
wildcardValues.includes(existingTargetEnv) ||
wildcardValues.includes(newTargetEnv)
) {
return true;
}
return existingTargetEnv === newTargetEnv;
},
[SecretSync.CloudflarePages]: defaultDuplicateCheck,
[SecretSync.CloudflareWorkers]: defaultDuplicateCheck,
[SecretSync.Supabase]: defaultDuplicateCheck,
[SecretSync.Zabbix]: defaultDuplicateCheck,
[SecretSync.Railway]: defaultDuplicateCheck,
[SecretSync.Checkly]: defaultDuplicateCheck,
[SecretSync.DigitalOceanAppPlatform]: defaultDuplicateCheck,
[SecretSync.Netlify]: defaultDuplicateCheck,
[SecretSync.Bitbucket]: defaultDuplicateCheck
};
@@ -12,6 +12,7 @@ import {
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { DatabaseErrorCode } from "@app/lib/error-codes";
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
import { deepEqualSkipFields } from "@app/lib/fn/object";
import { OrgServiceActor } from "@app/lib/types";
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
@@ -20,6 +21,7 @@ import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { enterpriseSyncCheck, listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns";
import {
SecretSyncStatus,
TCheckDuplicateDestinationDTO,
TCreateSecretSyncDTO,
TDeleteSecretSyncDTO,
TFindSecretSyncByIdDTO,
@@ -35,7 +37,12 @@ import {
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
import { TSecretSyncDALFactory } from "./secret-sync-dal";
import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "./secret-sync-maps";
import {
DESTINATION_DUPLICATE_CHECK_MAP,
SECRET_SYNC_CONNECTION_MAP,
SECRET_SYNC_NAME_MAP,
SECRET_SYNC_SKIP_FIELDS_MAP
} from "./secret-sync-maps";
import { TSecretSyncQueueFactory } from "./secret-sync-queue";
type TSecretSyncServiceFactoryDep = {
@@ -696,6 +703,61 @@ export const secretSyncServiceFactory = ({
return updatedSecretSync as TSecretSync;
};
const checkDuplicateDestination = async (
{ destination, destinationConfig, excludeSyncId, projectId }: TCheckDuplicateDestinationDTO,
actor: OrgServiceActor
) => {
const skipFields = SECRET_SYNC_SKIP_FIELDS_MAP[destination];
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionSecretSyncActions.Read,
ProjectPermissionSub.SecretSyncs
);
if (!destinationConfig || Object.keys(destinationConfig).length === 0) {
return { hasDuplicate: false, duplicateProjectId: undefined };
}
try {
const existingSyncs = await secretSyncDAL.findByDestinationAndOrgId(destination, actor.orgId);
const duplicates = existingSyncs.filter((sync) => {
if (sync.id === excludeSyncId) {
return false;
}
try {
const baseFieldsMatch = deepEqualSkipFields(sync.destinationConfig, destinationConfig, skipFields);
if (baseFieldsMatch) {
return DESTINATION_DUPLICATE_CHECK_MAP[destination](
sync.destinationConfig as Record<string, unknown>,
destinationConfig
);
}
return false;
} catch {
return false;
}
});
const hasDuplicate = duplicates.length > 0;
return {
hasDuplicate,
duplicateProjectId: hasDuplicate ? duplicates[0].projectId : undefined
};
} catch (error) {
return { hasDuplicate: false, duplicateProjectId: undefined };
}
};
return {
listSecretSyncOptions,
listSecretSyncsByProjectId,
@@ -707,6 +769,7 @@ export const secretSyncServiceFactory = ({
deleteSecretSync,
triggerSecretSyncSyncSecretsById,
triggerSecretSyncImportSecretsById,
triggerSecretSyncRemoveSecretsById
triggerSecretSyncRemoveSecretsById,
checkDuplicateDestination
};
};
@@ -324,6 +324,13 @@ export type TDeleteSecretSyncDTO = {
removeSecrets: boolean;
};
export type TCheckDuplicateDestinationDTO = {
destination: SecretSync;
destinationConfig: Record<string, unknown>;
excludeSyncId?: string;
projectId: string;
};
export enum SecretSyncStatus {
Pending = "pending",
Running = "running",
@@ -408,3 +415,8 @@ export type TSecretMap = Record<
secretMetadata?: ResourceMetadataDTO;
}
>;
export type DestinationDuplicateCheckFn = (
existingConfig: Record<string, unknown>,
newConfig: Record<string, unknown>
) => boolean;