Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
8aac966
Add db schema
joeauyeung Dec 9, 2025
6e50f46
Add `CredentialRepository.findByTeamIdAndSlugs`
joeauyeung Dec 9, 2025
dd776bf
Add enabled app slugs for attribute syncing
joeauyeung Dec 9, 2025
49e3f33
Create repository for `IntegrationAttributeSync`
joeauyeung Dec 9, 2025
2c897bf
Create zod schemas
joeauyeung Dec 9, 2025
4968b21
Create `AttributeSyncUserRuleOutputMapper`
joeauyeung Dec 9, 2025
deeec8e
Create `IntegrationAttributeSyncService`
joeauyeung Dec 9, 2025
25ca942
Create DI contianer
joeauyeung Dec 9, 2025
7b178e9
Create trpc endpoints
joeauyeung Dec 9, 2025
09ac928
Create page
joeauyeung Dec 9, 2025
c0a71d2
Include team name in `CredentialRepository.findByTeamIdAndSlugs`
joeauyeung Dec 11, 2025
22dbcfb
Update schema and relations
joeauyeung Dec 11, 2025
0264a9c
Update types and schemas
joeauyeung Dec 11, 2025
80144ab
Add more methods to IntegrationAttributeSyncRepository
joeauyeung Dec 11, 2025
8d0d61f
Add more services to `IntegrationAttributeSyncService`
joeauyeung Dec 11, 2025
fd39eae
Refactor `getTeams.handler` to use repository
joeauyeung Dec 11, 2025
e83a8e4
Create `createAttributeSync` trpc endpoint
joeauyeung Dec 11, 2025
b0eba94
Create `updateAttributeSync` trpc endpoint
joeauyeung Dec 11, 2025
a4796d3
Add router to trpc
joeauyeung Dec 11, 2025
ccbf84e
Create attribute sync child components
joeauyeung Dec 11, 2025
3ce313d
Pass custom actions to `FormCard`
joeauyeung Dec 11, 2025
98af081
Create `IntegrationAttributeSyncCard`
joeauyeung Dec 11, 2025
3303c2f
Pass inital props via server side
joeauyeung Dec 11, 2025
9cc2e5a
Merge branch 'main' into attribute-sync-ui
joeauyeung Dec 15, 2025
3fd9430
Fix prop
joeauyeung Dec 17, 2025
a6a4dc8
Only refetch on mutation
joeauyeung Dec 17, 2025
96802ed
Fixes
joeauyeung Dec 17, 2025
bbce738
Add form error when duplicate field and attribute combo
joeauyeung Dec 17, 2025
013c51e
Add `updateTransactionWithRUleAndMappings` logic
joeauyeung Dec 17, 2025
327ea66
Adjust zod schemas
joeauyeung Dec 17, 2025
19c8423
Service add `updateIncludeRulesAndMappings`
joeauyeung Dec 17, 2025
652c87c
Pass orgId from server to component
joeauyeung Dec 17, 2025
daad1d5
Rename types
joeauyeung Dec 17, 2025
5d176bd
Add deleteById method to repository
joeauyeung Dec 17, 2025
da07950
Add name to integrationAttributeSync
joeauyeung Dec 17, 2025
48fe98c
Add deleteById method to service
joeauyeung Dec 17, 2025
fece321
Rename method
joeauyeung Dec 17, 2025
920bd80
Add deleteAttributeSync trpc endpoint
joeauyeung Dec 17, 2025
6857276
Make the IntegrationAttributeSyncCard a dummy component
joeauyeung Dec 17, 2025
8fc4eaf
test: add tests for IntegrationAttributeSync feature
devin-ai-integration[bot] Dec 18, 2025
6d98c8b
Move creating a attribute sync record to the service
joeauyeung Dec 18, 2025
26889b6
Add i18n strings
joeauyeung Dec 18, 2025
3b9f334
Safe select credential in find by id and team
joeauyeung Dec 18, 2025
eea3710
Fix default credentialId value in form
joeauyeung Dec 18, 2025
c43f6ad
Update repository return types
joeauyeung Dec 18, 2025
9113610
Add i18n string
joeauyeung Dec 18, 2025
53c50ef
Make credentialId optional for form schema
joeauyeung Dec 18, 2025
24255e6
Fix label
joeauyeung Dec 18, 2025
ef3b4a5
Add cascade deletes
joeauyeung Dec 18, 2025
767e5dc
Add verification that syncs belong to org
joeauyeung Dec 18, 2025
60f4171
Create mapper for repository output
joeauyeung Dec 18, 2025
cf0cbe0
Type fixes
joeauyeung Dec 18, 2025
1c0b033
Remove old test file
joeauyeung Dec 18, 2025
04f60d7
Pass `attributeOptions` from parent to children
joeauyeung Dec 19, 2025
08cd9a0
Infer types from zod schema
joeauyeung Dec 19, 2025
fec867f
Type fixes
joeauyeung Dec 19, 2025
947d91e
Type fix
joeauyeung Dec 19, 2025
28ed00d
Clean up
joeauyeung Dec 19, 2025
9b0d437
Add i18n strings
joeauyeung Dec 19, 2025
898542f
Remove unused file
joeauyeung Dec 19, 2025
ad65b71
Address feedback
joeauyeung Dec 19, 2025
b288081
Merge branch 'main' into attribute-sync-ui
joeauyeung Dec 19, 2025
1a3abbc
Add migration file
joeauyeung Dec 19, 2025
fce660a
Address feedback
joeauyeung Dec 19, 2025
e37b5d8
Add validation for new integration values
joeauyeung Dec 19, 2025
a83c2e9
Remove unused router
joeauyeung Dec 19, 2025
544877d
Move away from z.infer to z.ZodType
joeauyeung Dec 20, 2025
5c43536
Clean up comments
joeauyeung Dec 20, 2025
8c38861
Type fix
joeauyeung Dec 20, 2025
2eceb83
Type fixes
joeauyeung Dec 20, 2025
dda286d
Type fix
joeauyeung Dec 23, 2025
ba5d4a4
Merge branch 'main' into attribute-sync-ui
joeauyeung Dec 23, 2025
ad1e092
fix: add passthrough to syncFormDataSchema to preserve extra fields
devin-ai-integration[bot] Dec 23, 2025
4f1ce3f
fix: remove incorrect test that expected extra fields to pass through…
devin-ai-integration[bot] Dec 23, 2025
c2252a6
Add endpoint for SF to call
joeauyeung Dec 31, 2025
e475921
Create scratch org config
joeauyeung Dec 31, 2025
2fc58dc
Create sf cli scripts
joeauyeung Dec 31, 2025
efa9260
Create package logic
joeauyeung Dec 31, 2025
e28dc1d
Update README
joeauyeung Dec 31, 2025
7e549d3
Remove unused file
joeauyeung Dec 31, 2025
ebf508c
Add indexes
joeauyeung Dec 31, 2025
3c0a79d
Add aria label
joeauyeung Dec 31, 2025
6ff5680
Address feedback - consistent validation
joeauyeung Dec 31, 2025
d60966d
Merge branch 'main' into attribute-sync-ui
joeauyeung Dec 31, 2025
2d336b2
Merge branch 'main' into attribute-sync-ui
joeauyeung Dec 31, 2025
4578077
Fix import paths for attribute types
joeauyeung Dec 31, 2025
a8e4ede
Merge branch 'attribute-sync-ui' into init-salesforce-package
joeauyeung Jan 6, 2026
dad327a
Merge branch 'main' into attribute-sync-ui
joeauyeung Jan 6, 2026
a41af8e
Merge branch 'attribute-sync-ui' into init-salesforce-package
joeauyeung Jan 6, 2026
baca20b
refactor: change attributeSyncRules array to singular attributeSyncRule
devin-ai-integration[bot] Jan 6, 2026
4a516ad
Merge branch 'attribute-sync-ui' into init-salesforce-package
joeauyeung Jan 6, 2026
1b88faf
Merge branch 'main' into attribute-sync-ui
joeauyeung Jan 9, 2026
1034c4f
Merge branch 'attribute-sync-ui' into init-salesforce-package
joeauyeung Jan 9, 2026
f02a534
Merge branch 'main' into attribute-sync-ui
joeauyeung Jan 12, 2026
6001e66
feat: implement FeatureOptInService (#25805)
eunjae-lee Jan 6, 2026
5806f18
Prevent duplicate field and attribute mappings
joeauyeung Jan 12, 2026
f08c12a
Add validation that attribute belongs to the org
joeauyeung Jan 12, 2026
7cf9599
Merge branch 'main' into attribute-sync-ui
joeauyeung Jan 12, 2026
c3ff202
fix: address Cubic AI review feedback
devin-ai-integration[bot] Jan 12, 2026
8a0ba2d
fix: address remaining cubic review feedback
devin-ai-integration[bot] Jan 12, 2026
af72704
Pull test file from main
joeauyeung Jan 12, 2026
5cb22e1
Merge branch 'attribute-sync-ui' into init-salesforce-package
joeauyeung Jan 12, 2026
e45a7ed
fix: address Cubic AI review feedback (confidence >= 9/10)
devin-ai-integration[bot] Jan 12, 2026
6a0ff44
Merge remote-tracking branch 'origin/main' into init-salesforce-package
hariombalhara Jan 13, 2026
c536b19
Update README
joeauyeung Jan 13, 2026
02f85cf
Merge branch 'main' into init-salesforce-package
joeauyeung Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions packages/app-store/salesforce/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
## Working With GraphQL
# Creating a Salesforce test org
You must have the [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli) installed to create a test org. Once installed, you can create a test org using the following command `yarn scratch-org:create`

This will create a scratch org with the configuration specified in the `project-scratch-def.json` file.

To open a browser tab to the org, run `yarn scratch-org:start`

# Working With GraphQL
This package utilizes [`GraphQL Codegen`](https://the-guild.dev/graphql/codegen#graphql-codegen) to generate types and queries from the Salesforce schema.

### Generating GraphQL Schema
Currently v63 of the Salesforce graphql endpoint throws an error when trying to generate files. This is due to the `Setup__JoinInput` type not generating any fields. To work around this, the `schema.json` file comes from the [Salesforce graphql introspection query](https://www.postman.com/salesforce-developers/salesforce-developers/request/sy8qaf9/introspection-query). This file is then converted to a SDL file using the [graphql-introspection-json-to-sdl](https://github.com/Calcom/graphql-introspection-json-to-sdl) package. You can generate the SDL file by running `yarn generate:schema`.

### Generating Queries
When working with graphql files ensure that `yarn codegen:watch` is running in the background. This will generate the types and queries from the SDL file.
When working with graphql files ensure that `yarn codegen:watch` is running in the background. This will generate the types and queries from the SDL file.

# Developing the SFDC package
The SFDC package is written using Apex. To develop this package, you need to have the Salesforce CLI installed. Then you can run `yarn sfdc:deploy:preview` to see what changes will be deployed to the scratch org. Running `yarn sfdc:deploy` will deploy the changes to the scratch org.

Note that if you want to call your local development instances you need to change the "Named Credential" on the scratch org settings to point the `CalCom_Development` credential to the local instance.
1 change: 1 addition & 0 deletions packages/app-store/salesforce/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as add } from "./add";
export { default as callback } from "./callback";
export { default as "user-sync" } from "./user-sync";
25 changes: 25 additions & 0 deletions packages/app-store/salesforce/api/user-sync.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for this endpoint will be in a stacked PR

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from "next";

import logger from "@calcom/lib/logger";

const log = logger.getSubLogger({ prefix: ["[salesforce/user-sync]"] });

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}

const { instanceUrl, orgId, salesforceUserId, email, changedFields, timestamp } = req.body;

log.info("Received user sync request", {
instanceUrl,
orgId,
salesforceUserId,
timestamp,
});

// TODO: Validate instanceUrl + orgId against stored credentials
// TODO: Sync changedFields to Cal.com user

return res.status(200).json({ success: true });
}
6 changes: 5 additions & 1 deletion packages/app-store/salesforce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
"scripts": {
"generate:schema": "graphql-introspection-json-to-sdl ./schema.json > ./schema.graphql",
"codegen": "graphql-codegen --config codegen.ts",
"codegen:watch": "graphql-codegen --config codegen.ts --watch"
"codegen:watch": "graphql-codegen --config codegen.ts --watch",
"scratch-org:create": "sf org create scratch --definition-file scratch-org-def.json --alias calcom-test --duration-days 7 --set-default",
"scratch-org:start": "sf org open --target-org calcom-test",
"sfdc:deploy": "cd sfdc-package && sf project deploy start",
"sfdc:deploy:preview": "cd sfdc-package && sf project deploy preview"
Comment on lines 11 to 14
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scripts to help with package development

},
"dependencies": {
"@calcom/lib": "workspace:*",
Expand Down
6 changes: 6 additions & 0 deletions packages/app-store/salesforce/scratch-org-def.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"orgName": "Cal.com Test Org",
"edition": "Developer",
"features": [],
"settings": {}
}
12 changes: 12 additions & 0 deletions packages/app-store/salesforce/sfdc-package/.forceignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status
# More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm
#

