
Sf Security
Apply Salesforce CRUD/FLS, USER_MODE SOQL/DML, stripInaccessible, and with-sharing class patterns in Apex you are writing or reviewing.
Install
npx skills add https://github.com/clientell-ai/salesforce-skills --skill sf-securityWhat is this skill?
- SOQL and dynamic query examples with WITH USER_MODE and Database.query AccessLevel.USER_MODE
- stripInaccessible for CREATABLE, UPDATABLE, and READABLE flows before DML and API responses
- Database.insert/update/upsert with AccessLevel.USER_MODE
- Guidance to default to public with sharing and reserve without sharing for explicit system-level cases
- Reference-style snippets for sharing model and field-level security enforcement
Adoption & trust: 1 installs on skills.sh; 7 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Journey fit
Ship/security is the canonical shelf because the reference encodes enforcement before release and audit-ready access control, though patterns are written during Build. Security subphase matches CRUD/FLS, sharing declarations, and data exposure controls rather than generic Apex feature work.
Common Questions / FAQ
Is Sf Security safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Sf Security
# Salesforce Security Patterns Reference ## CRUD/FLS Enforcement ### SOQL — User Mode ```apex // Enforces both CRUD and FLS automatically List<Account> accounts = [ SELECT Id, Name, Industry FROM Account WHERE Industry = :filter WITH USER_MODE ]; // Dynamic SOQL with user mode List<Account> accounts = Database.query( 'SELECT Id, Name FROM Account', AccessLevel.USER_MODE ); ``` ### DML — stripInaccessible ```apex // Before INSERT SObjectAccessDecision decision = Security.stripInaccessible( AccessType.CREATABLE, records ); insert decision.getRecords(); // Before UPDATE SObjectAccessDecision decision = Security.stripInaccessible( AccessType.UPDATABLE, records ); update decision.getRecords(); // Before returning data to user SObjectAccessDecision decision = Security.stripInaccessible( AccessType.READABLE, records ); return decision.getRecords(); // Check which fields were stripped Set<String> strippedFields = decision.getRemovedFields().get('Account'); ``` ### Database Operations with AccessLevel ```apex // Insert with user mode Database.insert(records, AccessLevel.USER_MODE); // Update with user mode Database.update(records, AccessLevel.USER_MODE); // Upsert with user mode Database.upsert(records, ExternalId__c, AccessLevel.USER_MODE); ``` ## Sharing Model ### Class Declarations ```apex // DEFAULT — always use this public with sharing class MyService { } // Only when system-level access is explicitly needed public without sharing class SystemDataService { } // Inherits from caller public inherited sharing class UtilityClass { } ``` ### When to Use Without Sharing - Aggregate reporting queries that span ownership - System-level operations in batch jobs - Platform event handlers that need cross-user access - **Always document the reason in a comment** ## SOQL Injection Prevention ### Bind Variables (Preferred) ```apex String nameFilter = userInput; List<Account> results = [ SELECT Id, Name FROM Account WHERE Name = :nameFilter WITH USER_MODE ]; ``` ### escapeSingleQuotes (Dynamic SOQL) ```apex String safeName = String.escapeSingleQuotes(userInput); String query = 'SELECT Id FROM Account WHERE Name = \'' + safeName + '\''; ``` ### Never Do This ```apex // VULNERABLE — direct concatenation String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\''; ``` ## Visualforce XSS Prevention ### Output Encoding ```html <!-- Auto-escaped (safe) --> <apex:outputText value="{!accountName}"/> <!-- Manual encoding when needed --> <script> var name = '{!JSENCODE(accountName)}'; var url = '{!URLENCODE(accountName)}'; </script> <!-- DANGEROUS — never use --> <apex:outputText value="{!accountName}" escape="false"/> ``` ## Named Credentials (No Hardcoded Secrets) ```apex // GOOD — uses Named Credential HttpRequest req = new HttpRequest(); req.setEndpoint('callout:My_Named_Credential/api/resource'); req.setMethod('GET'); // BAD — hardcoded req.setEndpoint('https://api.example.com/resource'); req.setHeader('Authorization', 'Bearer ' + hardcodedToken); ``` --- ## Schema Describe FLS Checks ### isAccessible Pattern (Read Check) ```apex public class SecureQueryService { public static List<Account> getAccounts(Set<Id> ids) { // Object-level read check if (!Schema.sObjectType.Account.isAccessible()) { throw new AuraHandledException('Insufficient access to Account'); } // Field-level read checks Map<String, Schema.SObjectField> fieldMap = Schema.sObjectType.Account.fields.getMap(); List<String> queryFields = new List<String>{'Id'}; for (String fieldName : new List<String>{'Name', 'Industry', 'AnnualRevenue', 'Phone'}) { if (fieldMap.get(fieldName).getDescribe().isAccessible()) { queryFields.add(fieldName); } } String soql = 'SELECT ' + String.join(queryFields, ', ') + ' FROM Account WHERE Id IN :ids';