Django scanner
shadowaudit's Django scanner reads urls.py files and extracts route definitions from path(), re_path(), url(), and router.register() calls.
How it works
The scanner finds all urls.py and *_urls.py files in the scanned directory and uses regex to extract route patterns. It converts Django's <type:param> syntax to OpenAPI's {param} syntax for comparison.
Detected patterns
Pattern 1 — path() function
from django.urls import path
from . import views
urlpatterns = [
path('users/', views.UserList),
path('users/<int:pk>/', views.UserDetail),
]
Django path parameters are converted to OpenAPI syntax:
| Django | OpenAPI (stored) |
|---|---|
<int:pk> | {pk} |
<str:name> | {name} |
<slug:slug> | {slug} |
<uuid:id> | {id} |
Pattern 2 — re_path() / url() with regex
from django.urls import re_path
urlpatterns = [
re_path(r'^api/items/$', views.ItemList),
]
# → GET /api/items/ (regex anchors stripped)
Pattern 3 — DRF router.register()
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet
router = DefaultRouter()
router.register(r'articles', ArticleViewSet)
This generates 6 standard REST routes automatically:
GET /articles/ — list
POST /articles/ — create
GET /articles/{pk}/ — retrieve
PUT /articles/{pk}/ — update
PATCH /articles/{pk}/ — partial_update
DELETE /articles/{pk}/ — destroy
Example scan output
shadowaudit --dir ./src --spec openapi.json --framework django
[INFO] Running Django route scanner...
[✓] Found 12 Django routes in ./src
[INFO] GET /articles/ → urls.py:7 [no auth]
[INFO] POST /articles/ → urls.py:7 [no auth]
[INFO] GET /articles/{pk}/ → urls.py:7 [no auth]
[INFO] GET /health/ → urls.py:10 [no auth]
[INFO] GET /api/users/ → urls.py:12 [auth]
Auth detection
shadowaudit detects Django auth by scanning the urls.py file content for hardcoded patterns:
@login_required @permission_required IsAuthenticated IsAdminUser
TokenAuthentication SessionAuthentication permission_classes authentication_classes
LoginRequiredMixin PermissionRequiredMixin jwt_required requires_auth
Known limitations
- Cross-file CBV detection — class-based views defined in
views.pyand registered inurls.pyuse the v0.3.1 cross-file auth detection path. Same-file CBV detection (class +router.register()/.as_view()in same file) is fully supported. include('app.urls')prefixes — reconciled since v0.3.1 ✅- Class-based view methods — fully supported since v0.6.0 ✅ (ModelViewSet, APIView, DRF generics)
- Cross-file auth detection — supported since v0.3.1 ✅ (scans
views.pyforpermission_classes,@login_required, etc.)
Class-based views (v0.6.0+)
shadowaudit detects Django REST Framework class-based views:
ModelViewSet
class UserViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
router = DefaultRouter()
router.register(r'users', UserViewSet)
Generates all 6 CRUD routes: GET/POST /users/, GET/PUT/PATCH/DELETE /users/{pk}/
APIView
class UserListView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request): pass
def post(self, request): pass
urlpatterns = [path('users/', UserListView.as_view())]
Methods detected from def get, def post, etc. in the class body.
DRF generic views
| Class | Methods |
|---|---|
ListAPIView | GET |
CreateAPIView | POST |
ListCreateAPIView | GET + POST |
RetrieveAPIView | GET |
UpdateAPIView | PUT + PATCH |
DestroyAPIView | DELETE |
RetrieveUpdateDestroyAPIView | GET + PUT + PATCH + DELETE |
Auth detection for CBVs
permission_classes = [IsAuthenticated]→hasAuth: truepermission_classes = [AllowAny]→hasAuth: falseauthentication_classes = [...]→hasAuth: true- No
permission_classes→hasAuth: false