Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Sf Security Model

ASecurity

Salesforce security model and secure-coding enforcement for Apex, LWC, and metadata - org/object/field/record layers, permission sets vs profiles vs permission set groups vs muting permission sets, CRUD and FLS enforcement with WITH USER_MODE, AccessLevel.USER_MODE, Security.stripInaccessible, and Schema describe checks, sharing keywords and Apex managed sharing __Share records, Lightning Web Security and CSP, SOQL injection, XSS, open redirect and CSRF fixes, Named Credentials and protected ...

2 stars
0 votes
0 copies
0 views
Added 9/19/2026
developmentjavascriptjavabashsqlnodetestingdebuggingapidatabasesecurity

Works with

cliapi

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add grzmol/vibe-force --skill sf-security-model --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sf Security Model?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Sf Security Model
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/grzmol-sf-security-model/badge)](https://www.skillsdirectory.com/skills/grzmol-sf-security-model)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: sf-security-model
description: Salesforce security model and secure-coding enforcement for Apex, LWC, and metadata - org/object/field/record layers, permission sets vs profiles vs permission set groups vs muting permission sets, CRUD and FLS enforcement with WITH USER_MODE, AccessLevel.USER_MODE, Security.stripInaccessible, and Schema describe checks, sharing keywords and Apex managed sharing __Share records, Lightning Web Security and CSP, SOQL injection, XSS, open redirect and CSRF fixes, Named Credentials and protected custom metadata for secrets, Shield Platform Encryption impacts, and Event Monitoring. Use when writing or reviewing @AuraEnabled controllers, triggers, dynamic SOQL, lwc:dom="manual" markup, permission set metadata, integration users, or when running vf-check analyzer or the sf-security-reviewer agent.
---

# Salesforce Security Model

## When to use

- Any Apex class that queries or writes data, especially `@AuraEnabled`, `@InvocableMethod`, `@RestResource`, `global`, or Visualforce controller entry points.
- Building or reviewing permission metadata (`permissionsets/`, `permissionsetgroups/`, object/field permissions).
- Dynamic SOQL/SOSL, `Database.query`, string-concatenated queries, `lwc:dom="manual"`, `innerHTML`, redirects, HTTP callouts with credentials.
- Wave 2 of the vibe-force workflow: `sf-security-reviewer` reads this skill before reviewing a diff; `vf-check analyzer` enforces the machine-verifiable subset.
- Auditing the standing posture of a whole org rather than one change: skill `sf-org-security-audit`, which reuses the permission queries in `references/permission-architecture.md`.

## API version 67.0 default-mode change (read this first)

`config/vibe-force.defaults.json` pins `apiVersion` `67.0`. That version changed the Apex security defaults:

| Behaviour | API 66.0 and earlier | API 67.0 and later |
| --- | --- | --- |
| Database operation access mode | system mode (FLS/object perms ignored) | **user mode** (FLS, object perms, sharing enforced) |
| Class with no sharing declaration | resolved per inheritance chain / entry point, often `without sharing` | **`with sharing`** |
| Trigger body DML/SOQL | system mode | **user mode**, which overrides the trigger's own `without sharing` context |
| Anonymous Apex, Connect in Apex | `with sharing` | `with sharing` |
| `WITH SECURITY_ENFORCED` in a SOQL `SELECT` in Apex | allowed, with limitations | **not allowed** - the compiler rejects it; use `WITH USER_MODE` |

Consequences for vibe-force code: never rely on the default. Declare `with sharing` / `inherited sharing` / `without sharing` explicitly on every class with data access, and set an explicit access mode (`WITH USER_MODE`, `WITH SYSTEM_MODE`, `as user`, `as system`, `AccessLevel.*`) on every database operation so the code reads the same on any API version. Triggers cannot carry a sharing declaration: they always run `without sharing`, so delegate to a handler class (see skill `sf-apex-development`).

## Security layer decision table

| Layer | Controls | Configured with | Enforced in Apex by |
| --- | --- | --- | --- |
| Org | login IP ranges, hours, MFA, session settings | Setup, security policies | nothing in code |
| Object (CRUD) | create/read/edit/delete per object | permission sets, permission set groups, profiles | user mode, `Schema.DescribeSObjectResult.isCreateable/isAccessible/isUpdateable/isDeletable`, `stripInaccessible` |
| Field (FLS) | read/edit per field | permission sets (`fieldPermissions`) | user mode, `Schema.DescribeFieldResult`, `stripInaccessible` |
| Record | which rows a user sees | OWD, role hierarchy, sharing rules, manual shares, Apex managed sharing, restriction rules | class sharing keyword + user mode |
| Apex/VF class | who may invoke the class | `classAccesses` / `pageAccesses` in permission sets | runtime access check on `@AuraEnabled` and VF entry points |
| Client | DOM/script isolation | Lightning Web Security, CSP | LWS sandbox, `lightning/ui*Api` (enforces FLS server-side) |

Object/field permissions and sharing are independent and can coexist; where they conflict, object-level and field-level permissions take precedence over sharing rules.

## Core patterns

### 1. Query in user mode (default choice)

```apex
public with sharing class ExpenseSelector {
    public static List<Expense__c> recent(Integer rowLimit) {
        return [
            SELECT Id, Name, Amount__c, Client__c, Date__c
            FROM Expense__c
            WHERE Date__c = LAST_N_DAYS:30
            WITH USER_MODE
            ORDER BY Date__c DESC
            LIMIT :rowLimit
        ];
    }
}
```

`WITH USER_MODE` processes every clause including `WHERE`, supports polymorphic fields (`Owner`, `Task.WhatId`), reports all FLS errors, and exposes them through `QueryException.getInaccessibleFields()`. It is the only clause-based option at our target version: in API 67.0 and later a SOQL `SELECT` in Apex cannot use `WITH SECURITY_ENFORCED` at all. Legacy (pre-67.0) code that still carries `WITH SECURITY_ENFORCED` must be migrated to `WITH USER_MODE` when its class is raised to 67.0; that clause also reported only the first error, skipped the `WHERE` clause, ignored polymorphic fields, and was never allowed in AppExchange packages.

### 2. DML in user mode, with error introspection

```apex
public with sharing class ExpenseWriter {
    public static void save(List<Expense__c> expenses) {
        List<Database.SaveResult> results = Database.insert(expenses, false, AccessLevel.USER_MODE);
        for (Database.SaveResult r : results) {
            if (!r.isSuccess()) {
                for (Database.Error e : r.getErrors()) {
                    System.debug(LoggingLevel.WARN, e.getStatusCode() + ' on ' + e.getFields());
                }
            }
        }
    }
}
```

Statement form: `insert as user expenses;` / `update as system expenses;`. On a user-mode `DmlException`, `getDmlFieldNames()` returns the fields that failed FLS.

### 3. Graceful degradation with `stripInaccessible`

```apex
SObjectAccessDecision decision = Security.stripInaccessible(
    AccessType.READABLE,
    [SELECT Name, BudgetedCost, ActualCost FROM Campaign WITH SYSTEM_MODE]
);
if (decision.getRemovedFields().get('Campaign')?.contains('ActualCost') == true) {
    // render the table without ActualCost instead of throwing
}
List<Campaign> safe = (List<Campaign>) decision.getRecords();
```

`AccessType` values: `READABLE`, `CREATABLE`, `UPDATABLE`, `UPSERTABLE`. `Id` is never stripped. `AggregateResult` is unsupported and throws. Use `getModifiedIndexes()` to detect that anything was stripped, and `sObject.isSet('Field__c')` to test a specific field.

### 4. Dynamic SOQL without injection

```apex
Map<String, Object> binds = new Map<String, Object>{ 'title' => '%' + userInput + '%' };
List<Personnel__c> rows = Database.queryWithBinds(
    'SELECT Id, Name, Title__c FROM Personnel__c WHERE Title__c LIKE :title',
    binds,
    AccessLevel.USER_MODE
);
```

`Database.queryWithBinds` (API 57.0+) resolves binds from a map, so the variables need not be in scope. Bind map keys are compared case-insensitively; duplicate case-variant keys throw `QueryException`. `String.escapeSingleQuotes` only helps inside single-quoted literals and is explicitly not the recommended defence. Field and object names cannot be bound: allowlist them (`Set<String>` of permitted API names) plus a describe accessibility check.

### 5. Record access beyond sharing rules: Apex managed sharing

```apex
Job__Share share = new Job__Share(
    ParentId = job.Id,
    UserOrGroupId = job.Recruiter__c,
    AccessLevel = 'Edit',
    RowCause = Schema.Job__Share.RowCause.Recruiter__c
);
Database.SaveResult sr = Database.insert(share, false, AccessLevel.USER_MODE);
```

Share objects are `<Object>Share` / `MyObject__Share`. Access levels: `Read`, `Edit`, `All` (`All` is managed-sharing only; `None` applies to `AccountShare` only). Apex sharing reasons (`RowCause`) are custom-object-only and survive owner changes; `RowCause = 'Manual'` shares are deleted when the owner changes. The object's OWD must not already be the most permissive setting, otherwise insert fails with `FIELD_FILTER_VALIDATION_EXCEPTION`. Details and the recalculation batch pattern: `references/sharing-and-record-access.md`.

### 6. Ship permission sets, not profiles

Grant all functional access through permission sets and compose them with permission set groups; use muting permission sets to subtract from a group rather than forking a permission set. Profiles stay minimal (`Minimum Access - Salesforce` plus license/defaults). Metadata shapes, retrieval caveats, and deployment ordering: `references/permission-architecture.md`.

### 7. Secrets never live in source

| Need | Use | Never |
| --- | --- | --- |
| Outbound HTTP auth | Named Credential + External Credential (`callout:MyNC/path`) | hardcoded tokens, `setHeader('Authorization', 'Bearer ...')` |
| Package-scoped secret | protected custom metadata type in a namespaced managed package | custom setting readable by users |
| Org-local operational secret | protected custom setting / custom metadata restricted by permission set | Apex constant, static resource |
| Crypto keys/IVs | `Crypto.generateAesKey`, stored via the above | literal key/IV in Apex (`ApexBadCrypto`) |

Credentials in `ExternalCredential` are encrypted by the platform with org-specific keys. vibe-force hooks scan writes for credential-looking literals and block them in `standard` and `strict` hook modes; `vf-check analyzer` flags `ApexSuggestUsingNamedCred` and `ApexBadCrypto`.

### 8. Client-side security

- Lightning Web Security is the default architecture for orgs created in Winter '23 and later and GA for all orgs since Summer '23; it replaces Lightning Locker with per-namespace JavaScript virtualization. Components must not override `window`/`document` functions, must load libraries via `lightning/platformResourceLoader` from a static resource, and must not use inline `<script>`.
- CSP for Lightning is `default-src 'self'`, `script-src 'self'`, `object-src 'self'`, `style-src 'self' https:`, `img-src 'self' http: https: data:`, `connect-src 'self'`, `frame-src https:`, `frame-ancestors https:`, `font-src https: data:`. Enable the stricter CSP org setting and develop against it. `unsafe-inline` and `unsafe-eval` are not blocked by CSP today but are rejected in AppExchange security review.
- `lightning/uiRecordApi`, `lightning-record-form`, and the other Lightning Data Service wire adapters apply sharing, CRUD, and FLS for you. Prefer them over custom `@AuraEnabled` data plumbing (skill `sf-lwc-development`).
- Every `@AuraEnabled` method is a public web service: assume an attacker calls it with arbitrary parameters, regardless of what your component sends.

## Anti-patterns

| Anti-pattern | Why it fails | Fix |
| --- | --- | --- |
| `public class Svc { ... }` with DML | sharing mode depends on API version and call chain | declare `with sharing` (or `inherited sharing` for reusable services) |
| `[SELECT ... FROM Account]` in a class saved at 66.0 and callers assuming FLS | pre-67.0 default is system mode | add `WITH USER_MODE` |
| `Database.query('... WHERE Name = \'' + input + '\'')` | SOQL injection | `Database.queryWithBinds` with `AccessLevel.USER_MODE` |
| `String.escapeSingleQuotes(input)` on an unquoted boolean/number position | escaping does nothing outside quotes | typecast or allowlist |
| `with sharing` used as an FLS control | sharing keywords set record access only | add user mode or `stripInaccessible` |
| `stripInaccessible` result discarded (`insert original;`) | inaccessible fields still written | insert `decision.getRecords()` |
| `WITH SECURITY_ENFORCED` anywhere in new Apex | rejected by the compiler at API 67.0 and later | `WITH USER_MODE` |
| `this.template.querySelector('div').innerHTML = serverHtml` | XSS inside the component | render with template directives, or sanitize before `lwc:dom="manual"` |
| `PageReference(ApexPages.currentPage().getParameters().get('retURL'))` | open redirect (`ApexOpenRedirect`) | allowlist relative paths |
| DML in a constructor or `@AuraEnabled(cacheable=true)` getter | CSRF-on-GET (`ApexCSRF`) | move state changes to an explicit POST-style action |
| `System.debug(userRecord)` with PII, `APEX_CODE=FINEST` in prod | debug logs leak PII; FINEST logs every variable assignment | scrub logs, cap log level (skill `sf-debugging-logs`) |
| Integration user with `Modify All Data` | no least privilege, blast radius on token leak | dedicated integration permission set, object-scoped |

## Verification

```bash
# security-tagged static analysis (PMD + ESLint + Flow + Regex engines)
sf code-analyzer run --workspace . --rule-selector Security --view detail

# path-based CRUD/FLS analysis over Apex (Graph Engine)
sf code-analyzer run --workspace . --rule-selector sfge --target force-app/main/default/classes

# the gate vibe-force runs; fails at configured analyzerFailSeverity (default 3)
node "${CLAUDE_PLUGIN_ROOT}/scripts/checks/vf-check.mjs" analyzer --changed

# prove enforcement at runtime with a least-privilege user
sf apex run test --tests SecurityEnforcementTest --synchronous --target-org vf-dev

# confirm what a permission set actually grants in the org
sf data query --query "SELECT SobjectType, PermissionsRead, PermissionsCreate, PermissionsEdit, PermissionsDelete FROM ObjectPermissions WHERE ParentId IN (SELECT Id FROM PermissionSet WHERE Name = 'Expense_App_User')" --target-org vf-dev
```

Runtime proof pattern (full version in `references/crud-fls-enforcement.md`): create a minimum-access user, assign only the intended permission set, wrap the call in `System.runAs`, and assert that an unauthorized call throws `System.QueryException` / `System.DmlException` / `SecurityException` rather than silently returning data.

## PR security checklist

1. Every class touching data has an explicit sharing declaration; triggers delegate to handlers.
2. Every SOQL/SOSL/DML has an explicit access mode; `WITH SYSTEM_MODE` / `as system` / `without sharing` carries a comment naming the business reason.
3. No string-concatenated user input in a query; dynamic field/object names allowlisted.
4. `@AuraEnabled` / `@RestResource` / `@InvocableMethod` parameters validated and never used to choose objects or fields directly.
5. No secrets in source; callouts use Named Credentials; endpoints are HTTPS (`ApexInsecureEndpoint`).
6. No `innerHTML`/`lwc:dom="manual"` with server data; no inline script; libraries loaded from static resources.
7. Permission sets updated in the same PR as new objects, fields, Apex classes, and pages; no profile edits.
8. Tests assert negative access with `System.runAs` for at least one least-privilege persona.
9. `vf-check analyzer` and `vf-check local` pass; any suppression has a rule selector, a limit, and a reason.
10. No PII in `System.debug`; no `APEX_CODE=FINEST` trace flags left active.

## References

- `references/crud-fls-enforcement.md` - decision matrix and runnable code for every enforcement mechanism, with tests.
- `references/sharing-and-record-access.md` - OWD, hierarchy, sharing rules, `__Share` objects, recalculation, restriction rules.
- `references/secure-coding-catalogue.md` - vulnerability to vulnerable code to fixed code to detecting rule.
- `references/permission-architecture.md` - permission set, group, and muting metadata, deployment notes.
- Apex Developer Guide: [Apex Security and Sharing Model](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_security_sharing_chapter.htm) - authoritative Versioned Behavior Changes list for API 67.0.
- Apex Developer Guide: [Enforce Object and Field Permissions](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_perms_enforcing.htm), [Set an Access Mode for Database Operations](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_enforce_usermode.htm), [stripInaccessible](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_with_security_stripInaccessible.htm), [sharing keywords](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_keywords_sharing.htm), [Apex Managed Sharing](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_bulk_sharing.htm), [Dynamic SOQL](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dynamic_soql.htm)
- Secure Coding Guide: [CRUD/FLS](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_access_control_protect_from_crud_fls_vulnerabilities.htm), [Sharing Violations](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_access_control_protect_from_sharing_violations.htm), [SOQL Injection](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_sql_injection.htm), [XSS](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_cross_site_scripting.htm), [Storing Sensitive Data](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_storing_sensitive_data.htm), [Lightning Security](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_lightning_security.htm)
- LWC Developer Guide: [Secure Apex Classes](https://developer.salesforce.com/docs/platform/lwc/guide/apex-security.html)
- [Lightning Web Security](https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-intro.html)
- Metadata API: [PermissionSet](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_permissionset.htm), [PermissionSetGroup](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_permissionsetgroup.htm), [MutingPermissionSet](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_mutingpermissionset.htm), [ExternalCredential](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_externalcredential.htm)
- Object Reference: [EventLogFile](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_eventlogfile.htm)
- Sibling skills: `sf-apex-development`, `sf-apex-testing`, `sf-lwc-development`, `sf-code-analyzer-quality`, `sf-debugging-logs`, `sf-deployment-strategies`, `sf-post-deploy-verification`, `sf-integration-patterns`.

Attribution

grzmolgrzmol
View sourceMore from grzmol →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →