This lab explains how sharing violations occur in Apex service and controller layers, how to validate real data exposure, and how to enforce the Salesforce record-visibility model reliably.
Executive Summary
Sharing violations arise when code executes in a context that ignores intended record visibility, allowing users to retrieve records they should not access under org-wide defaults, role hierarchy, and sharing rules.
Salesforce Attack Surface
Classes declared without sharing in user-facing execution paths
Service methods reused by multiple controllers without access checks
Report/export endpoints returning broad datasets
Custom API wrappers exposing cross-team or cross-region records
Mixed class hierarchies where enforcement is assumed but not guaranteed
Business Impact
Record confidentiality loss: users access out-of-scope customer or deal data
Regulatory and contractual exposure: unauthorized data disclosure events
Trust and governance impact: security model appears inconsistent
PoC Use Cases
// Vulnerable sharing pattern
public without sharing class OpportunityService {
public List<Opportunity> fetchPipeline() {
return [SELECT Id, Name, Amount, StageName FROM Opportunity];
}
}
If invoked by a low-visibility user, this may return opportunities outside permitted scope.
Testing Methodology
Enumerate class-level sharing declarations in exposed execution paths
Test with users across different roles, territories, and OWD constraints
Compare expected visibility vs returned record sets
Trace controller-to-service call chains for implicit sharing bypasses
Document exploitability with reproducible role-based evidence
Secure Engineering Patterns
Default to with sharing for user-context execution paths
Use user-mode-aware query patterns where applicable
Perform explicit authorization checks for sensitive operations
Centralize data-access guardrails in shared repository/service layers
Review inheritance and invocation chains to avoid accidental bypass
// Safer sharing enforcement
public with sharing class OpportunityService {
public List<Opportunity> fetchPipeline() {
return [SELECT Id, Name, Amount, StageName FROM Opportunity];
}
}