all insights

What the AppExchange Security Review actually checks — and how to pass the first time

The Security Review is the gate every AppExchange app must clear. Here is what reviewers look for and how to build so you pass on the first submission.

If you are building a product on Salesforce, the AppExchange Security Review is the gate between “we built an app” and “customers can install it.” It has a reputation for being painful — long, opaque, and prone to rejections that send teams back for weeks of rework.

It does not have to be. Almost everything the review checks is knowable up front. The teams that fail are usually the ones who treated security as a final step instead of a design constraint. Here is what reviewers actually look for.

It is not one test — it is a posture

The review combines automated scanning and manual testing against the Salesforce security requirements. Broadly, reviewers are checking that your package cannot be used to compromise the customer org it is installed into. That breaks down into a handful of recurring themes.

1. CRUD and FLS enforcement

This is the most common reason packages fail. Apex runs in system mode by default, which means it can happily read and write objects and fields the running user should never touch. Your code has to enforce object permissions (CRUD) and field-level security (FLS) explicitly.

// Don't trust system mode. Enforce what the user is actually allowed to do.
List<Account> accts = [
    SELECT Id, Name, AnnualRevenue
    FROM Account
    WITH USER_MODE          // enforces CRUD + FLS + sharing for you
    LIMIT 50
];

WITH USER_MODE and the Security.stripInaccessible() API exist precisely so you can do this cleanly. Use them everywhere user-driven data flows through Apex.

2. Injection and unsafe input

Reviewers will probe for SOQL/SOSL injection, DML injection, and cross-site scripting. The rules are familiar from web security:

  • Never concatenate user input into a dynamic SOQL string. Use bind variables.
  • Escape output in Lightning components. Avoid lwc:dom="manual" and innerHTML with untrusted data.
  • Validate and type-check anything that crosses a boundary — URL parameters, Apex REST inputs, integration payloads.

3. Sharing and with sharing

Every Apex class that touches data should declare its sharing model deliberately. with sharing, without sharing, and inherited sharing each say something specific. Leaving it unspecified is both a security risk and a red flag to a reviewer.

4. Secrets, endpoints, and stored data

Hard-coded credentials, API keys in code, and unprotected named-credential usage all fail. Secrets belong in protected custom settings, protected custom metadata, or named credentials — never in source. Reviewers also check that any external endpoint you call is declared and uses TLS.

The mental model that passes: assume a hostile admin installs your package and tries to use it to escalate their own access. Every check is a version of “can they?”

How to pass on the first submission

The difference between a one-shot pass and three rounds of rework is almost entirely about when you do the work.

  1. Run the checkers continuously, not at the end. The Salesforce Code Analyzer (which wraps PMD and others) and the Checkmarx scan available through the Partner Portal catch the bulk of issues. Wire them into CI so a violation breaks the build the day it is written.
  2. Write a security test, not just a functional test. Add tests that assert a low-privilege user cannot see or change what they shouldn’t. These double as regression protection.
  3. Document your design. The review asks for a questionnaire and architecture notes. A clear explanation of your sharing model, data flows, and external connections speeds up the manual review considerably.
  4. Test as a minimal user. Install your package in a fresh scratch org, log in as a user with the least privilege your app supports, and try to break it. This is exactly what the reviewer does.

The payoff

Building to the Security Review standard is more work than building a quick internal customization — but that is the point. The discipline it forces (least privilege, explicit sharing, no injection surface, tested permissions) is simply what good Salesforce engineering looks like. It is also why we bring the same standard to client orgs that will never go through the review: code built to clear that bar is code you can trust in production.


Building something for the AppExchange and want a second set of eyes before you submit? Let’s talk — we’ve been through it.