VYPR
High severity8.8OSV Advisory· Published Nov 7, 2025· Updated Apr 15, 2026

CVE-2025-64184

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.

PackageAffected versionsPatched versions
dosagePyPI
< 3.23.2

Affected products

1

Patches

1
336a96841916

Make file extension guessing more robust

https://github.com/webcomics/dosageTobias GruetzmacherOct 31, 2025via ghsa
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

4

News mentions

0

No linked articles in our index yet.