Sentry-specific security review based on real vulnerability history. Use when reviewing Sentry endpoints, serializers, or views for security issues. Trigger keywords: "sentry security review", "check for IDOR", "access control review", "org scoping", "cross-org", "security audit endpoint".
Find security vulnerabilities in Sentry code by checking for the patterns that have caused real vulnerabilities in this codebase.
This skill is Sentry-specific. It encodes patterns from 37 real security patches shipped in the last year — not generic OWASP theory.
Review the code provided by the user (file, diff, or endpoint). Research the codebase as needed to build confidence before reporting.
Report only HIGH and MEDIUM confidence findings. Do not report theoretical issues.
| Confidence | Criteria | Action |
|---|---|---|
| HIGH | Traced the flow, confirmed no check exists | Report with fix |
| MEDIUM | Check may exist but could not confirm | Report as needs verification |
| LOW | Theoretical or mitigated elsewhere | Do not report |
Determine what you're reviewing and load the relevant reference.
| Code Type | Load Reference |
|---|---|
API endpoint (inherits from *Endpoint) | references/endpoint-patterns.md |
| Serializer or form field | references/serializer-patterns.md |
| Email template or HTML rendering | references/output-sanitization.md |
| Token, OAuth, or session handling | references/token-lifecycle.md |
| Role or permission logic | references/privilege-escalation.md |
If the code spans multiple categories, load all relevant references.
Always load references/enforcement-layers.md — it documents where security checks can legitimately live in Sentry's request lifecycle. A check in any layer counts as enforcement.
These are ordered by frequency from the last year of real patches.
The most common vulnerability. An endpoint accepts an ID from the request but does not scope the query by the organization from the URL.
Trace this flow for every ID that comes from the request:
1. Where does the ID enter? (query param, request body, URL kwarg)
2. Where is it used in an ORM query?
3. Between (1) and (2), is the query scoped by organization_id or project_id
from the URL (NOT from the request body)?
Red flags:
Model.objects.get(id=request.data["something_id"]) — no org scopeModel.objects.filter(id=request.GET["id"]) — no org scopeproject_id from request body/query used directly without Project.objects.filter(id=pid, organization_id=organization.id)OrganizationEndpoint but handler method does not accept or use the organization parameterSafe patterns:
organization_id=organization.id where organization comes from convert_args()self.get_projects() which scopes by org internallyconvert_args()An endpoint or serializer performs a sensitive operation without verifying the user has permission.
Check:
OrganizationEndpoint, ProjectEndpoint, etc.)permission_classes? If not, it inherits the base class default — verify that's appropriate.@login_required or equivalent?A user can assign ownership, modify roles, or escalate access beyond what their role allows.
Check:
OwnerActorField (validates membership), NOT ActorField (allows any actor)team:admin)Token lifecycle gaps that allow unauthorized access.
Check:
organization_id required and validated?User-controlled strings rendered unsafely in emails, markdown, or HTML.
Check:
format_html() vs string concatenation in templatesmark_safe() called on user inputAuthentication state inconsistencies.
Check:
If no checks produced a potential finding, stop and report zero findings. Do not invent issues to fill the report. An empty result is the correct output when the code has no vulnerabilities matching these patterns.
For each potential finding, trace the complete request flow end-to-end. Do not stop at the authentication class — follow into the endpoint handler, then into any business logic classes it delegates to (e.g., Validator, Refresher, GrantExchanger).
1. Authentication class → does authenticate() or authenticate_token() enforce the check?
2. Permission class → does has_permission() enforce it?
3. convert_args() → does has_object_permission() / determine_access() enforce it?
4. Access module → does from_rpc_auth() or from_request() enforce it?
5. Handler method → does the endpoint handler enforce it?
6. Business logic classes → do downstream classes (Validator, etc.) enforce it?
7. Serializer → do validate_*() methods enforce it?
A check at ANY layer is enforcement. Before marking HIGH, confirm the check is absent from all layers using the checklist in enforcement-layers.md.
If you cannot confirm the check is absent from every layer, mark the finding as MEDIUM (needs verification), not HIGH.
Cross-flow enforcement for token issuance: For token/credential issuance flows, also check whether the issued credential is blocked at usage time (e.g., determine_access() rejects it at all endpoints in the relevant scope). Classify based on the enforcement scope:
See enforcement-layers.md "Cross-Flow Enforcement."
Non-DRF views: OAuth views are plain Django views — the 7-layer DRF model does not apply to the view itself. Check the view's own decorators and handler logic. But tokens issued by these views are later used at DRF endpoints where the full enforcement chain applies.
## Sentry Security Review: [Component]
### Findings
#### [SENTRY-001] [Title] (Severity: Critical/High/Medium)
- **Category**: [IDOR | Missing Auth | Privilege Escalation | Token | XSS | Auth/MFA]
- **Location**: `path/to/file.py:123`
- **Confidence**: HIGH — confirmed through code tracing
- **Issue**: [What the vulnerability is]
- **Trace**:
1. [Step-by-step trace showing how the vulnerability is reached]
- **Impact**: [What an attacker could do]
- **Fix**:
```python
[Code that fixes the issue — must enforce, not document]
```
[MEDIUM confidence items with explanation of what to verify]
[Areas outside the scope of this review]
Fix suggestions must include actual enforcement code. Never suggest a comment or docstring as a fix.