> For the complete documentation index, see [llms.txt](https://docs.flxbl.io/flxbl/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flxbl.io/flxbl/sfp/analysing-a-project/compliance-check.md).

# Compliance Check

{% hint style="info" %}
Available in sfp-pro.
{% endhint %}

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:

1. Loads rules from your configuration file (defaults to `config/compliance-rules.yaml`)
2. Scans metadata components using Salesforce's ComponentSet API
3. Applies rules based on metadata type and field specifications
4. Evaluates content and field values using configurable operators
5. 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:

1. `--compliance-rules <path>` CLI flag (absolute or relative path)
2. `config/compliance-rules.yaml` relative to the project root

```bash
sfp project:analyze --compliance-rules config/compliance-rules.yaml --fail-on compliance
```

#### Extending 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:

```bash
sfp project:analyze --generate-compliance-config
```

This creates `config/compliance-rules.yaml` with sample rules:

```yaml
# 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 higher
```

#### Built-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

```yaml
# 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 identifiable
```

#### Example: Standalone Rules (No Preset)

```yaml
# 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 methods
```

#### Custom Rules

Define custom rules to match your specific organizational requirements:

```yaml
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 higher
```

#### Rule 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:

```yaml
# Top-level element
field: status

# Nested XML path
field: userPermissions.ModifyAllData

# For Profile permissions
field: userPermissions.CustomizeApplication
```

#### Content Analysis

Use the special `_content` field to scan raw file text:

```yaml
field: _content
operator: contains_salesforce_id
value: true
```

```yaml
field: _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 IDs
```

#### Violation 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

{% hint style="info" %}
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.
{% endhint %}

When integrating compliance checking in your CI/CD pipeline:

1. **Enforce Compliance Standards**:

   ```bash
   sfp project:analyze --compliance-rules config/compliance-rules.yaml --fail-on compliance
   ```
2. **Generate Reports**:

   ```bash
   sfp project:analyze --compliance-rules config/compliance-rules.yaml --report-dir ./reports --output-format markdown
   ```
3. **GitHub Actions Integration**:

   ```yaml
   - 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

```bash
sfp project:analyze --compliance-rules config/compliance-rules.yaml -p core,utils
```

#### By Domain

```bash
sfp project:analyze --compliance-rules config/compliance-rules.yaml -d sales
```

#### By Source Path

```bash
sfp project:analyze --compliance-rules config/compliance-rules.yaml -s ./force-app/main/default
```

#### By Changed Files

Analyze only specific changed files:

```bash
# 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:

```yaml
- id: minimum-api-version
  enabled: true
  metadata: [ApexClass, ApexTrigger]
  field: ApexClass.apiVersion
  operator: greater_or_equal
  value: 59.0
  severity: warning
```

#### Security Hardening

Prevent security vulnerabilities:

```yaml
- 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 Settings
```

#### Profile Security

Restrict dangerous permissions:

```yaml
- 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 permission
```

### Logging and Debugging

Use different log levels to control output verbosity:

```bash
# 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 debug
```

Debug logging shows:

* Rules being processed
* Files being scanned
* Field extraction details
* Rule evaluation results

### Troubleshooting

#### Rule Not Triggering

1. **Verify Field Path**: Ensure the field path matches the XML structure
2. **Check Metadata Types**: Confirm the rule applies to the correct metadata types
3. **Validate Operators**: Ensure the operator logic matches your expectations
4. **Enable Debug Logging**: Use `--loglevel debug` to see detailed evaluation

#### False Positives

1. **Refine Rule Conditions**: Adjust operators or values to be more specific
2. **Scope Rules Appropriately**: Use metadata type filters to target specific components
3. **Create Exceptions**: Disable rules for specific packages or paths when needed

#### Performance Issues

1. **Scope Analysis**: Use package, domain, or path filters to reduce scope
2. **Optimize Rules**: Complex regex patterns can slow down content analysis
3. **Exclude Large Files**: Consider excluding generated or vendor files

### Configuration Examples

#### Organization Standards

```yaml
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: error
```

#### Documentation Standards

```yaml
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
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flxbl.io/flxbl/sfp/analysing-a-project/compliance-check.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
