CRUD / FLS enforcement
-
All SOQL queries in
@AuraEnabledmethods useWITH SECURITY_ENFORCED -
All DML operations in controllers are gated by
isCreateable(),isUpdateable(),isDeletable()checks -
CIO_SchemaHelpervalidatesisAccessible()before returning objects/fields to the UI -
Subquery and relationship-traversal queries catch
QueryExceptionand degrade gracefully when the running user is missing FLS
Sharing model
-
All controller classes use
with sharing(respects org-wide defaults and sharing rules) -
Service classes that run in async context use
inherited sharing -
No class uses
without sharing
SOQL injection prevention
-
Admin-supplied filter text on
CIO_Scheduled_Sync__c.SOQL_Filter__cis parsed and validated byCIO_SoqlFilterValidatorbefore execution. Strict whitelist grammar — only safe operators, validated field names, escaped string literals, no sub-SELECTs, no comments, no semicolons. -
CIO_RecruiterDashboardServiceresolves the configurable owner field against a cached schema-validated allowlist; native SOQL with bind variables is dispatched over the canonical name returned bySchema.SObjectField.getDescribe().getName(). -
CIO_SourceConfigController.saveSourceMappingsdeletes existing mappings via a hardcodedswitchover the five legitimate lookup-field branches — never via dynamicDatabase.query. - All other dynamic SOQL uses bind variables; user-supplied values are never string-concatenated.
API key security
-
API_Key__c(Pipelines CDP write key) andApp_API_Key__c(App API bearer token) areEncryptedTextfields. Salesforce encrypts them at rest with platform encryption. -
Subscriber-org users see the masked form (
*****) unless they have "View Encrypted Data" permission. - The Settings UI returns a fully-asterisk-masked value to the LWC; the unmasked key is never sent over the wire to the browser.
-
CIO_SettingsController.saveSettingsonly updates the stored key when the submitted value does not contain*— i.e. the admin actually re-typed a new key. -
API keys are scrubbed from any persisted activity-log body via
CIO_Logger.sanitizeBody().
Webhook security
-
Inbound webhooks at
/services/apexrest/cio/webhook/*verify HMAC-SHA256 signatures using a per-config secret stored onCIO_Webhook_Config__c.Secret_Key__c. -
Signature comparison is constant-time (
CIO_WebhookReceiver.constantTimeBlobEquals) to defeat timing-side-channel attacks. - Malformed signatures are rejected without echoing parser errors back to the caller.
-
Internal exceptions never leak to the response body — handler returns a
generic
{"error": "Internal server error"}and logs detail server-side. -
All response bodies are built with
JSON.serialize(Map<String, Object>), never with string concatenation.
Data retention & PII handling
-
CIO_Activity_Log__cretention is governed byLog_Body_Retention_Days__c(default 30 days, minimum 7). -
The daily
CIO_LogPurgeBatch(Schedulable) deletes records older than the cutoff. -
Set
Log_Bodies_Enabled__c = falseto skip body capture entirely. The log row is still created (operational visibility) but bodies arenull. -
The on-demand purge endpoint enforces a 7-day floor, requires
isDeletable()on the log object, and writes an audit record before deletion. - Activity log list views do not expose request/response bodies (only available in detail view).
-
CIO_ReadOnlypermission set excludesRequest_Body__candResponse_Body__cfields. - Error messages returned to the client are generic (no stack traces or internal details).
No third-party data sharing
CIO Pipelines does not share PII with any third party other than Customer.io itself, which is the integration target. We do not:
- Send analytics events to external services
- Ship telemetry from subscriber orgs to Creative Round
- Contact any URL other than the configured Customer.io endpoints
- Ship usage reports off the customer's Salesforce org
The only outbound HTTP traffic is to:
-
cdp.customer.ioorcdp-eu.customer.io(CDP writes) -
api.customer.ioorapi-eu.customer.io(App API reads, via Named Credential)
Both endpoints are pre-registered as Remote Site Settings with SSL enforced.
Permission sets
CIO_Admin
-
Full CRUD on
CIO_Event_Trigger__c,CIO_Field_Mapping__c,CIO_Activity_Log__c - Read/write access to all non-required custom fields
-
Access to
CIO_Settings__ccustom setting (API credentials) -
View Encrypted Data(so admins can edit and round-trip API keys) CIO_Hometab visibility
CIO_ReadOnly
- Read-only access to event triggers, field mappings, and activity log
- No access to request/response body fields (PII protection)
- No access to
CIO_Settings__ccustom setting
Encryption & transport
-
All credentials at rest: Salesforce platform encryption (AES-256) on
EncryptedTextfields - All API traffic in transit: TLS via Salesforce HTTP callouts + Named Credentials
- Platform encryption applies to the underlying database; backup/restore inherits encryption
Testing
- 36 test classes cover 40 production Apex classes
-
All tests run in managed-package context (
System.runAs(CIO_TestDataFactory.getAdminUser())) -
HTTP callouts are mocked via
CIO_HttpCalloutMock -
No test uses
@isTest(SeeAllData=true) - Negative-path tests for the SOQL filter validator, webhook HMAC verification, and FLS enforcement
Reporting a vulnerability
Email security findings to [email protected]. We will respond within 2 business days and coordinate disclosure with affected installations.
Need a SOC 2 questionnaire response or a custom DPA? Available on the Enterprise tier.
Talk to us