vulkro scan --include-pmd
Run PMD-for-Apex alongside Vulkro's native Salesforce detectors
and merge the findings into the same scan report. Together with the
native Apex pass, vulkro scan --include-pmd stands in for the PMD
step of sfdx-scanner so a Salesforce project only needs one binary
installed in CI.
This page covers what the flag does, how to install the PMD binary, the curated security-only ruleset Vulkro ships, and a CI snippet.
What it does
When --include-pmd is set, after the native scan finishes, Vulkro:
-
Discovers the
pmdbinary on this machine. The lookup order is theVULKRO_PMD_BINenv var first, then a PATH search forpmd(orpmd.baton Windows). -
Writes Vulkro's bundled security-only Apex ruleset to a temp file and invokes:
pmd check -d <project-root> --rulesets <bundled-ruleset.xml> --format json --no-cache -
Parses the PMD JSON output and maps each violation to a Vulkro
SecurityFinding:- PMD priority 1 -> Critical, 2 -> High, 3 -> Medium, 4 -> Low, 5 -> Info.
- The message starts with
PMD-APEX-<rule>:(so SIEM / baseline-diff consumers can group PMD findings). - Confidence is
Mediumby contract (PMD is a separate engine with its own pattern-matching heuristics; Vulkro does not have in-process taint into PMD's verdicts to call them High).
-
Folds the PMD findings into the same
findingsarray as the native detectors. The dedup pass collapses any(file, line, message)overlap between PMD and a native detector.
If PMD is not available, the flag emits an actionable error message that names the env var, the install link, and continues the scan with the native findings only. The wrapper's failure to find PMD never aborts the rest of the scan.
Install PMD
Vulkro does not ship PMD. The latest PMD release works for Apex; v7 is recommended.
# macOS
brew install pmd
# Linux (manual install)
curl -L -o pmd.zip https://github.com/pmd/pmd/releases/latest/download/pmd-dist-7.x.x-bin.zip
unzip pmd.zip
sudo ln -s "$PWD/pmd-bin-7.x.x/bin/pmd" /usr/local/bin/pmd
# Windows (Chocolatey)
choco install pmd
Confirm:
pmd --version
If pmd is not on PATH (common on Windows or self-managed CI
images), point Vulkro at the binary directly:
export VULKRO_PMD_BIN=/opt/pmd/bin/pmd
vulkro scan --include-pmd .
The curated ruleset
The bundled ruleset lives at
src/security/wrappers/rulesets/pmd-apex-security.xml. It is loaded
at runtime via include_str! so the wrapper never depends on a
runtime install layout. The ruleset is security-only by design.
Included
-
The complete PMD Apex security category (
category/apex/security.xml). Every rule. This is the canonical PMD Apex security category and matches whatsfdx-scannerruns.ApexBadCryptoApexCRUDViolationApexCSRFApexDangerousMethodsApexInsecureEndpointApexOpenRedirectApexSharingViolationsApexSOQLInjectionApexSuggestUsingNamedCredApexXSSFromEscapeFalseApexXSSFromURLParam
-
A security-relevant subset of the Apex error-prone category (
category/apex/errorprone.xml):AvoidDirectAccessTriggerMapAvoidHardcodingIdEmptyCatchBlockEmptyIfStmtEmptyStatementBlockEmptyTryOrFinallyBlockEmptyWhileStmt
Excluded by design
| Category | Why it is out |
|---|---|
category/apex/codestyle.xml | Formatting opinions. Not a security review concern. |
category/apex/design.xml | Cyclomatic complexity, class fan-out, etc. Maintainability, not security. |
category/apex/documentation.xml | Javadoc presence checks. |
category/apex/performance.xml | SOQL-in-loop and friends. The native Vulkro detectors flag the actually-dangerous shapes; the rest is a perf concern. |
category/apex/bestpractices.xml | Mixed bag. The rules here that ARE security-relevant are duplicates of category/apex/security.xml entries. |
The exclusion list is locked in the ruleset header comment. Changing it requires a deliberate code edit, so the wrapper's "security only" promise stays enforceable.
Severity and confidence mapping
| PMD priority | Vulkro severity |
|---|---|
| 1 | Critical |
| 2 | High |
| 3 | Medium |
| 4 | Low |
| 5 | Info |
Every PMD finding lands at Confidence::Medium. The wrapper attaches
one evidence row tagged pmd-apex-wrapper so triagers can tell
PMD-sourced findings apart from native-detector findings at a
glance (the row shows the rule name, the PMD ruleset short label,
and the priority that drove the severity).
CI example
A GitHub Actions step that installs PMD, runs Vulkro with the wrapper, and fails the build on Critical or High findings:
name: vulkro
on:
pull_request:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install PMD
run: |
curl -L -o pmd.zip https://github.com/pmd/pmd/releases/latest/download/pmd-dist-7.x.x-bin.zip
unzip -q pmd.zip
echo "VULKRO_PMD_BIN=$PWD/pmd-bin-7.x.x/bin/pmd" >> "$GITHUB_ENV"
- name: Install Vulkro
run: curl -fsSL https://vulkro.com/install.sh | sh
- name: Scan
run: vulkro scan --include-pmd --fail-on critical,high .
The wrapper inherits the rest of the vulkro scan flag set,
including --format json, --fail-on <SEVERITIES>, --ratchet,
--baseline <PATH>, and --gate-vs <REF>. A PMD-emitted finding
is treated the same way as any other Vulkro finding in those flags.
Exit codes
The exit code follows the standard vulkro scan contract (see
scan for the full table):
| Code | Meaning |
|---|---|
0 | Scan completed, no findings at or above the --fail-on threshold. |
1 | Scan completed, at least one finding at or above the threshold. |
2 | Operational error (bad args, IO failure, internal crash). PMD missing or PMD's own non-zero exit does NOT cause exit 2; the scan continues with the native findings and prints an actionable message on stderr. |
Env vars
| Variable | Default | Effect |
|---|---|---|
VULKRO_PMD_BIN | unset | Absolute path to a pmd binary. When set, the wrapper uses this binary unconditionally instead of searching PATH. The path must point at an existing file. |
The standard VULKRO_* env vars honored by vulkro scan apply to
this subcommand too.
See also
vulkro scanfor the fullscanreference.vulkro sf-appexchange-reportfor the AppExchange readiness report; every PMD wrapper finding lands under that report'sCode Quality and Best Practicessection.- The native Vulkro Apex detectors continue to run unchanged
whether or not
--include-pmdis set; see the Salesforce coverage page for the rule catalog.