Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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".
.claude/skills/sentry-security/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✗→✓ | ▲ Improved | — | — |
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-21 | ✗→✓ | ▲ Improved | — | — |
| case-20 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
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()a downstream query in the same flow IS org-scoped and raises the same error — no differential behavior means no information leak
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.
`markdown## 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.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-19 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.