Apex
These checks cover every *.cls and *.trigger file, in both the SFDX (force-app/main/default/classes/) and MDAPI (src/classes/) layouts.
vulkro-sf scan ./force-app
Checks
| Check | What it finds |
|---|---|
SOQL injection (apex-taint-soql-injection) | Untrusted input concatenated into Database.query(...) or other dynamic SOQL. Bind variables (:name) and String.escapeSingleQuotes(...) clear it. |
escapeSingleQuotes outside quotes (escape-single-quotes-call) | An escaped value placed in an unquoted slot: a field or object name, ORDER BY / LIMIT, a numeric or date position, an IN (...) list or a SOSL clause. Quoted, LIKE '%...%' and SOSL '*...*' wraps are safe. |
Mass delete without authorization (mass-delete-unfiltered-no-authz) | delete [SELECT ...] with no WHERE / LIMIT in an @AuraEnabled, @RestResource or webservice class with no isDeletable(), Security.stripInaccessible or USER_MODE gate (CWE-285). |
| CRUD on writes | insert / update / delete / upsert with no isCreateable() / isUpdateable() / isDeletable(), no WITH SECURITY_ENFORCED on the prior read and no AccessLevel.USER_MODE. |
| FLS on reads | SOQL returning restricted fields with no isAccessible(), WITH USER_MODE or Security.stripInaccessible(AccessType.READABLE, ...). |
| USER_MODE migration | An advisory on WITH SECURITY_ENFORCED so you can plan the move to USER_MODE. |
| Sharing | A class declared without sharing (or with no keyword) that reads or writes user-owned objects. |
IDOR (broken-object-level-auth/idor) | A method that takes an Id and selects by it with no ownership filter such as OwnerId = :UserInfo.getUserId(). |
Mass assignment (mass-assignment-json-deserialize-sobject) | JSON.deserialize / deserializeUntyped of caller JSON into a record with no field allowlist. |
| Crypto misuse | Crypto.generateAesKey(128) (use 256), ECB mode, hardcoded IVs, MD5 / SHA-1 for passwords. |
| Secrets in code | API keys, OAuth client secrets, JWT signing keys and passwords as Apex string literals. |
| Cleartext callouts | http:// URLs as Apex literals. Named Credential endpoints are on the Named Credentials page. |
| CSRF | Apex REST methods that change state with no Authorization-header gate. |
| Reflected XSS (controller side) | A controller property read from ApexPages.currentPage().getParameters() with no escaping. The page side is on Visualforce. |
| User enumeration | Distinguishable error messages on login or password-reset endpoints. |
Confidence is High when the source flows into the sink in the same expression, Medium when the source is an instance field set elsewhere, and Low when it is a static field set at class load.
Example
Non-compliant: SOQL injection, a sharing violation and missing FLS.
public without sharing class AccountSearchCtrl {
@AuraEnabled
public static List<Account> search(String name) {
String query =
'SELECT Id, Name FROM Account WHERE Name = \'' + name + '\'';
return Database.query(query);
}
}
Compliant:
public with sharing class AccountSearchCtrl {
@AuraEnabled
public static List<Account> search(String name) {
return [SELECT Id, Name FROM Account WHERE Name = :name WITH USER_MODE];
}
}
See also: LWC and Aura, Cross-method engine, Methodology