A Salesforce CLI (sf) plugin that runs a complete, read-only security audit against any Salesforce org, risk-scores it with an A–F grade, and turns the result into a report your security team (or your client's) can act on.
- 92 read-only checks across identity, access, data, code, integrations, monitoring, and Agentforce / GenAI
- Attack-chain correlation: links individual findings into named, multi-step attack scenarios — eleven modelled chains, plus an emergent pass for combinations nobody has named yet (see Attack chains)
- Compliance mapping: every finding mapped to source-verified controls across 10 frameworks (OWASP, OWASP LLM Top 10, SOC 2, ISO/IEC 27001:2022, Security Benchmark for Salesforce, NZ Privacy Act, HISO 10029, NZISM, HIPAA Security Rule, GDPR)
- Outputs: a technical
html/md/jsonreport, or a branded, client-ready executive report (print-to-PDF) with priorities, remediation roadmap, and a compliance coverage matrix - History & diff: archives each run and shows security-posture drift over time
- Free event baseline:
sf audit events pullcaptures the org's free dailyEventLogFilelogs to local disk before the 1-day retention window drops them — no Event Monitoring / Shield add-on needed - Connected-app least-privilege:
sf audit appsreads theRestApiEventLogFile to see which objects each connected app actually uses, compares that against what its run-as user is granted, and reports the over-grant per object and read/write bit — plus a suggested least-privilege permission set - Strictly read-only (SOQL/Tooling/REST GETs + Metadata API reads); see PERMISSIONS.md for the least-privilege access it needs
sf plugins install @cclabsnz/sf-auditYou will be warned that the plugin is not digitally signed. That is expected and says nothing about
this plugin: Salesforce only accepts signing keys served from developer.salesforce.com, so no
third-party plugin can satisfy it. Answer y. For unattended installs, allowlisting and local
development setup are in docs/INSTALL.md.
Since "trust us" is a poor answer from a tool that authenticates against your production org, the guarantees here are checkable rather than asserted — see Trust & verification.
sf audit security --target-org <orgAlias>Runs all 92 checks and writes an HTML report to the current directory. sf audit list prints every
check id.
| Flag | Default | |
|---|---|---|
--format / -f |
html |
html, md, json, executive — comma-separated |
--fail-on |
(none) | Exit 1 if any finding is at or above CRITICAL/HIGH/MEDIUM/LOW. For CI |
--fail-on-inconclusive |
false |
Exit 3 if any check could not gather evidence. For CI |
--digest |
false |
Compact result for programmatic callers — no passing checks, no prose, capped lists |
--checks |
(all) | Run only these check ids |
--frameworks |
universal |
Compliance matrix scope for the executive report |
# Fail a pipeline on HIGH or worse
sf audit security --target-org myOrg --fail-on HIGH
# Also fail if the audit user could not see enough to judge
sf audit security --target-org myOrg --fail-on HIGH --fail-on-inconclusive
# Branded, client-ready PDF-able report
sf audit security --target-org myOrg --format executive --prepared-for "Acme Health"
# What can this audit user actually establish? Ask before spending a run
sf audit preflight --target-org myOrg
# Machine-readable: result on stdout, progress logging suppressed
sf audit security --target-org myOrg --json
# Compact result for an agent or script
sf audit security --target-org myOrg --json --digest| Code | Meaning |
|---|---|
0 |
Audit completed; nothing the caller asked to fail on |
1 |
Findings at or above --fail-on |
2 |
The audit could not run — authentication, connection, or bad flags |
3 |
Audit ran, but checks could not gather evidence (--fail-on-inconclusive only) |
A definite finding outranks unknown coverage: when both apply you get 1, and the
inconclusive count is still in the report body. 3 exists so a pipeline can tell
"this org is fine" apart from "the audit user could not see enough to judge".
All twelve flags, more examples, and what the executive report contains: docs/COMMANDS.md.
92 read-only checks across ten domains. Every finding is risk-rated CRITICAL → INFO, mapped to compliance controls, and correlated into attack chains.
| Domain | Checks |
|---|---|
| Identity & Authentication | 13 |
| Users, Permissions & Privilege | 13 |
| Data Access & Sharing | 11 |
| Guest & External-Facing Access | 12 |
| Integrations, Connected Apps & Deployments | 11 |
| Monitoring & Threat Detection | 10 |
| Apex & Code Security | 9 |
| AI & Agents (Agentforce / GenAI) | 6 |
| Org Health & Configuration | 5 |
| Secrets & Credential Storage | 2 |
Full inventory, with what each check looks for: docs/CHECKS.md.
A handful of checks emit an advisory rather than a verdict, because the underlying setting is not exposed to the read APIs this tool uses. Those are listed and explained in the same document — advisories are INFO and never inflate the health score.
Findings map to controls across ten frameworks: OWASP Top 10, OWASP LLM Top 10, SOC 2, ISO/IEC 27001:2022, the Security Benchmark for Salesforce, the NZ Privacy Act, HISO 10029, NZISM, the HIPAA Security Rule, and GDPR.
The mapping is built on a sourced catalog — each control carries its framework, pinned version, official title and a citation — so a finding ties to an exact, defensible requirement rather than a bare tag. A provenance gate means a control renders only once its reference has been confirmed against the authoritative source.
Scope the executive report's matrix with --frameworks universal|nz|all, or name them:
--frameworks owasp,iso,hipaa. HIPAA and GDPR are in no named pack, because jurisdiction is your
call, not a default.
Not an attestation. A control showing "No findings detected" means this audit surfaced no issues mapped to it. It is not a statement of compliance. See Scope & Liability.
Framework versions, the packs, and the verification status of every control: docs/COMPLIANCE.md.
A list of findings is not a risk assessment. Three MEDIUM findings that combine into an unauthenticated path to bulk data matter more than a lone HIGH that leads nowhere, and reading a report severity-by-severity hides exactly that.
So every audit correlates its findings into attack chains. Sixteen named chains are hand-modelled scenarios — each with its own narrative and remediation, several naming the concrete request path an attacker would use. Where no named chain explains a combination, an emergent pass reports the remaining entry-point → outcome pairs as lower-confidence "potential attack paths", so a novel combination is still surfaced.
Every chain lists the findings forming its steps, and remediating any one step breaks the chain. A chain is reported only when every ingredient is present: a clean org produces none.
All eleven with severities and trigger conditions: docs/ATTACK-CHAINS.md.
What this tool is. A read-only, point-in-time configuration review. Every check uses standard Salesforce SOQL, Tooling, and REST GET queries only: the tool performs no DML, no metadata deployments, and never modifies the target org or its data. It runs under the permissions of the authenticated sf user; checks that the user cannot access are reported as inconclusive rather than passing silently.
What this tool is not. It is not a penetration test, a dynamic/runtime security test, or a source-code audit of managed-package internals. It does not exploit vulnerabilities, attempt privilege escalation, or guarantee detection of every misconfiguration. The Health Score and A–F grade are prioritisation aids, not certifications, and do not represent compliance with, or accreditation under, any standard (OWASP, SOC 2, ISO 27001, HIPAA, GDPR, or otherwise). Compliance-framework tags indicate relevance to a control area only.
Point-in-time. Results reflect org configuration at the moment the audit ran. Configuration drift, new customisations, and platform changes can invalidate findings at any time. Re-run regularly (see History & Diff).
Authorisation. Run this tool only against orgs you own or are explicitly authorised in writing to assess. You are responsible for obtaining the necessary permissions and for handling generated reports (which may contain sensitive security configuration) in accordance with your organisation's data-handling and confidentiality obligations.
No warranty. This software is provided "as is", without warranty of any kind, express or implied. To the maximum extent permitted by law, the authors and CloudCounsel Limited accept no liability for any loss, damage, or claim arising from use of this tool or reliance on its output. Findings are informational and should be validated by a qualified Salesforce security practitioner before any remediation action is taken.
Because this tool authenticates against production orgs, "is it safe to run?" deserves a verifiable answer rather than a claim. Every guarantee below is something you can check yourself:
- Read-only, enforced in CI — a test statically fails the build if any write path appears in the source
- No network egress — enforced the same way; reports are fully self-contained (fonts and Chart.js inlined)
- Signed provenance — released from CI via npm trusted publishing (OIDC);
npm audit signaturesverifies the tarball against the public commit - Independent scans — CodeQL (
security-extended, source and workflows), Semgrep, OpenSSF Scorecard, a dependency-review licence gate,pnpm audit, Dependabot, secret scanning with push protection, SHA-pinned Actions, and a CycloneDX SBOM per release
Run the guards yourself:
npm test test/unit/invariants
npm audit signaturesFull detail, including why Socket raises two behavioural alerts against this package and how to confirm each: docs/TRUST.md. Least-privilege access is in PERMISSIONS.md; vulnerability reporting in SECURITY.md.
Each finding carries a risk weight (CRITICAL 10, HIGH 7, MEDIUM 4, LOW 1, INFO 0). The health score is
100 - (total weight / max possible weight) * 100, capped at 0, and maps to an A–F grade — A needs
≥ 85 with no HIGH findings; any CRITICAL is an F.
All weights and grade thresholds are configurable without recompiling, via
--scoring-config ./my-scoring.json. Start from config/scoring.sample.json,
which lists every valid checkWeights key; your config is deep-merged with the defaults, so include
only what you want to change.
Grade bands, the full config shape and worked examples: docs/SCORING.md.
Beyond the audit itself, the plugin ships five commands. Each is documented in docs/COMMANDS.md.
| Command | What it does |
|---|---|
sf audit history / sf audit diff |
Every run auto-archives to ~/.sf/audit-history/{orgId}. Show posture drift across runs as a table plus an HTML timeline, or diff any two report JSONs |
sf audit events pull |
Capture the org's free daily EventLogFile logs to local disk before the ~1-day retention window drops them — no Event Monitoring / Shield add-on needed. Idempotent, safe to cron |
sf audit timeline |
Reconstruct one actor's activity across every captured event type, entirely offline. Refuses to expand a shared identity or join on a blank field, and always reports capture coverage first |
sf audit preflight |
Read the running user's effective permissions in one query and report which checks will produce a verdict and which will return inconclusive — before an audit is run |
sf audit apps |
Read the RestApi event log to compare what each connected app actually uses against what its run-as user is granted, and emit a suggested least-privilege permission set |
To triage captured logs for abuse patterns, pair events pull with the companion CLI
sfelf-triage, which reads this plugin's
~/.sf/event-baseline/<orgId> layout directly.
- Node.js 18+
- Salesforce CLI (
sf) v2+ - A least-privilege, read-only org user. The audit performs no writes and does not require
View All Data— without it one probe (integration write evidence) reports inconclusive rather than drawing a conclusion. See PERMISSIONS.md for the exact minimum permission set, what each is for, what the tool does not need, and a ready-to-deploySF Audit (Read-Only)permission set (docs/permissionset/).
npm run build # compile TypeScript
npm run typecheck # type-check src + test (tsc --noEmit)
npm test # typecheck, then run all tests
npm run test:unit # typecheck, then unit tests only
npm run test:jest # tests without the typecheck — fast inner loop
npm run clean # remove compiled outputJest transforms with swc, which strips types without checking them, so
npm run typecheck is what catches type errors in tests — it runs ahead of Jest in
npm test. Use npm run test:jest while iterating, but don't treat it as a green run.
Release history is in CHANGELOG.md; each entry mirrors its GitHub Release, which carries the signed provenance attestation and the CycloneDX SBOM for that build.
Project documents: GOVERNANCE.md (roles, decisions, continuity), ARCHITECTURE.md, ROADMAP.md, and the assurance case — the argument, with evidence, that this tool is safe to point at a production org.
Maintainers: see docs/RELEASE.md for the release checklist and the one-time repository-hardening steps (npm provenance token, branch protection, and the CodeQL / Scorecard setup behind the badges above).
Deep dives on this tool and the topics it checks, from our engineering blog softwareinsights.dev — including how sf-audit works, the free Event Monitoring baseline, guest-user exposure grading, the delivery-team access model, Agentforce hardening after ForcedLeak, and the NZ compliance context.
Full annotated list: docs/FURTHER-READING.md.
sf-audit is free and open source. If you'd like hands-on help — interpreting findings, prioritising remediation, or a full Salesforce security and architecture review — CloudCounsel, the team behind this plugin, offers Salesforce security consulting. Reach us at hello@cloudcounsel.co.nz.
