Skip to main content
New: Vulkro Cloud for Salesforce, a hosted workspace for your team. Available by invitation: register your interestView docs

AppExchange Top-20 vulnerabilities (Vulkro coverage)

Salesforce Dev Relations publishes the empirical ranking of the Top 20 vulnerabilities found in the AppExchange Security Review, ordered by real-world submission failure rate. This page maps every entry to the Vulkro detector that catches it.

The order below is the canonical Salesforce ordering: failures #1 (most common) through #20 (least common, still material).

1. CRUD / FLS enforcement​

Failure to check object and field-level accessibility before a query or DML. Catches the entire class of "the controller queried a sensitive object the user does not have read access to."

Detector: detectors/apex. The CRUD / FLS enforcement rule (apex_crud_fls) flags missing WITH USER_MODE / WITH SECURITY_ENFORCED, missing isAccessible() / isCreateable() checks, and DML calls that omit AccessLevel.USER_MODE.

2. SOQL injection​

Untrusted input concatenated into a dynamic SOQL string. Catches the Database.query('... WHERE Name = \'' + name + '\'') shape.

Detector: detectors/apex. The SOQL injection rule flags string concatenation into Database.query / Database.queryWithBinds arguments without String.escapeSingleQuotes and without a bind variable.

3. Sharing violations​

Apex class with no sharing declaration (or without sharing) reachable from user input. The class runs in system context and bypasses record-level access.

Detector: detectors/apex. The sharing violation rule flags without sharing on a user-reachable class, and the absence of any sharing keyword on a class that performs DML or SOQL against an sObject containing user-supplied input.

4. Mass assignment via JSON.deserialize​

JSON.deserialize(payload, Account.class) lets a caller set fields they should not be able to write (OwnerId, RecordTypeId). Equivalent of Rails mass assignment.

Detector: detectors/apex. The mass-assignment rule flags JSON.deserialize (and JSON.deserializeStrict) into an sObject type without a subsequent Security.stripInaccessible filter.

5. IDOR via record-ID parameter​

Controller accepts an Id parameter and queries the record without re-verifying the user can see that ID. Classic insecure-direct-object -reference shape.

Detector: detectors/apex. The IDOR rule flags constructor / @AuraEnabled method arguments of type Id that flow into a SOQL WHERE Id = :param without an accompanying access check.

6. Flow system-context DML​

A Flow declared in System Context performs DML on a user-reachable trigger. Bypasses sharing rules and CRUD/FLS.

Detector: detectors/flow. The flow rule parses the .flow-meta.xml file, identifies RunInMode = SystemModeWithSharing / SystemModeWithoutSharing declarations, and flags writes to sensitive standard objects when reachable from a public guest entry point.

7. Visualforce controller CSRF (DML in constructor on GET)​

A Visualforce controller performs DML in its constructor, which runs on HTTP GET. An attacker constructs a URL that triggers the DML; the victim's browser sends the request with the victim's session cookie.

Detector: visualforce. The CSRF rule flags DML statements (insert, update, delete, upsert, Database.insert / Database.update / etc.) inside a constructor method of a class bound to a *.page file.

8. Crypto misuse​

MD5 or SHA-1 for authentication; static IV; Math.random() for tokens; hardcoded crypto keys.

Detector: detectors/apex. The crypto rule flags Crypto.generateDigest('MD5', ...), 'SHA-1', AES with a static IV (literal bytes in source), Math.random() used as a token source, and hardcoded Blob.valueOf keys passed to Crypto.encrypt.

9. External script include over HTTP​

<apex:includeScript value="http://cdn.example.com/foo.js" />. The script bypasses CSP and runs in the page's origin.

Detector: visualforce. The external-include rule flags <apex:includeScript> whose value is a literal URL (not {!$Resource.Foo}) and whose protocol is http:// or https:// (the Top-20 #9 wants StaticResource references, not external CDNs).

10. LWC DOM XSS​

lwc:dom="manual" plus direct innerHTML assignment from a @wire-bound value. Or template.innerHTML = userContent. Any path from external data into the DOM without sanitisation.

Detector: detectors/lwc-aura. The LWC DOM-XSS rule traces @wire / @api / @track fields into innerHTML, outerHTML, and insertAdjacentHTML sinks without an intervening DOMPurify call.

11. Aura DOM XSS​

aura:unescapedHtml rendered with a user-controlled value, or Component.set('v.html', userData) into an aura:unescapedHtml body. The Aura equivalent of #10.

