The AgentExchange Security Review (formerly AppExchange) has a clock built into it. Salesforce's ISVforce guide describes the flow: you submit through the Partner Console, pick a review office-hours slot that is typically three to four weeks out, and the review itself can take up to four weeks after verification. Solutions are also re-reviewed periodically, so this is not a one-time gate.
A fail restarts that clock. If the reviewer finds a CRUD/FLS gap in one controller, you fix it, resubmit and wait again. Two failures can push a launch by a quarter. The cost of the review is therefore not the review itself but the number of times you go through it.
The way to go through it once is to know the verdict before you submit. This post explains what reviewers look at, what Salesforce requires you to bring, and how a local readiness report with a PASS, FAIL or NOT EVALUATED on each section of the checklist gives you a verdict you can act on while the office-hours date is still weeks away.
What the reviewer is looking for
The security requirements page in the ISVforce guide has been in effect since 2023-08-09, and the list has not become shorter. The checklist covers the classes you would expect from a code review on any platform, in their Salesforce form: queries built from user input, DML without object and field-level checks, sharing declarations, cryptography, CSRF on Visualforce and Lightning pages, and reflected output.
One of these dominates. Missing authorization (CWE-862) sits at #4 in the 2025 CWE Top 25. In Apex, missing authorization is a CRUD or FLS gap: a without sharing class, a DML statement that never asked whether the running user may update the field, a query that returns records the caller should not see. Every reviewer knows this is where packages fail, which is why it is where you should spend your preparation.
What you must bring, and what a scanner is not
Salesforce requires a Code Analyzer report with your submission. The guide describes Salesforce Code Analyzer as a bundle of the platform's own Apex rule engine, its copy-paste detector, a JavaScript linter, a library-vulnerability checker and the Graph Engine, and it also requires that all new solutions be Lightning Ready.
Two things follow. First, no third-party scanner replaces that report. Vulkro does not. The Code Analyzer output is a required artifact, and the readiness report described below is built to sit next to it, not instead of it. Second, the Salesforce reviewer makes the final call. A readiness verdict of READY is a statement about what an automated check could establish against the published checklist. It is not a promise about how a human reviewer will read your package.
With those limits stated, here is what the local report does add.
A verdict per section, not a list of findings
vulkro-sf appexchange-report maps every finding from a package scan onto the review checklist and prints a PASS, FAIL or NOT EVALUATED for each section, then an overall READY or GAPS FOUND.
vulkro-sf appexchange-report ./my-package -o report.html --packetThe mapping uses two sets of categories. SF-ASRR-001 to 006 are the security review requirement areas. SF-PRESUBMIT-001 to 010 are the pre-submission items that trip packages before a reviewer ever reads code: the things the Partner Console or the first verification pass rejects.
Two design choices matter for trusting the result. The report lists every finding that did not map to a checklist section, so a real issue cannot vanish because it fell between categories. And it pins the mapping version, so when the checklist moves (it does) you can tell which version of the checklist your READY was measured against.
NOT EVALUATED is a verdict in its own right. Some sections depend on things a source scan cannot see: a runtime configuration, a managed-package setting, a human judgment about data handling. The report says so rather than counting those sections as passed. If you are preparing a submission and a section reads NOT EVALUATED, that is the section you review by hand.
Drafting the false-positive justifications
A common reason for a second round is not a real bug. It is a Code Analyzer result that the reviewer asks you to explain, and that you explain badly or late. The review process accepts false-positive justifications, and writing them is tedious because each one needs the finding, the reason it is not exploitable and the control that makes it so.
The readiness report takes the Code Analyzer output as input. --import-code-analyzer <file> accepts JSON, SARIF or CSV, cross-checks each result against Vulkro's own analysis of the same code, and drafts a justification for the ones that look like false positives.
vulkro-sf appexchange-report ./my-package \
--import-code-analyzer code-analyzer.json \
--fpw-out FPW.json \
-o report.html --packetEvery entry written by --fpw-out FPW.json is marked draft. That label is deliberate. A draft justification is a starting point for the developer who knows the code, not a document to forward as is. If the draft says a query is safe because the value is bound, and you know the value is actually concatenated two methods away, the draft is wrong and you must fix it. Vulkro's job here is to get you from a blank page to a reviewed page faster, not to sign the page for you.
For a longer treatment of what reviewers accept in a justification, see AgentExchange Security Review false positives.
Running it in CI so the verdict stays current
A readiness report run once, three weeks before office hours, goes stale the moment someone merges a new controller. The useful pattern is to make it part of the package pipeline.
vulkro-sf appexchange-report ./my-package --format markdown --gateWith --gate, the command exits 1 when a pre-submit item fails, so a pull request that reintroduces an SF-PRESUBMIT failure is visible before it lands. The Markdown output reads well in a pull-request comment or a pipeline log. The HTML report with --packet is the one you keep for the submission itself.
The detectors behind the mapping are the Apex rules that cover the review's main failure classes: apex-taint-soql-injection, escape-single-quotes-call for an escaped value used in a slot where escaping does nothing, mass-delete-unfiltered-no-authz (CWE-285), broken-object-level-auth/idor, mass-assignment-json-deserialize-sobject, the CRUD/FLS and sharing checks, crypto misuse, CSRF and reflected XSS. If you want to see what fails most often, what fails and how to pass the first time goes through the list.
What to do this week
- Book the office-hours slot now. The queue is the longest fixed cost and nothing you do locally shortens it.
- Run the Code Analyzer and keep its output. It is required, and it is the input to the false-positive drafts.
- Run
vulkro-sf appexchange-reporton the package and read every FAIL and every NOT EVALUATED section before you read anything else. - Review each
draftjustification with the developer who owns the code, and delete any draft you cannot stand behind. - Add
--format markdown --gateto the package pipeline so the verdict does not go stale between now and the review date.
The review process page explains how the readiness report fits around the official steps. To run it on your own package before your office-hours date, start the 14-day trial.