CodeVetter · Repo Unpack · Risk map
django/django
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.
- 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.
- 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.
- 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.
- 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.
- 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).
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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).