fix: validate credential ownership in attribute sync update (#28873)

This commit is contained in:
Pedro Castro
2026-04-14 08:29:26 -03:00
committed by GitHub
parent 51e852f671
commit cbb234cb35
3 changed files with 35 additions and 2 deletions
@@ -63,6 +63,7 @@ export interface ISyncFormData {
id: string;
name: string;
credentialId?: number;
integration?: AttributeSyncIntegrations;
enabled: boolean;
organizationId: number;
ruleId: string;
@@ -138,7 +139,7 @@ export interface IIntegrationAttributeSyncUpdateParams {
integrationAttributeSync: Omit<
IntegrationAttributeSync,
"attributeSyncRule" | "syncFieldMappings" | "integration"
>;
> & { integration?: AttributeSyncIntegrations };
attributeSyncRule: AttributeSyncRule;
fieldMappingsToCreate: Omit<AttributeSyncFieldMapping, "id">[];
fieldMappingsToUpdate: AttributeSyncFieldMapping[];
@@ -217,4 +217,11 @@ export class IntegrationAttributeSyncService {
async getAllByCredentialId(credentialId: number) {
return this.deps.integrationAttributeSyncRepository.getAllByCredentialId(credentialId);
}
async validateCredentialBelongsToOrg(credentialId: number, organizationId: number) {
return this.deps.credentialRepository.findByIdAndTeamId({
id: credentialId,
teamId: organizationId,
});
}
}
@@ -1,4 +1,8 @@
import { getIntegrationAttributeSyncService } from "@calcom/ee/integration-attribute-sync/di/IntegrationAttributeSyncService.container";
import {
AttributeSyncIntegrations,
type ISyncFormData,
} from "@calcom/ee/integration-attribute-sync/repositories/IIntegrationAttributeSyncRepository";
import {
DuplicateAttributeWithinSyncError,
DuplicateAttributeAcrossSyncsError,
@@ -37,8 +41,29 @@ const updateAttributeSyncHandler = async ({ ctx, input }: UpdateAttributeSyncOpt
throw new TRPCError({ code: "UNAUTHORIZED" });
}
// Never trust user-supplied organizationId — override with authenticated user's org
const safeInput: ISyncFormData = { ...input, organizationId: org.id };
// Validate credential belongs to the user's organization and derive integration type
if (input.credentialId !== undefined) {
const credential = await integrationAttributeSyncService.validateCredentialBelongsToOrg(
input.credentialId,
org.id
);
if (!credential) {
throw new TRPCError({ code: "NOT_FOUND", message: "Credential not found" });
}
const integrationValue = credential.app?.slug || credential.type;
if (!Object.values(AttributeSyncIntegrations).includes(integrationValue as AttributeSyncIntegrations)) {
throw new TRPCError({ code: "BAD_REQUEST", message: `Unsupported integration type: ${integrationValue}` });
}
safeInput.integration = integrationValue as AttributeSyncIntegrations;
}
try {
await integrationAttributeSyncService.updateIncludeRulesAndMappings(input);
await integrationAttributeSyncService.updateIncludeRulesAndMappings(safeInput);
} catch (error) {
if (error instanceof DuplicateAttributeWithinSyncError) {
throw new TRPCError({