Compliance Check
Available in sfp-pro.
The compliance check functionality ensures your Salesforce metadata adheres to organizational standards and best practices. This feature helps maintain code quality, security, and consistency across your Salesforce project by enforcing configurable rules.
Overview
Compliance checking provides a comprehensive framework for:
- Enforcing coding standards and best practices
- Preventing security vulnerabilities like hardcoded IDs and URLs
- Maintaining API version consistency
- Validating metadata field values against organizational policies
- Generating detailed violation reports with remediation guidance
How It Works
The compliance checker:
- Loads rules from your configuration file (defaults to
config/compliance-rules.yaml) - Scans metadata components using Salesforce's ComponentSet API
- Applies rules based on metadata type and field specifications
- Evaluates content and field values using configurable operators
- Reports violations with file locations, severity levels, and helpful messages
Configuration
Compliance checking is configured through YAML files that define rules and their enforcement. The config file is checked in this order:
--compliance-rules <path>CLI flag (absolute or relative path)config/compliance-rules.yamlrelative to the project root
sfp project:analyze --compliance-rules config/compliance-rules.yaml --fail-on complianceExtending the Built-in Preset
Add extends: default at the top of the file to inherit the built-in rules and layer your own on top. Without extends, only the rules defined in your file are used.
When extending, your rules are merged on top — matching IDs override the preset values. This lets you selectively enable preset rules and add custom ones in the same file.
Configuration File Structure
Create a compliance rules file using the generate command:
sfp project:analyze --generate-compliance-configThis creates config/compliance-rules.yaml with sample rules:
# SFP Compliance Rules Configuration
extends: default
rules:
- id: no-hardcoded-ids
enabled: true
comment: Enable this rule to prevent hardcoded Salesforce IDs
- id: no-hardcoded-urls
enabled: false
comment: Enable this rule to prevent hardcoded Salesforce URLs
- id: profile-no-modify-all
enabled: false
comment: Enable this rule to prevent profiles with Modify All Data permission
- id: custom-api-version
name: Minimum API Version Check
enabled: true
metadata:
- ApexClass
- ApexTrigger
field: ApexClass.apiVersion
operator: greater_or_equal
value: 59.0
severity: warning
message: Component should use API version 59.0 or higherBuilt-in Rules
The system includes several built-in rules that can be enabled. All are disabled by default and must be explicitly enabled.
| Rule ID | Metadata | Field | Operator | Severity |
|---|---|---|---|---|
no-hardcoded-ids | ApexClass, ApexTrigger, Flow | _content | contains_salesforce_id | error |
no-hardcoded-urls | ApexClass, ApexTrigger, Flow | _content | regex | warning |
profile-no-modify-all | Profile | userPermissions.ModifyAllData | equals | error |
flow-inactive-check | Flow | status | not_equals | warning |
permissionset-view-all-data | PermissionSet | userPermissions.ViewAllData | equals | warning |
permissionset-modify-all-data | PermissionSet | userPermissions.ModifyAllData | equals | warning |
permissionset-author-apex | PermissionSet | userPermissions.AuthorApex | equals | warning |
permissionset-customize-application | PermissionSet | userPermissions.CustomizeApplication | equals | warning |
Documentation & Metadata Quality
| Rule ID | Metadata | Field | Operator | Severity |
|---|---|---|---|---|
field-missing-description | CustomField | description | is_empty | warning |
field-missing-help-text | CustomField | inlineHelpText | is_empty | warning |
object-missing-description | CustomObject | description | is_empty | warning |
validation-rule-missing-description | ValidationRule | description | is_empty | warning |
Example: Selective Override of Built-in Preset
# config/compliance-rules.yaml
extends: default
rules:
# Enable specific preset rules by referencing their IDs
- id: no-hardcoded-ids
enabled: true
- id: profile-no-modify-all
enabled: true
# Override the severity of a preset rule
- id: flow-inactive-check
enabled: true
severity: error
# Add a completely custom rule
- id: no-test-class-prefix
name: Test Classes Must Use Correct Prefix
metadata: ApexClass
field: _content
operator: regex
value: '@isTest'
severity: warning
message: Apex test classes should be identifiableExample: Standalone Rules (No Preset)
# config/compliance-rules.yaml
# No "extends" — only these rules run
rules:
- id: require-sharing-model
name: Apex Classes Must Declare Sharing Model
metadata: ApexClass
field: _content
operator: contains
value: 'with sharing'
severity: warning
message: Declare "with sharing" or "without sharing" explicitly
- id: no-system-runAs-in-prod
name: System.runAs Not Allowed in Non-Test Code
metadata: ApexClass
field: _content
operator: regex
value: 'System\.runAs\s*\('
severity: error
message: System.runAs is only valid inside @isTest methodsCustom Rules
Define custom rules to match your specific organizational requirements:
rules:
- id: minimum-api-version
name: Enforce Minimum API Version
description: All components must use API version 59.0 or higher
enabled: true
metadata:
- ApexClass
- ApexTrigger
field: ApexClass.apiVersion
operator: greater_or_equal
value: 59.0
severity: warning
message: Component should use API version 59.0 or higherRule Configuration Options
| Field | Description | Required | Values |
|---|---|---|---|
id | Unique identifier for the rule | Yes | String |
name | Human-readable rule name | No | String |
description | Detailed rule description | No | String |
enabled | Whether the rule is active | No | true/false (default: false) |
metadata | Metadata types to check | Yes | Array of metadata type names |
field | Field path to evaluate | Yes | Dot-notation path or "_content" |
operator | Comparison operator | Yes | See operators table below |
value | Expected value for comparison | Yes | String, Number, Boolean |
severity | Violation severity level | Yes | error, warning, info |
message | Custom violation message | No | String |
comment | Internal team note; not surfaced in output | No | String |
Supported Operators
| Operator | Description | Typical value Type |
|---|---|---|
equals | Field value equals value exactly | string, boolean, number |
not_equals | Field value does not equal value | string, boolean, number |
contains | Field value contains the substring value | string |
not_contains | Field value does not contain value | string |
greater_than | Field value is numerically greater | number |
less_than | Field value is numerically less | number |
greater_or_equal | Field value is greater than or equal | number |
less_or_equal | Field value is less than or equal | number |
regex | Field value matches the regular expression value | string (regex pattern) |
is_empty | Field is absent, null, or empty string | — (value ignored) |
contains_salesforce_id | File content contains a Salesforce record ID (15 or 18 char) detected via 700+ known key prefixes | true |
The field Property
The field property controls what part of a metadata file the rule evaluates:
| Value | Behavior |
|---|---|
_content | Scans the raw text content of the file (works for Apex, Flow, any text format). |
status | Reads the top-level <status> element from the XML. |
userPermissions.ModifyAllData | Reads a nested XML path (userPermissions → ModifyAllData). |
description | Reads the <description> element. |
Dot notation traverses XML element nesting. Arrays are handled automatically — if there are multiple <userPermissions> blocks, each is evaluated independently.
XML Field Paths
For XML metadata files, use dot notation to specify field paths:
# Top-level element
field: status
# Nested XML path
field: userPermissions.ModifyAllData
# For Profile permissions
field: userPermissions.CustomizeApplicationContent Analysis
Use the special _content field to scan raw file text:
field: _content
operator: contains_salesforce_id
value: truefield: _content
operator: regex
value: '(password|secret|key)\s*=\s*["\'][^"\']+["\']'Understanding Results
The compliance check provides detailed violation reports with multiple output formats:
Console Output
📋 Compliance Check Results
═══════════════════════════
Rules Checked: 3/5
Found 5 violations:
❌ Errors (2):
• src/classes/MyClass.cls-meta.xml:3
Component should use API version 59.0 or higher
Rule: Minimum API Version Check
⚠️ Warnings (3):
• src/classes/Controller.cls:15
Hardcoded Salesforce IDs found - use Custom Settings, Custom Metadata, or SOQL queries instead
Rule: No Hardcoded Salesforce IDsViolation Details
Each violation includes:
- File Path: Exact location of the violation
- Line Number: Specific line where the issue occurs (when applicable)
- Rule Name: Which rule was violated
- Severity: Error, Warning, or Info level
- Message: Descriptive explanation and remediation guidance
- Actual Value: The found value that triggered the violation
- Expected Value: What the rule expected to find
Integration with CI/CD
Integration is limited only to GitHub at the moment. The command needs GITHUB_APP_PRIVATE_KEY and GITHUB_APP_ID to be set in environment variables for results to be reported as GitHub checks.
When integrating compliance checking in your CI/CD pipeline:
-
Enforce Compliance Standards:
sfp project:analyze --compliance-rules config/compliance-rules.yaml --fail-on compliance -
Generate Reports:
sfp project:analyze --compliance-rules config/compliance-rules.yaml --report-dir ./reports --output-format markdown -
GitHub Actions Integration:
- name: Run Compliance Check run: | sfp project:analyze --compliance-rules config/compliance-rules.yaml --fail-on compliance --output-format github env: GITHUB_APP_PRIVATE_KEY: ${{ secrets.GITHUB_APP_PRIVATE_KEY }} GITHUB_APP_ID: ${{ secrets.GITHUB_APP_ID }}
Scoping Compliance Checks
Use the same scoping options as other analysis commands:
By Package
sfp project:analyze --compliance-rules config/compliance-rules.yaml -p core,utilsBy Domain
sfp project:analyze --compliance-rules config/compliance-rules.yaml -d salesBy Source Path
sfp project:analyze --compliance-rules config/compliance-rules.yaml -s ./force-app/main/defaultBy Changed Files
Analyze only specific changed files:
# Manual specification of changed files
sfp project:analyze --changed-files "src/classes/MyClass.cls,src/lwc/myComponent/myComponent.html"In GitHub Actions PR context, the analyzer automatically detects and analyzes only changed files when no package, domain, or source path filters are specified.
Output Formats
The compliance checker supports multiple output formats:
- Console: Human-readable terminal output with color coding
- Markdown: Detailed reports suitable for documentation
- JSON: Machine-readable format for integration with other tools
- GitHub: Special format for GitHub Checks API integration
Common Compliance Scenarios
API Version Management
Ensure all components use recent API versions:
- id: minimum-api-version
enabled: true
metadata: [ApexClass, ApexTrigger]
field: ApexClass.apiVersion
operator: greater_or_equal
value: 59.0
severity: warningSecurity Hardening
Prevent security vulnerabilities:
- id: no-hardcoded-credentials
enabled: true
metadata: [ApexClass]
field: _content
operator: regex
value: '(password|secret|key)\s*=\s*["\'][^"\']+["\']'
severity: error
message: Remove hardcoded credentials - use Named Credentials or Custom SettingsProfile Security
Restrict dangerous permissions:
- id: no-modify-all-data
enabled: true
metadata: [Profile]
field: Profile.userPermissions.ModifyAllData
operator: equals
value: true
severity: error
message: Profile should not have Modify All Data permissionLogging and Debugging
Use different log levels to control output verbosity:
# Basic progress information
sfp project:analyze --compliance-rules config/compliance-rules.yaml --loglevel info
# Detailed debugging information
sfp project:analyze --compliance-rules config/compliance-rules.yaml --loglevel debugDebug logging shows:
- Rules being processed
- Files being scanned
- Field extraction details
- Rule evaluation results
Troubleshooting
Rule Not Triggering
- Verify Field Path: Ensure the field path matches the XML structure
- Check Metadata Types: Confirm the rule applies to the correct metadata types
- Validate Operators: Ensure the operator logic matches your expectations
- Enable Debug Logging: Use
--loglevel debugto see detailed evaluation
False Positives
- Refine Rule Conditions: Adjust operators or values to be more specific
- Scope Rules Appropriately: Use metadata type filters to target specific components
- Create Exceptions: Disable rules for specific packages or paths when needed
Performance Issues
- Scope Analysis: Use package, domain, or path filters to reduce scope
- Optimize Rules: Complex regex patterns can slow down content analysis
- Exclude Large Files: Consider excluding generated or vendor files
Configuration Examples
Organization Standards
extends: default
rules:
# Security Rules
- id: no-hardcoded-ids
enabled: true
- id: no-hardcoded-urls
enabled: true
# API Version Standards
- id: minimum-api-version
name: Enforce API Version 59+
enabled: true
metadata: [ApexClass, ApexTrigger]
field: ApexClass.apiVersion
operator: greater_or_equal
value: 59.0
severity: warning
# Profile Security
- id: profile-no-modify-all
enabled: true
severity: errorDocumentation Standards
extends: default
rules:
# Field Documentation
- id: field-missing-description
enabled: true
severity: warning
- id: field-missing-help-text
enabled: true
severity: warning
# Object Documentation
- id: object-missing-description
enabled: true
severity: warning
# Validation Rules
- id: validation-rule-missing-description
enabled: true
severity: info
message: Test classes should follow naming convention with 'Test' suffix