# Code Analyzer v5 — config and gates

Target: Code Analyzer v5 (`@salesforce/plugin-code-analyzer`). Sources: [Code Analyzer docs](https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/get-started.html), [Engines](https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/engines.html), [Customize the Configuration](https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/config-custom.html).

Code Analyzer v4 (`sf scanner run`, the standalone PMD plugin) is retired. v5 runs every engine in one command and one result set.

## 1. Install and run

```bash
sf plugins install code-analyzer

sf code-analyzer run                                    # Recommended rules, current folder
sf code-analyzer run --workspace force-app --severity-threshold 3 --output-file findings.csv
sf code-analyzer run --workspace force-app --rule-selector flow      # flows only
sf code-analyzer run --workspace force-app --rule-selector "pmd:(1,2):Security" --view detail
sf code-analyzer rules --rule-selector all              # what exists, with severities and tags
sf code-analyzer config --output-file code-analyzer.yml # generate a commented starting config
```

Engines and their short names: `pmd` (Apex, Visualforce, HTML, XML, JS — includes the PMD AppExchange rules), `eslint`, `retire-js`, `regex`, `cpd`, `flow` (Flow Scanner), `sfge` (Graph Engine, data-flow analysis for CRUD/FLS), `apexguru` (needs `--target-org`).

Selector syntax: `engine`, `engine:RuleName`, `engine:severity`, `engine:Tag`, combined with `:` for AND and `,` for OR — `--rule-selector "pmd:(2,3):Security"`. Severity 1 is Critical and 5 is Info; `--severity-threshold 3` fails the command when anything moderate or worse is found.

## 2. A `code-analyzer.yml` to start from

```yaml
rules:
  pmd:
    ApexUnitTestClassShouldHaveAsserts: { severity: 2, tags: ["Recommended", "Security"] }
    AvoidSoqlInLoops:                   { severity: 2 }
    ApexCRUDViolation:                  { severity: 2 }
    AvoidHardcodingId:                  { severity: 2 }
    ApexSharingViolations:              { severity: 2 }
    OperationWithLimitsInLoop:          { severity: 2 }
  eslint:
    no-console: { severity: 4 }

engines:
  eslint:
    eslint_config_file: .eslintrc.json
  regex:
    custom_rules:
      NoSeeAllData:
        regex: /SeeAllData\s*=\s*true/gi
        file_extensions: [".cls"]
        description: Test classes must not use SeeAllData=true.
        violation_message: SeeAllData=true found — build data in a TestDataFactory instead.
        severity: Moderate
        tags: ["Recommended"]
      NoWithSecurityEnforced:
        regex: /WITH\s+SECURITY_ENFORCED/gi
        file_extensions: [".cls", ".trigger"]
        description: WITH USER_MODE supersedes WITH SECURITY_ENFORCED.
        violation_message: Replace WITH SECURITY_ENFORCED with WITH USER_MODE.
        severity: Moderate
        tags: ["Recommended"]

suppressions:
  disable_suppressions: false
  "force-app/main/legacy/":
    - rule_selector: "pmd:ApexCRUDViolation"
      max_suppressed_violations: 40
      reason: "Legacy module scheduled for rewrite — ticket PLAT-1187, review 2027-03"

ignores:
  files:
    - "**/staticresources/**"
```

Notes: every regex needs the global modifier (`/…/gi`), or the engine errors. Custom rules only exist when the config file is in scope — from the workspace root it is picked up automatically, otherwise pass `--config-file`.

## 3. Wiring it into the pipeline

```yaml
- run: sf plugins install code-analyzer
- run: sf code-analyzer run --workspace force-app --severity-threshold 3 --output-file code-analyzer.csv
- if: always()
  uses: actions/upload-artifact@v4
  with: { name: code-analyzer, path: code-analyzer.csv }
```

- Run it **before** the org validation: it is seconds against minutes, and it catches most of what a validation would only find after the tests.
- The Graph Engine (`sfge`) needs the whole workspace to build its graph, so keep `--workspace force-app` even when targeting a subset with `--target`.
- Salesforce publishes an official GitHub Action if you would rather not manage the install step.
- The AppExchange security review requires Code Analyzer reports — the same command, same config, no separate tool.

## 4. Suppressions with an expiry habit

Two ways to suppress, both deliberate:

- **Inline**: `code-analyzer-suppress` / `code-analyzer-unsuppress` comments in the source, for a single justified line.
- **Config**: a `suppressions` block per folder with `max_suppressed_violations`, a `reason` and a ticket, as above. The cap is the useful part — it fails the build when the debt grows instead of silently absorbing new violations.

Review the suppressions block every release. A suppression with no ticket and no review date is a rule that has been switched off, and it should be switched off explicitly in `rules:` instead, so everyone can see it.

## 5. Rules worth raising to severity 2 in most orgs

| Rule | Engine | Why |
|---|---|---|
| `ApexCRUDViolation` | pmd | CRUD/FLS not enforced; pairs with the `sfge` analysis |
| `AvoidSoqlInLoops`, `OperationWithLimitsInLoop` | pmd | The governor-limit failure mode |
| `AvoidHardcodingId` | pmd | Breaks between orgs, always |
| `ApexSharingViolations` | pmd | A class with no sharing keyword |
| `ApexUnitTestClassShouldHaveAsserts` | pmd | Coverage without assertions |
| `ApexBadCrypto`, `ApexInsecureEndpoint` | pmd | Security basics the review will catch anyway — cheaper here |
| Flow Scanner DML-in-loop and missing-fault rules | flow | The flow equivalent of the Apex bulk bug |
| RetireJS findings on static resources | retire-js | Vendored libraries with known CVEs |
