{"id":"98f81047-ae94-402e-a369-09fbf64192a4","shortId":"fcKnyv","kind":"skill","title":"django-access-review","tagline":"django-access-review","description":"---\nname: django-access-review\ndescription: Django access control and IDOR security review. Use when reviewing Django views, DRF viewsets, ORM queries, or any Python/Django code handling user authorization. Trigger keywords: \"IDOR\", \"access control\", \"authorization\", \"Django permissions\", \"object permissions\", \"tenant...\n--- LICENSE\n---\n\n<!--\nReference material based on OWASP Cheat Sheet Series (CC BY-SA 4.0)\nhttps://cheatsheetseries.owasp.org/\n-->\n\n# Django Access Control & IDOR Review\n\nFind access control vulnerabilities by investigating how the codebase answers one question:\n\n**Can User A access, modify, or delete User B's data?**\n\n## When to Use\n- You need to review Django or DRF code for access control gaps, IDOR risk, or object-level authorization failures.\n- The task involves confirming whether one user can access, modify, or delete another user's data.\n- You want an investigation-driven authorization review instead of generic pattern matching.\n\n## Philosophy: Investigation Over Pattern Matching\n\nDo NOT scan for predefined vulnerable patterns. Instead:\n\n1. **Understand** how authorization works in THIS codebase\n2. **Ask questions** about specific data flows\n3. **Trace code** to find where (or if) access checks happen\n4. **Report** only what you've confirmed through investigation\n\nEvery codebase implements authorization differently. Your job is to understand this specific implementation, then find gaps.\n\n---\n\n## Phase 1: Understand the Authorization Model\n\nBefore looking for bugs, answer these questions about the codebase:\n\n### How is authorization enforced?\n\nResearch the codebase to find:\n\n```\n□ Where are permission checks implemented?\n  - Decorators? (@login_required, @permission_required, custom?)\n  - Middleware? (TenantMiddleware, AuthorizationMiddleware?)\n  - Base classes? (BaseAPIView, TenantScopedViewSet?)\n  - Permission classes? (DRF permission_classes?)\n  - Custom mixins? (OwnershipMixin, TenantMixin?)\n\n□ How are queries scoped?\n  - Custom managers? (TenantManager, UserScopedManager?)\n  - get_queryset() overrides?\n  - Middleware that sets query context?\n\n□ What's the ownership model?\n  - Single user ownership? (document.owner_id)\n  - Organization/tenant ownership? (document.organization_id)\n  - Hierarchical? (org -> team -> user -> resource)\n  - Role-based within context? (org admin vs member)\n```\n\n### Investigation commands\n\n```bash\n# Find how auth is typically done\ngrep -rn \"permission_classes\\|@login_required\\|@permission_required\" --include=\"*.py\" | head -20\n\n# Find base classes that views inherit from\ngrep -rn \"class Base.*View\\|class.*Mixin.*:\" --include=\"*.py\" | head -20\n\n# Find custom managers\ngrep -rn \"class.*Manager\\|def get_queryset\" --include=\"*.py\" | head -20\n\n# Find ownership fields on models\ngrep -rn \"owner\\|user_id\\|organization\\|tenant\" --include=\"models.py\" | head -30\n```\n\n**Do not proceed until you understand the authorization model.**\n\n---\n\n## Phase 2: Map the Attack Surface\n\nIdentify endpoints that handle user-specific data:\n\n### What resources exist?\n\n```\n□ What models contain user data?\n□ Which have ownership fields (owner_id, user_id, organization_id)?\n□ Which are accessed via ID in URLs or request bodies?\n```\n\n### What operations are exposed?\n\nFor each resource, map:\n- List endpoints - what data is returned?\n- Detail/retrieve endpoints - how is the object fetched?\n- Create endpoints - who sets the owner?\n- Update endpoints - can users modify others' data?\n- Delete endpoints - can users delete others' data?\n- Custom actions - what do they access?\n\n---\n\n## Phase 3: Ask Questions and Investigate\n\nFor each endpoint that handles user data, ask:\n\n### The Core Question\n\n**\"If I'm User A and I know the ID of User B's resource, can I access it?\"**\n\nTrace the code to answer this:\n\n```\n1. Where does the resource ID enter the system?\n   - URL path: /api/documents/{id}/\n   - Query param: ?document_id=123\n   - Request body: {\"document_id\": 123}\n\n2. Where is that ID used to fetch data?\n   - Find the ORM query or database call\n\n3. Between (1) and (2), what checks exist?\n   - Is the query scoped to current user?\n   - Is there an explicit ownership check?\n   - Is there a permission check on the object?\n   - Does a base class or mixin enforce access?\n\n4. If you can't find a check, is there one you missed?\n   - Check parent classes\n   - Check middleware\n   - Check managers\n   - Check decorators at URL level\n```\n\n### Follow-Up Questions\n\n```\n□ For list endpoints: Does the query filter to user's data, or return everything?\n\n□ For create endpoints: Who sets the owner - the server or the request?\n\n□ For bulk operations: Are they scoped to user's data?\n\n□ For related resources: If I can access a document, can I access its comments?\n  What if the document belongs to someone else?\n\n□ For tenant/org resources: Can User in Org A access Org B's data by changing\n  the org_id in the URL?\n```\n\n---\n\n## Phase 4: Trace Specific Flows\n\nPick a concrete endpoint and trace it completely.\n\n### Example Investigation\n\n```\nEndpoint: GET /api/documents/{pk}/\n\n1. Find the view handling this URL\n   → DocumentViewSet.retrieve() in api/views.py\n\n2. Check what DocumentViewSet inherits from\n   → class DocumentViewSet(viewsets.ModelViewSet)\n   → No custom base class with authorization\n\n3. Check permission_classes\n   → permission_classes = [IsAuthenticated]\n   → Only checks login, not ownership\n\n4. Check get_queryset()\n   → def get_queryset(self):\n   →     return Document.objects.all()\n   → Returns ALL documents!\n\n5. Check for has_object_permission()\n   → Not implemented\n\n6. Check retrieve() method\n   → Uses default, which calls get_object()\n   → get_object() uses get_queryset(), which returns all\n\n7. Conclusion: IDOR - Any authenticated user can access any document\n```\n\n### What to look for when tracing\n\n```\nPotential gap indicators (investigate further, don't auto-flag):\n- get_queryset() returns .all() or filters without user\n- Direct Model.objects.get(pk=pk) without ownership in query\n- ID comes from request body for sensitive operations\n- Permission class checks auth but not ownership\n- No has_object_permission() and queryset isn't scoped\n\nLikely safe patterns (but verify the implementation):\n- get_queryset() filters by request.user or user's org\n- Custom permission class with has_object_permission()\n- Base class that enforces scoping\n- Manager that auto-filters\n```\n\n---\n\n## Phase 5: Report Findings\n\nOnly report issues you've confirmed through investigation.\n\n### Confidence Levels\n\n| Level | Meaning | Action |\n|-------|---------|--------|\n| **HIGH** | Traced the flow, confirmed no check exists | Report with evidence |\n| **MEDIUM** | Check may exist but couldn't confirm | Note for manual verification |\n| **LOW** | Theoretical, likely mitigated | Do not report |\n\n### Suggested Fixes Must Enforce, Not Document\n\n**Bad fix**: Adding a comment saying \"caller must validate permissions\"\n**Good fix**: Adding code that actually validates permissions\n\nA comment or docstring does not enforce authorization. Your suggested fix must include actual code that:\n- Validates the user has permission before proceeding\n- Raises an exception or returns an error if unauthorized\n- Makes unauthorized access impossible, not just discouraged\n\nExample of a BAD fix suggestion:\n```python\ndef get_resource(resource_id):\n    # IMPORTANT: Caller must ensure user has access to this resource\n    return Resource.objects.get(pk=resource_id)\n```\n\nExample of a GOOD fix suggestion:\n```python\ndef get_resource(resource_id, user):\n    resource = Resource.objects.get(pk=resource_id)\n    if resource.owner_id != user.id:\n        raise PermissionDenied(\"Access denied\")\n    return resource\n```\n\nIf you can't determine the right enforcement mechanism, say so - but never suggest documentation as the fix.\n\n### Report Format\n\n```markdown\n## Access Control Review: [Component]\n\n### Authorization Model\n[Brief description of how this codebase handles authorization]\n\n### Findings\n\n#### [IDOR-001] [Title] (Severity: High/Medium)\n- **Location**: `path/to/file.py:123`\n- **Confidence**: High - confirmed through code tracing\n- **The Question**: Can User A access User B's documents?\n- **Investigation**:\n  1. Traced GET /api/documents/{pk}/ to DocumentViewSet\n  2. Checked get_queryset() - returns Document.objects.all()\n  3. Checked permission_classes - only IsAuthenticated\n  4. Checked for has_object_permission() - not implemented\n  5. Verified no relevant middleware or base class checks\n- **Evidence**: [Code snippet showing the gap]\n- **Impact**: Any authenticated user can read any document by ID\n- **Suggested Fix**: [Code that enforces authorization - NOT a comment]\n\n### Needs Manual Verification\n[Issues where authorization exists but couldn't confirm effectiveness]\n\n### Areas Not Reviewed\n[Endpoints or flows not covered in this review]\n```\n\n---\n\n## Common Django Authorization Patterns\n\nThese are patterns you might find - not a checklist to match against.\n\n### Query Scoping\n```python\n# Scoped to user\nDocument.objects.filter(owner=request.user)\n\n# Scoped to organization\nDocument.objects.filter(organization=request.user.organization)\n\n# Using a custom manager\nDocument.objects.for_user(request.user)  # Investigate what this does\n```\n\n### Permission Enforcement\n```python\n# DRF permission classes\npermission_classes = [IsAuthenticated, IsOwner]\n\n# Custom has_object_permission\ndef has_object_permission(self, request, view, obj):\n    return obj.owner == request.user\n\n# Django decorators\n@permission_required('app.view_document')\n\n# Manual checks\nif document.owner != request.user:\n    raise PermissionDenied()\n```\n\n### Ownership Assignment\n```python\n# Server-side (safe)\ndef perform_create(self, serializer):\n    serializer.save(owner=self.request.user)\n\n# From request (investigate)\nserializer.save(**request.data)  # Does request.data include owner?\n```\n\n---\n\n## Investigation Checklist\n\nUse this to guide your review, not as a pass/fail checklist:\n\n```\n□ I understand how authorization is typically implemented in this codebase\n□ I've identified the ownership model (user, org, tenant, etc.)\n□ I've mapped the key endpoints that handle user data\n□ For each sensitive endpoint, I've traced the flow and asked:\n  - Where does the ID come from?\n  - Where is data fetched?\n  - What checks exist between input and data access?\n□ I've verified my findings by checking parent classes and middleware\n□ I've only reported issues I've confirmed through investigation\n```\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["django","access","review","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding"],"capabilities":["skill","source-sickn33","skill-django-access-review","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/django-access-review","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34831 github stars · SKILL.md body (10,782 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T06:51:04.850Z","embedding":null,"createdAt":"2026-04-18T21:36:11.317Z","updatedAt":"2026-04-24T06:51:04.850Z","lastSeenAt":"2026-04-24T06:51:04.850Z","tsv":"'-001':1079 '-20':310,328,342 '-30':358 '/api/documents':510,700,1106 '1':143,195,499,540,702,1103 '123':516,521,1085 '2':151,369,522,542,712,1110 '3':158,458,538,727,1116 '4':169,575,684,739,1122 '5':752,878,1130 '6':760 '7':778 'access':3,7,12,16,41,51,56,70,90,109,166,402,456,491,574,646,651,670,785,982,1005,1038,1063,1097,1362 'action':452,893 'actual':945,961 'ad':932,942 'admin':287 'anoth':113 'answer':64,204,497 'api/views.py':711 'app.view':1258 'area':1176 'ask':152,459,470,1344,1417 'assign':1268 'attack':372 'auth':295,831 'authent':782,1147 'author':37,43,99,123,146,181,198,212,366,726,955,1067,1076,1160,1169,1189,1307 'authorizationmiddlewar':232 'auto':802,875 'auto-filt':874 'auto-flag':801 'b':75,486,672,1099 'bad':930,990 'base':233,283,312,321,569,723,867,1136 'baseapiview':235 'bash':292 'belong':658 'bodi':409,518,824 'boundari':1425 'brief':1069 'bug':203 'bulk':631 'call':537,767 'caller':936,1000 'chang':676 'check':167,222,544,558,563,582,588,591,593,595,713,728,735,740,753,761,830,900,906,1111,1117,1123,1138,1261,1356,1369 'checklist':1199,1292,1303 'clarif':1419 'class':234,238,241,302,313,320,323,334,570,590,718,724,730,732,829,862,868,1119,1137,1234,1236,1371 'clear':1392 'code':34,88,160,495,943,962,1090,1140,1157 'codebas':63,150,179,209,216,1074,1313 'come':821,1349 'command':291 'comment':653,934,949,1163 'common':1187 'complet':695 'compon':1066 'conclus':779 'concret':690 'confid':889,1086 'confirm':104,175,886,898,912,1088,1174,1381 'contain':387 'context':261,285 'control':17,42,52,57,91,1064 'core':472 'couldn':910,1172 'cover':1183 'creat':431,619,1276 'criteria':1428 'current':551 'custom':229,242,250,330,451,722,860,1220,1239 'data':77,116,156,381,389,421,443,450,469,530,614,639,674,1333,1353,1361 'databas':536 'decor':224,596,1255 'def':336,743,994,1021,1243,1274 'default':765 'delet':73,112,444,448 'deni':1039 'describ':1396 'descript':14,1070 'detail/retrieve':424 'determin':1046 'differ':182 'direct':812 'discourag':986 'django':2,6,11,15,25,44,50,85,1188,1254 'django-access-review':1,5,10 'docstr':951 'document':514,519,648,657,751,787,929,1056,1101,1152,1259 'document.objects.all':748,1115 'document.objects.filter':1209,1215 'document.objects.for':1222 'document.organization':274 'document.owner':270,1263 'documentviewset':715,719,1109 'documentviewset.retrieve':709 'done':298 'drf':27,87,239,1232 'driven':122 'effect':1175 'els':661 'endpoint':375,419,425,432,438,445,465,606,620,691,698,1179,1329,1337 'enforc':213,573,870,927,954,1049,1159,1230 'ensur':1002 'enter':505 'environ':1408 'environment-specif':1407 'error':977 'etc':1323 'everi':178 'everyth':617 'evid':904,1139 'exampl':696,987,1014 'except':973 'exist':384,545,901,908,1170,1357 'expert':1413 'explicit':556 'expos':413 'failur':100 'fetch':430,529,1354 'field':345,393 'filter':610,809,853,876 'find':55,162,192,218,293,311,329,343,531,580,703,880,1077,1196,1367 'fix':925,931,941,958,991,1018,1059,1156 'flag':803 'flow':157,687,897,1181,1342 'follow':601 'follow-up':600 'format':1061 'gap':92,193,795,1144 'generic':127 'get':254,337,699,741,744,768,770,773,804,851,995,1022,1105,1112 'good':940,1017 'grep':299,318,332,348 'guid':1296 'handl':35,377,467,706,1075,1331 'happen':168 'head':309,327,341,357 'hierarch':276 'high':894,1087 'high/medium':1082 'id':271,275,352,395,397,399,404,483,504,511,515,520,526,679,820,998,1013,1025,1031,1034,1154,1348 'identifi':374,1316 'idor':19,40,53,93,780,1078 'impact':1145 'implement':180,190,223,759,850,1129,1310 'import':999 'imposs':983 'includ':307,325,339,355,960,1289 'indic':796 'inherit':316,716 'input':1359,1422 'instead':125,142 'investig':60,121,131,177,290,462,697,797,888,1102,1225,1284,1291,1383 'investigation-driven':120 'involv':103 'isauthent':733,1121,1237 'isn':841 'isown':1238 'issu':883,1167,1378 'job':184 'key':1328 'keyword':39 'know':481 'level':98,599,890,891 'licens':49 'like':844,919 'limit':1384 'list':418,605 'locat':1083 'login':225,303,736 'look':201,790 'low':917 'm':476 'make':980 'manag':251,331,335,594,872,1221 'manual':915,1165,1260 'map':370,417,1326 'markdown':1062 'match':129,134,1201,1393 'may':907 'mean':892 'mechan':1050 'medium':905 'member':289 'method':763 'middlewar':230,257,592,1134,1373 'might':1195 'miss':587,1430 'mitig':920 'mixin':243,324,572 'model':199,266,347,367,386,1068,1319 'model.objects.get':813 'models.py':356 'modifi':71,110,441 'must':926,937,959,1001 'name':9 'need':82,1164 'never':1054 'note':913 'obj':1250 'obj.owner':1252 'object':46,97,429,566,756,769,771,837,865,1126,1241,1245 'object-level':96 'one':65,106,585 'oper':411,632,827 'org':277,286,668,671,678,859,1321 'organ':353,398,1214,1216 'organization/tenant':272 'orm':29,533 'other':442,449 'output':1402 'overrid':256 'owner':350,394,436,624,1210,1280,1290 'ownership':265,269,273,344,392,557,738,817,834,1267,1318 'ownershipmixin':244 'param':513 'parent':589,1370 'pass/fail':1302 'path':509 'path/to/file.py':1084 'pattern':128,133,141,846,1190,1193 'perform':1275 'permiss':45,47,221,227,237,240,301,305,562,729,731,757,828,838,861,866,939,947,968,1118,1127,1229,1233,1235,1242,1246,1256,1423 'permissiondeni':1037,1266 'phase':194,368,457,683,877 'philosophi':130 'pick':688 'pk':701,814,815,1011,1029,1107 'potenti':794 'predefin':139 'proceed':361,970 'py':308,326,340 'python':993,1020,1205,1231,1269 'python/django':33 'queri':30,248,260,512,534,548,609,819,1203 'queryset':255,338,742,745,774,805,840,852,1113 'question':66,153,206,460,473,603,1093 'rais':971,1036,1265 'read':1150 'relat':641 'relev':1133 'report':170,879,882,902,923,1060,1377 'request':408,517,629,823,1248,1283 'request.data':1286,1288 'request.user':855,1211,1224,1253,1264 'request.user.organization':1217 'requir':226,228,304,306,1257,1421 'research':214 'resourc':280,383,416,488,503,642,664,996,997,1008,1012,1023,1024,1027,1030,1041 'resource.objects.get':1010,1028 'resource.owner':1033 'retriev':762 'return':423,616,747,749,776,806,975,1009,1040,1114,1251 'review':4,8,13,21,24,54,84,124,1065,1178,1186,1298,1414 'right':1048 'risk':94 'rn':300,319,333,349 'role':282 'role-bas':281 'safe':845,1273 'safeti':1424 'say':935,1051 'scan':137 'scope':249,549,635,843,871,1204,1206,1212,1395 'secur':20 'self':746,1247,1277 'self.request.user':1281 'sensit':826,1336 'serial':1278 'serializer.save':1279,1285 'server':626,1271 'server-sid':1270 'set':259,434,622 'sever':1081 'show':1142 'side':1272 'singl':267 'skill':1387 'skill-django-access-review' 'snippet':1141 'someon':660 'source-sickn33' 'specif':155,189,380,686,1409 'stop':1415 'substitut':1405 'success':1427 'suggest':924,957,992,1019,1055,1155 'surfac':373 'system':507 'task':102,1391 'team':278 'tenant':48,354,1322 'tenant/org':663 'tenantmanag':252 'tenantmiddlewar':231 'tenantmixin':245 'tenantscopedviewset':236 'test':1411 'theoret':918 'titl':1080 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'trace':159,493,685,693,793,895,1091,1104,1340 'treat':1400 'trigger':38 'typic':297,1309 'unauthor':979,981 'understand':144,187,196,364,1305 'updat':437 'url':406,508,598,682,708 'use':22,80,527,764,772,1218,1293,1385 'user':36,68,74,107,114,268,279,351,379,388,396,440,447,468,477,485,552,612,637,666,783,811,857,966,1003,1026,1095,1098,1148,1208,1223,1320,1332 'user-specif':378 'user.id':1035 'userscopedmanag':253 'valid':938,946,964,1410 've':174,885,1315,1325,1339,1364,1375,1380 'verif':916,1166 'verifi':848,1131,1365 'via':403 'view':26,315,322,705,1249 'viewset':28 'viewsets.modelviewset':720 'vs':288 'vulner':58,140 'want':118 'whether':105 'within':284 'without':810,816 'work':147","prices":[{"id":"72769a0f-aa03-4278-bb33-4b4cc7d0e4fa","listingId":"98f81047-ae94-402e-a369-09fbf64192a4","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:36:11.317Z"}],"sources":[{"listingId":"98f81047-ae94-402e-a369-09fbf64192a4","source":"github","sourceId":"sickn33/antigravity-awesome-skills/django-access-review","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/django-access-review","isPrimary":false,"firstSeenAt":"2026-04-18T21:36:11.317Z","lastSeenAt":"2026-04-24T06:51:04.850Z"}],"details":{"listingId":"98f81047-ae94-402e-a369-09fbf64192a4","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"django-access-review","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34831,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-24T06:41:17Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"5e0d4c1a17bc00a519e206e89b6d9bf977e98645","skill_md_path":"skills/django-access-review/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/django-access-review"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"django-access-review","description":"django-access-review"},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/django-access-review"},"updatedAt":"2026-04-24T06:51:04.850Z"}}