AppExchange Security Review readiness checklist
Catch the CRUD/FLS gaps that fail AppExchange Security Review submissions, offline, before you spend on a submission.
Salesforce does not publish a first-pass failure rate. The figure partners cite most often is about half, and it is an industry estimate rather than an official number. A paid listing pays a fee per submission attempt, and a failed round costs two to three weeks of reviewer turnaround. What is not in dispute is the top cause: object and field permission enforcement (CRUD/FLS).
The cheapest fix is to walk the checklist on your own laptop first.
vulkro-sf runs the same classes of check the reviewer applies,
entirely offline: your package source never leaves the machine.
Why CRUD/FLS is the top failure cause
Below API version 67, Apex runs in system context by default. Unless you explicitly enforce the running user's object and field permissions before a DML or SOQL operation, your package will happily read and write data the user should never be able to touch.
At API version 67 (Summer '26) Salesforce changed the default. SOQL,
SOSL, DML and Database methods run in user mode, and a class with
no sharing declaration defaults to with sharing. The same release
removed WITH SECURITY_ENFORCED, which no longer compiles. Most
packages in review still carry classes on an older API version, so
both worlds are usually in scope for the same submission.
Reviewers test enforcement aggressively because it is the most common real-world data exposure in managed packages.
"Enforce" means one of these, on every DML and SOQL path, ranked by what to reach for first:
WITH USER_MODEon the query andAccessLevel.USER_MODEon the DML. This is the current answer and the one to write in new code.Security.stripInaccessible(...)on the records, where the query has to stay broad and you filter after the fact.- Explicit
Schema.sObjectType.X.isAccessible() / isCreateable() / isUpdateable() / isDeletable()checks, for the cases the platform modes do not cover. - A vetted access-check helper class, applied on every path rather than on some of them.
WITH SECURITY_ENFORCED is the legacy clause. It is removed at API
version 67 and will not compile there, so treat it as something to
migrate off rather than something to add.
Partial enforcement (checking isAccessible but not
isUpdateable, or enforcing on one method but not the helper it
delegates to) is the trap that fails experienced teams. A
pattern-matching checker cannot see the enforcement that happens one
method call away. vulkro-sf builds the intra-class and cross-class
call graph and follows the delegation, so a real gap surfaces and a
false alarm does not.
The readiness checklist
Walk these before you submit. vulkro-sf appexchange-report
renders the same list as an HTML report grouped section by section,
pinned to the checklist version on the day you run it.
1. Object and field permissions (CRUD/FLS)
- Every SOQL query enforces FLS (
WITH USER_MODE,stripInaccessible, or explicit checks). - Every DML operation enforces CRUD for the running user
(
AccessLevel.USER_MODEor explicit checks). - No
WITH SECURITY_ENFORCEDclauses left in any class you are moving to API version 67 or later. - Enforcement holds across method and class boundaries, not just in the entry method.
-
@AuraEnabledand@RestResourcemethods enforce before reaching any DML.
2. Sharing model
- No
without sharingon classes that handle record data without an explicit ownership check. -
inherited sharingused where the caller's context should decide. - Every class states its sharing intent explicitly, so the behaviour does not change when the API version moves to 67.
3. Injection
- No dynamic SOQL built by string-concatenating request input (use bind variables).
- No SOQL injection across method boundaries (tainted input passed into a query-building helper).
- No Apex or JavaScript injection via dynamic
Type.forName,eval, or unescaped merge fields.
4. External integrations and secrets
- No hardcoded passwords or API tokens in Apex literals, named credentials, or custom settings.
- Named credentials have IP restrictions and use HTTPS endpoints.
- OAuth scopes on connected apps are no broader than the
integration needs (no
Fullwhere a narrower scope works).
5. Lightning (LWC + Aura)
- No
lwc:dom="manual"paired withinnerHTMLon untrusted data. - No secrets written to
localStorageorsessionStorage. -
@wirereturns are treated as untrusted in the DOM.
6. Visualforce
- No
escape="false"on a reflected merge field without aJSENCODE/$Resource/$Labelwrap. - No
<apex:includeScript>with a dynamic URL.
7. Flow
- No
runInMode = SystemModeWithoutSharingwhere user context is required. - No hardcoded org IDs in
<stringValue>elements. - No system-context DML that bypasses the running user's permissions.
8. Profiles and permission sets
- No
View All Data,Modify All Data,Customize Application, orAuthor Apexgranted to non-admin profiles the package ships.
9. Connected apps
- OAuth scope is the minimum the integration requires.
10. Insecure deserialization and mass assignment
- No
JSON.deserializeof caller-controlled input straight into an SObject that is later written via DML.
Run it before you submit
The scan and the readiness checklist on screen are Free, with no
account. vulkro-sf appexchange-report, the report you hand to the
Security Review team, requires Pro. The 14-day trial of the full
product covers a submission cycle, and licenses are issued directly by
our team at license@vulkro.com; prices are on the
pricing page.
# Render the reviewer-grouped readiness report, offline:
vulkro-sf appexchange-report force-app -o readiness.html
- Open
readiness.htmland walk all 10 sections. - Fix what is flagged. CRUD/FLS first: it is the most common fail.
- Re-run until the report reads READY.
- Only then submit to Salesforce.
- If Salesforce flags something new, re-run with the reviewer's notes to find the same pattern elsewhere in the package.
The whole loop runs on your laptop. Air-gap it with
VULKRO_OFFLINE=1 to enforce zero network at the process boundary.
Ready to walk the list?
Run vulkro-sf locally before you spend on a submission. The methodology maps every detector to its checklist section, and licenses are issued directly by our team at license@vulkro.com.
See Vulkro for Salesforce
See also: AppExchange readiness docs, Vulkro for Salesforce, methodology.