Detector: detectors/lwc-aura. The Aura DOM-XSS rule flags aura:unescapedHtml attributes whose value expression traces back to a controller call returning user input.

12. PostMessage handler missing origin check​

window.addEventListener('message', e => { ... }) with no e.origin check. Any cross-origin frame can post into the handler.

Detector: detectors/lwc-aura. The postMessage rule flags addEventListener('message', ...) callbacks that reference event.data without first comparing event.origin against a literal or known-good list.

13. Stack trace disclosure​

Exception.getStackTraceString() returned to a user-facing surface (an @AuraEnabled method response, a Visualforce <apex:outputText>, a REST resource response body).

Detector: visualforce and the Apex URL-safety rule. Any call to Exception.getStackTraceString() is flagged: the API itself is the smell, because stack-trace strings should never reach a user-facing surface.

14. SecuritySettings hardening​

The org's SecuritySettings.settings metadata: session timeout, HTTPS required, clickjack protection, CSRF on GET / POST, password policy length / complexity / expiration / lockout. Any of these set to a weak value fails review.

Detector: detectors/security-settings. Ten distinct checks on the SecuritySettings file: each weak setting is its own finding with the exact pillar it violates.

15. LightningMessageChannel isExposed=true​

A .messageChannel-meta.xml file with <isExposed>true</>. The channel becomes reachable across packages, including from any LWC the package consumer installs.

Detector: detectors/security-settings. The LMC inventory rule scans every *.messageChannel-meta.xml in the package and flags <isExposed>true</> at Medium severity (admin should confirm the cross-package surface is intended).

16. PageReference / setEndpoint untrusted concatenation​

new PageReference(userInput). HttpRequest.setEndpoint(baseUrl + userInput). Any URL construction that concatenates untrusted input. Catches open-redirect and the "credentials in URL query string" smell when the untrusted input is itself a sensitive identifier.

Detector: visualforce. The URL-safety rule flags new PageReference(...) constructors and HttpRequest.setEndpoint(...) calls whose argument is a string-concatenation expression that includes a variable not bound to a Named Credential.

17. Aura action CSRF​

Aura @AuraEnabled action invoked from a custom DOM element (not the Aura framework) without the framework-injected CSRF token. Rare but material on custom-rendered components.

Detector: detectors/lwc-aura. The Aura CSRF rule flags $A.enqueueAction patterns that bypass the standard form-level CSRF token, and XmlHttpRequest-style direct posts to Aura endpoints inside .cmp controllers.

18. Hardcoded secrets in Apex / LWC​

API keys, OAuth client secrets, AWS access keys, Slack webhooks pasted into Apex string literals, LWC .js, or .resource content. Catches the entire AppExchange "rejected for hardcoded secret" bucket.

Detector: detectors/apex. The secrets rule covers six credential patterns (AWS Access Key + Secret, Slack webhook, GitHub token, generic API-key shape, Bearer token), plus the engine's entropy-based string-literal scan. Visible across .cls, .trigger, .js, and .resource content.

19. System.debug of sensitive variables​

System.debug(password). System.debug('OAuth token: ' + accessToken). The debug log is preserved across every transaction and is readable by any user with View All Data; treating it as a public surface is the right mental model.

Detector: Apex logging detector (sf_apex_logging). The rule flags System.debug(...) calls whose argument expression references a variable whose name matches a sensitive-identifier heuristic (password, secret, token, api_key, session_id, etc.).

20. Agentforce class-bypass (ForcedLeak)​

A GenAiFunction Apex action whose target class is declared without sharing. The Apex action runs in the agent's runtime user context, so a without sharing class leaks data across users via the agent. Disclosed in 2025 as CVSS 9.4.

Detector: detectors/agentforce. The ForcedLeak rule is a two-pass walk: pass one identifies every GenAiFunction Apex action, pass two opens the referenced .cls file and confirms the source-level sharing keyword. Fires High when the target class is without sharing.

What about #X (the ones not in this list)?​

Every Top-20 entry has an explicit Covered / Partial / Out of scope status on the methodology page (section 10.1: "AppExchange Top-20 vulnerability coverage"). The handful of Salesforce entries marked Out of scope (cosmetic Lightning CSS load, Classic-only JavaScript, password-echo runtime checks, username-enumeration DAST-probes) are documented there with the architectural reason.

The full coverage matrix on the methodology page also names the rule ID for every Covered entry, so a finding emitted by vulkro-sf can be traced back to the row that justifies it.

Vulkro is a product of Reveriext.

reveriext.com

Visit Reveriext