package.xml

# LWC configuration files
**/jsconfig.json
**/.eslintrc.json

# LWC Jest
**/__tests__/**
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The files in sfdc-package the files are written using Apex https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dev_guide.htm

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
public with sharing class CalComCalloutQueueable implements Queueable, Database.AllowsCallouts {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For SF triggers, we need this class to call the endpoint on Cal


private static final String ENDPOINT_PATH = '/api/integrations/salesforce/user-sync';

private List<UserChangePayload> payloads;

public CalComCalloutQueueable(List<UserChangePayload> payloads) {
this.payloads = payloads;
}

public void execute(QueueableContext context) {
String namedCredential = getNamedCredential();

for (UserChangePayload payload : payloads) {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Sequential HTTP callouts in a loop may cause performance issues or timeout errors with multiple payloads. Consider checking Limits.getCallouts() before each callout and potentially chaining to a new Queueable job if the limit is approached, or batch payloads into a single API request if the endpoint supports it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/CalComCalloutQueueable.cls, line 14:

<comment>Sequential HTTP callouts in a loop may cause performance issues or timeout errors with multiple payloads. Consider checking Limits.getCallouts() before each callout and potentially chaining to a new Queueable job if the limit is approached, or batch payloads into a single API request if the endpoint supports it.</comment>

<file context>
@@ -0,0 +1,54 @@
+    public void execute(QueueableContext context) {
+        String namedCredential = getNamedCredential();
+
+        for (UserChangePayload payload : payloads) {
+            sendPayload(namedCredential, payload);
+        }
</file context>
Fix with Cubic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid point. I think we should batch the payloads if possible and send multiple of them in a single salesforce/user-sync request.

Queueable seems to have a limit of 100 callouts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I've saved this as a new learning to improve future reviews.

sendPayload(namedCredential, payload);
}
}

private String getNamedCredential() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Named Credentials are like env variables set at the Salesforce level. This method gets either the production or the development URL to send the updated user data to.

Boolean isSandbox = [SELECT IsSandbox FROM Organization LIMIT 1].IsSandbox;
return isSandbox ? 'callout:CalCom_Development' : 'callout:CalCom_Production';
}

private void sendPayload(String namedCredential, UserChangePayload payload) {
try {
HttpRequest req = new HttpRequest();
req.setEndpoint(namedCredential + ENDPOINT_PATH);
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(payload));
req.setTimeout(30000);

Http http = new Http();
HttpResponse res = http.send(req);

if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
System.debug(LoggingLevel.INFO, 'Successfully sent user update to Cal.com for user: ' + payload.salesforceUserId);
} else {
System.debug(LoggingLevel.ERROR, 'Failed to send user update to Cal.com. Status: ' + res.getStatusCode() + ' Body: ' + res.getBody());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we plan to handle failures in call out? Will we retry them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's one option but I think warrants a discussion

}
} catch (Exception e) {
System.debug(LoggingLevel.ERROR, 'Exception sending user update to Cal.com: ' + e.getMessage());
}
}

public class UserChangePayload {
public Id salesforceUserId;
public String email;
public String instanceUrl;
public String orgId;
public Map<String, Object> changedFields;
public String timestamp;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each class needs a metadata file for deployment.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>64.0</apiVersion>
<status>Active</status>
</ApexClass>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class determines which fields were changed and add it to the payload

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
public with sharing class UserUpdateHandler {

public static void handleAfterUpdate(Map<Id, User> oldMap, Map<Id, User> newMap) {
List<CalComCalloutQueueable.UserChangePayload> payloads = new List<CalComCalloutQueueable.UserChangePayload>();

for (Id userId : newMap.keySet()) {
User oldUser = oldMap.get(userId);
User newUser = newMap.get(userId);

Map<String, Object> changedFields = getChangedFields(oldUser, newUser);

if (!changedFields.isEmpty()) {
CalComCalloutQueueable.UserChangePayload payload = new CalComCalloutQueueable.UserChangePayload();
payload.salesforceUserId = userId;
payload.email = newUser.Email;
payload.instanceUrl = URL.getOrgDomainUrl().toExternalForm();
payload.orgId = UserInfo.getOrganizationId();
Comment on lines +16 to +17
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Cal's side, the mapping is associated with a credential on Cal's side. We can use the instanceUrl and the orgId from Salesforce and the credential to verify valid payloads.

payload.changedFields = changedFields;
payload.timestamp = Datetime.now().formatGMT('yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'');
payloads.add(payload);
}
}

if (!payloads.isEmpty()) {
System.enqueueJob(new CalComCalloutQueueable(payloads));
}
}

private static Map<String, Object> getChangedFields(User oldUser, User newUser) {
Map<String, Object> changedFields = new Map<String, Object>();

Map<String, Object> oldFields = oldUser.getPopulatedFieldsAsMap();
Map<String, Object> newFields = newUser.getPopulatedFieldsAsMap();

for (String fieldName : newFields.keySet()) {
Object oldValue = oldFields.get(fieldName);
Object newValue = newFields.get(fieldName);

if (oldValue != newValue) {
changedFields.put(fieldName, newValue);
}
Comment on lines +35 to +41
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Change detection misses cleared/nullified fields. When a User field is set to null, it won't appear in newFields.keySet() (since getPopulatedFieldsAsMap() only returns populated fields), so the change won't be detected. This causes field deletions to not sync to Cal.com.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/UserUpdateHandler.cls, line 35:

<comment>Change detection misses cleared/nullified fields. When a User field is set to null, it won't appear in `newFields.keySet()` (since `getPopulatedFieldsAsMap()` only returns populated fields), so the change won't be detected. This causes field deletions to not sync to Cal.com.</comment>

<file context>
@@ -0,0 +1,46 @@
+        Map<String, Object> oldFields = oldUser.getPopulatedFieldsAsMap();
+        Map<String, Object> newFields = newUser.getPopulatedFieldsAsMap();
+
+        for (String fieldName : newFields.keySet()) {
+            Object oldValue = oldFields.get(fieldName);
+            Object newValue = newFields.get(fieldName);
</file context>

Fix confidence (alpha): 8/10

Suggested change
for (String fieldName : newFields.keySet()) {
Object oldValue = oldFields.get(fieldName);
Object newValue = newFields.get(fieldName);
if (oldValue != newValue) {
changedFields.put(fieldName, newValue);
}
Set<String> allFields = new Set<String>();
allFields.addAll(oldFields.keySet());
allFields.addAll(newFields.keySet());
for (String fieldName : allFields) {
Object oldValue = oldFields.get(fieldName);
Object newValue = newFields.get(fieldName);
Fix with Cubic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems valid concern again @joeauyeung

}

return changedFields;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>64.0</apiVersion>
<status>Active</status>
</ApexClass>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>CalCom_Development</fullName>
<label>Cal.com Development</label>
<endpoint>https://app.cal.com</endpoint>
<principalType>Anonymous</principalType>
<protocol>Custom</protocol>
<allowMergeFieldsInBody>false</allowMergeFieldsInBody>
<allowMergeFieldsInHeader>true</allowMergeFieldsInHeader>
<generateAuthorizationHeader>false</generateAuthorizationHeader>
<calloutStatus>Enabled</calloutStatus>
</NamedCredential>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>CalCom_Production</fullName>
<label>Cal.com Production</label>
<endpoint>https://app.cal.com</endpoint>
<principalType>Anonymous</principalType>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Critical security vulnerability: Named Credential sends sensitive user data without authentication. The configuration uses principalType="Anonymous" and generateAuthorizationHeader="false", and the callout code doesn't add any Authorization header or API key. This allows anyone who discovers the endpoint to send unauthorized data to Cal.com. Add authentication using either Named Credential's per-user or per-org authentication, or implement API key authentication in the HTTP request headers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/salesforce/sfdc-package/force-app/main/default/namedCredentials/CalCom_Production.namedCredential-meta.xml, line 6:

<comment>Critical security vulnerability: Named Credential sends sensitive user data without authentication. The configuration uses principalType="Anonymous" and generateAuthorizationHeader="false", and the callout code doesn't add any Authorization header or API key. This allows anyone who discovers the endpoint to send unauthorized data to Cal.com. Add authentication using either Named Credential's per-user or per-org authentication, or implement API key authentication in the HTTP request headers.</comment>

<file context>
@@ -0,0 +1,12 @@
+    <fullName>CalCom_Production</fullName>
+    <label>Cal.com Production</label>
+    <endpoint>https://app.cal.com</endpoint>
+    <principalType>Anonymous</principalType>
+    <protocol>Custom</protocol>
+    <allowMergeFieldsInBody>false</allowMergeFieldsInBody>
</file context>
Fix with Cubic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is planned for stacked PR I believe

<protocol>Custom</protocol>
<allowMergeFieldsInBody>false</allowMergeFieldsInBody>
<allowMergeFieldsInHeader>true</allowMergeFieldsInHeader>
<generateAuthorizationHeader>false</generateAuthorizationHeader>
<calloutStatus>Enabled</calloutStatus>
</NamedCredential>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trigger UserUpdateTrigger on User (after update) {
UserUpdateHandler.handleAfterUpdate(Trigger.oldMap, Trigger.newMap);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexTrigger xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>64.0</apiVersion>
<status>Active</status>
</ApexTrigger>
12 changes: 12 additions & 0 deletions packages/app-store/salesforce/sfdc-package/sfdx-project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"packageDirectories": [
{
"path": "force-app",
"default": true
}
],
"name": "calcom-user-sync",
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "64.0"
}
Loading