# Risk map — django/django

> Canonical page: https://codevetter.com/unpack/django/risk-map

# Risk map — django/django

Scanned commit dd6f6b1531984823e3dc56740dfa93f3ceb09357 (2026-09-20). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The sharp edges are secret handling, host and CSRF checks, password-hasher compatibility, shell-outs, and a few APIs that look asynchronous or durable but are not. Deployment checks for SECRET_KEY do not run on ordinary manage.py check. The generated project is explicitly unsuitable for production (DEBUG True). Schema and request code have hard incompatibilities: mixed migration directions, ATOMIC_REQUESTS with async views, and admin registration of composite primary keys.
- startproject writes SECRET_KEY into settings and sets DEBUG = True with ALLOWED_HOSTS = []. The template comments say both are unsuitable for production. check_secret_key (security.W009) only runs as a deploy=True check and flags length < 50, fewer than 5 unique characters, or the django-insecure- prefix. — [django/conf/project_template/project_name/settings.py-tpl#L19-28](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/project_template/project_name/settings.py-tpl#L19-L28) [django/core/checks/security/base.py#L22-29](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/checks/security/base.py#L22-L29) [django/core/checks/security/base.py#L255-271](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/checks/security/base.py#L255-L271)
- HttpRequest.get_host allows any *.localhost, 127.0.0.1, and [::1] when DEBUG is true and ALLOWED_HOSTS is empty. SECURE_PROXY_SSL_HEADER is None by default and global_settings warns that setting it without a trusted proxy allows scheme spoofing. — [django/http/request.py#L190-210](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/http/request.py#L190-L210) [django/http/request.py#L314-327](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/http/request.py#L314-L327) [django/conf/global_settings.py#L466-473](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L466-L473)
- CSRF_COOKIE_HTTPONLY defaults to False so client script can read csrftoken. CSRF_TRUSTED_ORIGINS defaults to empty. A missing SessionMiddleware with CSRF_USE_SESSIONS raises ImproperlyConfigured from _get_secret. — [django/conf/global_settings.py#L575-585](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L575-L585) [django/middleware/csrf.py#L221-237](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/middleware/csrf.py#L221-L237)
- MD5PasswordHasher (algorithm md5) is still implemented and documented as not recommended, but it is not in the default PASSWORD_HASHERS list. identify_hasher still accepts 32-character unsalted MD5 and sha1$$ legacy hashes and will call get_hasher for those algorithms if they remain configured. — [django/contrib/auth/hashers.py#L172-191](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/contrib/auth/hashers.py#L172-L191) [django/contrib/auth/hashers.py#L656-679](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/contrib/auth/hashers.py#L656-L679) [django/conf/global_settings.py#L548-556](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L548-L556)
- get_git_changeset runs a fixed git log command via subprocess.run(..., shell=True) with cwd two directories above django/utils/version.py, and uses stdout as a version timestamp. The command string is not user input, but shell=True is still a process boundary. — [django/utils/version.py#L221-247](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/utils/version.py#L221-L247)
- dbshell reaches the database client through BaseDatabaseClient.runshell, which merges the backend env over os.environ and calls subprocess.run(args, env=env, check=True) without shell=True. The executable name is backend-defined (psql and similar). — [django/db/backends/base/client.py#L1-31](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/db/backends/base/client.py#L1-L31)
- SafeExceptionReporterFilter.hidden_settings redacts keys matching API, AUTH, TOKEN, KEY, SECRET, PASS, SIGNATURE, and HTTP_COOKIE, plus the session cookie name, before the debug exception reporter shows settings. DEFAULT_EXCEPTION_REPORTER_FILTER points at this class. Debug pages are still a risk if DEBUG is left on. — [django/views/debug.py#L112-145](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/views/debug.py#L112-L145) [django/conf/global_settings.py#L607-613](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L607-L613)
- ImmediateBackend executes task functions in the calling process during enqueue, stores tracebacks on failure, and does not persist results beyond the returned TaskResult. Treating settings.TASKS['default'] as a durable queue will drop work when the process exits. — [django/tasks/backends/immediate.py#L16-80](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/tasks/backends/immediate.py#L16-L80) [django/conf/global_settings.py#L689-692](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L689-L692)
- Email has two configuration paths during the Django 6.x cycle. EMAIL_BACKEND and related EMAIL_* settings are marked RemovedInDjango2028Warning, get_connection() warns, and defining MAILERS together with any deprecated email setting raises ImproperlyConfigured. — [django/conf/__init__.py#L42-70](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/__init__.py#L42-L70) [django/core/mail/__init__.py#L71-100](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/mail/__init__.py#L71-L100) [django/conf/global_settings.py#L190-199](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L190-L199)
- make_view_atomic wraps a view in transaction.atomic for every alias with ATOMIC_REQUESTS unless the view lists that alias in _non_atomic_requests. If the view is a coroutine function it raises RuntimeError instead of wrapping. — [django/core/handlers/base.py#L347-356](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/handlers/base.py#L347-L356)
- AdminSite.register raises ImproperlyConfigured for abstract models and for models with _meta.is_composite_pk, and AlreadyRegistered if the model is already in the registry. Swapped models are silently not registered. — [django/contrib/admin/sites.py#L112-149](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/contrib/admin/sites.py#L112-L149)
- Apps.populate raises RuntimeError if called reentrantly while loading, which happens if AppConfig.ready() or a model import triggers setup again. get_commands is functools.cache, so tests that mutate INSTALLED_APPS without clearing that cache see a stale command map. — [django/apps/registry.py#L72-84](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/apps/registry.py#L72-L84) [django/core/management/__init__.py#L52-80](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/management/__init__.py#L52-L80)
- runserver is a development server (help text) bound to loopback by default. SecurityMiddleware does not redirect to HTTPS unless SECURE_SSL_REDIRECT is true, and SECURE_HSTS_SECONDS defaults to 0 so HSTS is off. Those production headers are not applied by the generated project unless the deployer changes settings. — [django/core/management/commands/runserver.py#L27-81](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/management/commands/runserver.py#L27-L81) [django/middleware/security.py#L21-44](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/middleware/security.py#L21-L44) [django/conf/global_settings.py#L668-676](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L668-L676)
- Request body size is capped twice: LimitedStream refuses reads past CONTENT_LENGTH, and DATA_UPLOAD_MAX_MEMORY_SIZE defaults to 2.5 MB. ASGIRequest additionally aborts body reads after body_receive_timeout of 60 seconds (RequestAborted / RequestDataTooBig are imported by the ASGI handler). — [django/core/handlers/wsgi.py#L15-40](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/handlers/wsgi.py#L15-L40) [django/conf/global_settings.py#L318-326](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/conf/global_settings.py#L318-L326) [django/core/handlers/asgi.py#L11-13](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/handlers/asgi.py#L11-L13) [django/core/handlers/asgi.py#L45-47](https://github.com/django/django/blob/dd6f6b1531984823e3dc56740dfa93f3ceb09357/django/core/handlers/asgi.py#L45-L47)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
