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 auth detection — auth declared in
views.pyis not detected when scanningurls.py(tracked in the roadmap) include('app.urls')prefixes — not yet reconciled (tracked in issue #1)- Method detection — Django
path()doesn't bind to a single HTTP method. Without inspecting the view class, routes store method as'GET'as a default. DRF ViewSets get all 6 standard methods. - Class-based view methods — not yet detected (would need to inspect
def get,def poston the view class)