Skip to main content

Salesforce CRM Analytics detection

Vulkro's CRM Analytics detector pack (src/security/sf_crm_analytics.rs) scans Salesforce CRM Analytics artifacts (formerly Tableau CRM, formerly Einstein Analytics). The detector activates when an SFDX project contains *.saql files, *.wdf.json / *.wdf dataflow definitions, *.tcrm.json / *.dashboard files, a force-app/main/default/wave/ directory, or a force-app/main/default/dashboards/ directory.

Rules

IDSeverityWhat it catches
CRMA-001HighSAQL injection in dataset filter. A q = filter q by {{...$user_input...}} shape (or any SAQL template that interpolates a non-literal request-derived value) lets the caller alter the dataset filter at runtime.
CRMA-002HighDashboard JSON binding to a tainted source step. A binding rule whose source is a free-text input widget without a static-set or whitelisted-values constraint. Users can pass arbitrary SAQL fragments via the widget.
CRMA-003HighDataflow row-level security disabled (rowLevelSecurityFilter = "true") or absent on a dataset whose schema references PII fields. RLS is the only enforcement layer between an analyst with dataset access and the underlying customer data.
CRMA-004MediumLens / dashboard with sharing = "Public" on top of a dataset that references PII-flavoured fields (email, phone, ssn, dateOfBirth). Public sharing on a CRM Analytics asset bypasses the org's normal sharing rules for that asset.
CRMA-005MediumHardcoded SF user / record IDs in dashboard JSON. 15 or 18 character SF IDs that only resolve in one org; the dashboard breaks on every clone / sandbox refresh.

Pro gate

Pro-only. Gated by license::Feature::CrmAnalytics. The vulkro scan invocation detects CRM Analytics artifacts in the project and prompts for a Pro license before running the CRMA-* rules. Native scanners for the project's other languages run unchanged.

Example finding

Severity: High
Rule: CRMA-001
File: force-app/main/default/wave/Customer_360.saql
Line: 17
Message: CRMA-001: SAQL injection in dataset filter. The `q =
filter q by` clause interpolates `${userInput.region}`,
which derives from a dashboard widget without a
whitelisted-values constraint.

See also