Target: Code Analyzer v5 (@salesforce/plugin-code-analyzer). Sources: Code Analyzer docs, Engines, Customize the Configuration.
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
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
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
- 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-appeven 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-unsuppresscomments in the source, for a single justified line. - Config: a
suppressionsblock per folder withmax_suppressed_violations, areasonand 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 |