CVE-2025-64184
Description
Dosage is a comic strip downloader and archiver. When downloading comic images in versions 3.1 and below, Dosage constructs target file names from different aspects of the remote comic (page URL, image URL, page content, etc.). While the basename is properly stripped of directory-traversing characters, the file extension is taken from the HTTP Content-Type header. This allows a remote attacker (or a Man-in-the-Middle, if the comic is served over HTTP) to write arbitrary files outside the target directory (if additional conditions are met). This issue is fixed in version 3.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
dosagePyPI | < 3.2 | 3.2 |
Affected products
1Patches
1336a96841916Make file extension guessing more robust
1 file changed · +8 −8
dosagelib/comic.py+8 −8 modified@@ -8,6 +8,7 @@ import contextlib import glob import logging +import mimetypes import os from datetime import datetime from typing import Iterator @@ -59,6 +60,8 @@ def __init__(self, scraper, url, referrer, filename, text=None) -> None: self.url = url filename = getFilename(filename) self.filename, self.ext = os.path.splitext(filename) + if not self.ext: + self.ext = '.bin' self.text = text def connect(self, lastchange=None): @@ -75,18 +78,15 @@ def connect(self, lastchange=None): content_type = unquote(self.urlobj.headers.get( 'content-type', 'application/octet-stream')) content_type = content_type.split(';', 1)[0] - if '/' in content_type: - maintype, subtype = content_type.split('/', 1) - else: - maintype = content_type - subtype = None + maintype = content_type.split('/', 1)[0] if maintype != 'image' and content_type not in ( 'application/octet-stream', 'application/x-shockwave-flash'): raise IOError('content type %r is not an image at %s' % ( content_type, self.url)) - # Always use mime type for file extension if it is sane. - if maintype == 'image': - self.ext = '.' + subtype.replace('jpeg', 'jpg') + # Try to guess "better" extension from mime type + guessed_ext = mimetypes.guess_extension(content_type) + if guessed_ext and guessed_ext != '.bin': + self.ext = guessed_ext self.contentLength = int(self.urlobj.headers.get('content-length', 0)) logger.debug('... filename = %r, ext = %r, contentLength = %d', self.filename, self.ext, self.contentLength)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.