- Demystify the end-to-end AppExchange Security Review workflow, timeline, and core testing philosophies.
- Learn how to run and configure Salesforce Code Analyzer (SFCA) and PMD to identify potential compliance violations.
- Detail the exact manual penetration testing techniques employed by the Salesforce review team on external endpoints.
- Review and mitigate injection vulnerabilities including SOQL Injection, Stored/Reflected XSS, and Cross-Site Request Forgery (CSRF).
- Enforce strict CRUD and FLS (Field-Level Security) checks using Apex modern practices, including WITH USER_MODE and the Security.stripInaccessible API.
- Secure external integrations using Named Credentials, Content Security Policies (CSP), and Cross-Origin Resource Sharing (CORS).
1. The AppExchange Gatekeeper: Understanding the Security Review Architecture
The AppExchange is the cornerstone of the enterprise cloud software ecosystem, but its immense success relies on a single, non-negotiable principle: absolute trust. To protect client organisations from malicious actors, data leakages, and unstable code, Salesforce enforces the AppExchange Security Review. This process is not a cursory checklist exercise or a simple automated pass; it is an exhaustive architectural and security assessment. It applies to managed packages, external connected applications, browser extensions, and mobile clients that interact with the Salesforce API.
For ISV partners, understanding the architecture of the review is vital. The process evaluates the entire threat model of your offering. If your managed package communicates with an external Heroku application, the Heroku application, its APIs, its database, and its hosting infrastructure are all in scope. Salesforce treats your solution as a unified system, meaning a vulnerability in your external Node.js backend will fail your entire Salesforce listing. The review aims to verify three core pillars: data isolation, platform integrity, and secure transport. By standardising on robust security practices from day one, organisations can avoid the common cycle of failure, redesign, and resubmission.
The lifecycle of the security review typically involves several key stages. First is the preparatory stage where developers run local scanners and document the architecture. Next is the submission phase where the managed package and all required documentation (including automated scan reports and architecture diagrams) are uploaded. This is followed by the automated scanning queue and finally the manual penetration testing phase. If issues are discovered, the review team issues a detailed report listing the failures, which the partner must remediate before resubmitting. Understanding this operational sequence allows product teams to align their development schedules with realistic certification timelines.
2. Automated Static Analysis: Mastering Salesforce Code Analyzer and PMD
Before your package is submitted, you must run automated static application security testing (SAST). The standard tool for this is the Salesforce Code Analyzer (SFCA), which bundles PMD, ESLint, Retire.js, and Salesforce's proprietary Graph Engine. These tools inspect code without executing it, searching for patterns that indicate security vulnerabilities or poor coding practices.
PMD scans Apex source code for common violations, such as missing CRUD/FLS checks, hardcoded sharing keywords, or SOQL inside loops. The Graph Engine takes static analysis further by performing path-sensitive dataflow analysis. It tracks user-controlled inputs (sources) to dangerous execution points (sinks) across multiple classes and methods. This helps to detect complex vulnerabilities like nested sharing bypasses and indirect injections that standard AST scanners miss.
To pass automated scanning, developers must address all high-severity violations. When false positives occur, they must be documented in a detailed security web-scan explanation spreadsheet, justifying why a specific pattern is secure (e.g., using a custom sanitisation method). Standardising on this sanitisation and documenting it clearly is the only path to gaining approval for unconventional architectural designs. Let us look at a typical PMD scan violation: using dynamic SOQL queries with string concatenation. The scanner flags this as a potential SOQL injection. The remedy is to standardise on static queries or leverage dynamic bind variables.
3. Manual Penetration Testing: What the Security Team Probes
Automated scans are only the first gate. Once they pass, your application is handed to the Salesforce Security Review team for manual penetration testing. This team acts as ethical hackers, probing your implementation for flaws that static scanners cannot detect. They look for logical vulnerabilities, privilege escalation bypasses, and access control issues.
Common manual attack vectors include privilege escalation, broken object-level authorisation, and session handling flaws. The review team will install your package in a test environment with multiple user profiles: a System Administrator, a Standard User, and a low-privilege Community User. They will attempt to bypass your UI controls by making direct API calls, manipulating network payloads, and modifying cookie parameters. They want to ensure that a user with restricted access cannot view, modify, or delete records they do not own or have not been explicitly granted access to.
If a low-privilege user can access clinical data by altering an ID parameter in a URL (Insecure Direct Object Reference, or IDOR), the review fails immediately. The testers also evaluate the security of your connected apps, looking for weak OAuth flows, token storage vulnerabilities, and improper scope requests. If your app integrates with an external service, they will probe the external API endpoints using tools like OWASP ZAP and Burp Suite to test for SQL injection, CSRF, and session hijacking vulnerabilities. Understanding that the manual testers leave no stone unturned is essential for building a bulletproof application.
4. Defending Against Injection and Client-Side Vulnerabilities
Injection attacks remain a primary reason for security review failures. In Apex, SOQL injection occurs when user input is concatenated directly into a dynamic SOQL query. To prevent this, dynamic queries must use bind variables or the input must be explicitly typed or sanitised using String.escapeSingleQuotes().
In client-side components (Lightning Web Components or Visualforce), Cross-Site Scripting (XSS) is a major concern. Salesforce's Lightning Locker and Lightning Web Security (LWS) provide sandboxing, but developers can still introduce vulnerabilities by using lwc:dom="manual" or rendering raw HTML with v-html equivalents. Let us examine how to secure an Apex controller against SOQL injection:
// VULNERABLE APEX CODE
public with sharing class VulnerableController {
public List searchAccounts(String userInput) {
// Vulnerable to SOQL Injection via user input manipulation
String query = 'SELECT Id, Name FROM Account WHERE Name LIKE \'%' + userInput + '%\'';
return Database.query(query);
}
}
// SECURED APEX CODE
public class SecuredController {
public List searchAccounts(String userInput) {
// Secured using dynamic bind variables and explicit USER_MODE enforcement
String query = 'SELECT Id, Name FROM Account WHERE Name LIKE :userInput';
return Database.query(query, AccessLevel.USER_MODE);
}
}
In Visualforce, output components automatically encode variables, but functions like <apex:outputText escape="false"> bypass this protection. Unless absolutely necessary, always let the platform escape output, or use programmatic sanitisation methods. When writing Lightning Web Components, adhere to the strict separation of concerns and avoid bypassing standard security boundaries. By customising and validating every user input before it reaches a DML or dynamic query sink, developers ensure their package meets the highest standard of security.
Cross-Site Request Forgery (CSRF) is another critical attack vector evaluated during the review. CSRF occurs when a malicious website causes a user's web browser to perform an unwanted action on a trusted site where the user is currently authenticated. In Salesforce, built-in anti-CSRF tokens protect standard actions, but developers must ensure that custom integration endpoints and external pages also enforce state-verification tokens or utilise HTTP POST methods with strict body validation. Never perform DML actions or state changes in page-load initialization methods (such as Visualforce action methods) as these are highly vulnerable to CSRF manipulation.
5. Enforcing CRUD and FLS: Programmatic and Declarative Shielding
Unlike standard enterprise customisation where developers occasionally bypass security for system processes, AppExchange packages must strictly enforce Object-Level Security (CRUD) and Field-Level Security (FLS). If your package displays or modifies fields without verifying that the current user has permission to do so, it will fail the security review. This requirement is absolute and applies to all components of your package, including controllers, triggers, and APIs.
Historically, developers had to write verbose checks using Schema.DescribeSObjectResult and Schema.DescribeFieldResult. While this is still acceptable, modern Apex provides elegant ways to enforce security:
1. WITH USER_MODE: Appending WITH USER_MODE to SOQL queries or running DML operations in USER_MODE forces the platform to enforce CRUD, FLS, and sharing rules.
2. Security.stripInaccessible(): This method filters out fields and objects that the user does not have permission to access, raising no exceptions but sanitising the data before it is rendered or updated.
Let us see an example of modern CRUD/FLS enforcement using these modern paradigms:
public class SecureDataService {
// Enforcing security directly in the SOQL query using USER_MODE
public List getOpportunitiesInUserMode() {
return [SELECT Id, Name, Amount, StageName
FROM Opportunity
WITH USER_MODE];
}
// Filtering inaccessible fields during DML operations
public void updateOpportunityAmount(List records) {
SObjectAccessDecision decision = Security.stripInaccessible(
AccessType.UPDATABLE,
records
);
// Update the sanitised records
update decision.getRecords();
}
}
By adopting USER_MODE and Security.stripInaccessible, you dramatically reduce the surface area for security review findings and ensure your application respects the customer's security settings. Remember, the review tests not only your code but also your behaviour in handling data access. Ensure that your testing processes validate these access controls across different user profiles to identify and resolve privilege escalation risks before submission. Optimising your security design in this manner guarantees a faster, smoother path through the AppExchange certification gates.
Key Takeaways
- The AppExchange Security Review evaluates the entire threat model of your offering, including external endpoints, APIs, and connected applications.
- Running the Salesforce Code Analyzer (SFCA) and resolving all high-severity violations is a mandatory pre-requisite for submission.
- Manual penetration testing by the Salesforce security team probes for logical flaws like IDOR, privilege escalation, and weak session handling.
- Injection vulnerabilities (SOQL Injection and XSS) must be eradicated by utilising dynamic bind variables, escaping single quotes, and avoiding escaping HTML output.
- AppExchange packages must strictly enforce CRUD and FLS using modern Apex features like USER_MODE and Security.stripInaccessible().
- External integrations must be secured through Named Credentials, strict Content Security Policies (CSP), and correct Cross-Origin Resource Sharing (CORS) configurations.
Checkpoint: Test Your Understanding
Which automated analysis tool executes path-sensitive dataflow analysis to track user-controlled inputs to dangerous execution points?
What is the recommended modern approach to automatically enforce CRUD and FLS in an Apex SOQL query?
During manual penetration testing, how does the security review team evaluate data isolation boundaries?
Discussion & Feedback