Skip to content

fix(api): sanitize the OpenAPI schema download filename so a crafted ?version= cannot 500 - #16656

Open
Maffooch wants to merge 1 commit into
devfrom
cmm/friendly-edison-xmf8k2
Open

Maffooch wants to merge 1 commit into
devfrom
cmm/friendly-edison-xmf8k2

Conversation

@Maffooch

@Maffooch Maffooch commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Description

Refs DefectDojo-Inc/dojo-pro#6013

The bug

GET/HEAD /api/v2/oa3/schema/?format=json&version=<CR/LF payload> returns a 500:

django.http.response.BadHeaderError: Header values can't contain newlines
  drf_spectacular/views.py _get_schema_response -> Content-Disposition built from the ?version= query parameter

drf-spectacular interpolates the version value into the Content-Disposition
filename without sanitizing it. version is attacker-controlled (the ?version=
query parameter; no versioning class is configured, so the raw value flows through),
and Django refuses to set a header value containing CR/LF — so building the response
raises BadHeaderError.

The header injection itself never succeeds (Django's rejection is exactly what
stops it), but the unhandled exception surfaces as an unauthenticated 500 on a public
endpoint, and each scanner probe generates an error report. It is a crash/noise bug,
not a successful injection.

The fix

Override DojoSpectacularAPIView._get_filename to strip CR, LF and the double-quote
delimiter from the filename before it reaches the header, so the Content-Disposition
header is always well formed whatever ?version= (or any DRF versioning scheme)
produces. A legitimate ?version=v2 is unaffected (the schema is served normally; the
filename just carries v2).

Tests

unittests/test_apiv2_oa3_schema.py:

  • a CR/LF ?version= now returns 200 with no CR/LF in Content-Disposition and no
    injected header (fails on dev — the request raises BadHeaderError);
  • a normal ?version=v2 is still served.

Impact on DefectDojo Pro

Pro serves the same endpoint through SerializedSpectacularAPIView, which subclasses
DojoSpectacularAPIView and only overrides get() (to hold a generation lock),
delegating to super(). It does not override _get_filename, so it inherits this fix
with no Pro-side change — the Pro /api/<schema>/schema/ endpoints are covered too.

Verification note

Prepared in a cloud session without a Docker daemon, so the containerised test suite
was not run locally; CI runs it. The crash and the fix were confirmed in an isolated
reproduction: feeding the raw version into a Django response header raises the exact
BadHeaderError: Header values can't contain newlines, and the sanitized filename sets
the header cleanly with no CR/LF. ruff check (pinned 0.16.10, the repo config) passes
on both changed files.


Generated by Claude Code

…= cannot 500

GET/HEAD /api/v2/oa3/schema/?version=<CR/LF> returned 500 with
django.http.response.BadHeaderError. drf-spectacular interpolates the
attacker-controlled ?version= query parameter into the Content-Disposition
filename unsanitized, and Django refuses header values containing CR/LF. The
header injection never actually succeeds, but each scanner probe produced an
unhandled 500 and a diagnostics error report.

Override DojoSpectacularAPIView._get_filename to strip CR, LF and the quote
delimiter from the filename, so the response header is always well formed
whatever ?version= contains. The Pro SerializedSpectacularAPIView subclass
inherits this with no extra change. Add a regression test.

Refs DefectDojo-Inc/dojo-pro#6013

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013r7wUwFigkbymmti9TLSmL
@Maffooch
Maffooch requested a review from blakeaowens as a code owner November 2, 2026 00:28
@Maffooch Maffooch added this to the 3.5.100 milestone Nov 2, 2026 — with Claude
@dryrunsecurity

dryrunsecurity Bot commented Nov 2, 2026

Copy link
Copy Markdown

DryRun Security

This pull request modifies a sensitive codepath in dojo/api_v2/views.py by an author ('claude') who is not on the allowed list. The finding is classified as low severity and is not blocking.

Configured Sensitive Codepath Modified by Non-Allowed Author in dojo/api_v2/views.py (drs_1cdfe390)
Vulnerability Configured Sensitive Codepath Modified by Non-Allowed Author
Description File 'dojo/api_v2/views.py' matches configured sensitive codepath pattern 'dojo/api_v2/*.py' and was modified by 'claude' (commit cfebf13) who is not in the allowed authors list.

Comment to provide feedback on these findings.

Report false positive: @dryrunsecurity fp [FINDING ID] [FEEDBACK]
Report low-impact: @dryrunsecurity nit [FINDING ID] [FEEDBACK]

Example: @dryrunsecurity fp drs_90eda195 This code is not user-facing

All finding details can be found in the DryRun Security Dashboard.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants