GHSA-GJ48-438W-JH9V

Vulnerability from github – Published: 2026-06-16 14:07 – Updated: 2026-06-16 14:07
VLAI
Summary
Bleach clean() / Cleaner() fails to sanitize dangerous URI schemes in allowed formaction attributes
Details

Summary

Bleach clean() / Cleaner() fails to sanitize dangerous URI schemes in allowed formaction attributes.

Bleach applies URI protocol sanitization only to attributes listed in attr_val_is_uri. While URI-bearing attributes such as action, href, src, and poster are included in that set, formaction is not. As a result, if a downstream application explicitly allows formaction on submit-capable controls in untrusted HTML, Bleach preserves dangerous values such as javascript:alert(1) instead of stripping them.

This can lead to submit-triggered JavaScript execution in applications that rely on Bleach to sanitize untrusted HTML and allow the relevant tag/attribute combination.


Details

The issue appears to be a URI-sanitization coverage gap in Bleach’s sanitizer logic.

Relevant code paths:

  • bleach/sanitizer.pyBleachSanitizerFilter.allow_token (around line 553)
  • bleach/_vendor/html5lib/filters/sanitizer.pyattr_val_is_uri (around line 525)

In BleachSanitizerFilter.allow_token, URI protocol sanitization is only applied when:

```python id="pft79m" if namespaced_name in self.attr_val_is_uri:


However, `(None, 'formaction')` is currently missing from `attr_val_is_uri`.

This creates an inconsistency where `action` is protocol-sanitized, but `formaction` is not.

As a result, if a downstream application allows:

* tags such as `<button>` or `<input>`
* the `formaction` attribute

then Bleach preserves dangerous URI schemes such as `javascript:` in `formaction`.

Examples of affected submit-capable controls include:

* `<button>` (default submit behavior unless `type="button"` is set)
* `<input type="submit">`
* `<input type="image">`

This appears to be a real library-side sanitizer gap rather than only an application misuse issue, because Bleach already treats similar URI-bearing attributes (such as `action`) as protocol-sensitive and sanitizes them.

Suggested minimal fix:

Add:

```python id="4v4fkn"
(None, 'formaction')

to attr_val_is_uri in:

  • bleach/_vendor/html5lib/filters/sanitizer.py

I also prepared a minimal patch and focused regression tests if helpful.


PoC

Below are minimal reproductions using bleach.clean().

1) <button>

```python id="d3g0v7" from bleach import clean

print(clean( ' go', tags={'form', 'button'}, attributes={'button': ['formaction']}, ))


**Actual output:**

```html id="i4nd7s"
<form><button formaction="javascript:alert(1)">go</button></form>

Expected output:

```html id="g4d2r1"

go

---

#### 2) `<input type="submit">`

```python id="l4dy0j"
print(clean(
    '<form><input type="submit" formaction="javascript:alert(1)" value="go"></form>',
    tags={'form', 'input'},
    attributes={'input': ['type', 'formaction', 'value']},
))

Actual output:

```html id="h8lgbt"


**Expected output:**

```html id="6y8mws"
<form><input type="submit" value="go"></form>

3) <input type="image">

```python id="g8q0x8" print(clean( ' ', tags={'form', 'input'}, attributes={'input': ['type', 'formaction', 'src']}, ))


**Actual output:**

```html id="fd22kg"
<form><input type="image" formaction="javascript:alert(1)" src="/foo.png"></form>

Expected output:

```html id="z6t6je"

```


Impact

This is a client-side HTML sanitization bypass / dangerous URI preservation issue.

If an application relies on Bleach to sanitize untrusted HTML and explicitly allows:

  • formaction
  • and submit-capable controls such as <button> or <input>

then Bleach can emit sanitized output that still contains a dangerous javascript: URI in formaction.

That can lead to submit-triggered JavaScript execution when the user activates the control.

Impact is limited to configurations that explicitly allow the relevant tag/attribute combination, but the issue is still security-relevant because:

  • formaction is a real browser sink
  • Bleach already protocol-sanitizes similar URI-bearing attributes like action
  • the omission creates inconsistent sanitizer coverage for dangerous URI schemes

I would currently assess this as Medium severity.

If useful, I also have:

  • a minimal patch
  • focused regression tests for:

  • <button formaction="javascript:...">

  • <input type="submit" formaction="javascript:...">
  • <input type="image" formaction="javascript:...">
  • a safe control case where formaction="/submit" is preserved
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "bleach"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.4.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-16T14:07:49Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nBleach `clean()` / `Cleaner()` fails to sanitize dangerous URI schemes in allowed `formaction` attributes.\n\nBleach applies URI protocol sanitization only to attributes listed in `attr_val_is_uri`. While URI-bearing attributes such as `action`, `href`, `src`, and `poster` are included in that set, `formaction` is not. As a result, if a downstream application explicitly allows `formaction` on submit-capable controls in untrusted HTML, Bleach preserves dangerous values such as `javascript:alert(1)` instead of stripping them.\n\nThis can lead to **submit-triggered JavaScript execution** in applications that rely on Bleach to sanitize untrusted HTML and allow the relevant tag/attribute combination.\n\n---\n\n### Details\n\nThe issue appears to be a URI-sanitization coverage gap in Bleach\u2019s sanitizer logic.\n\nRelevant code paths:\n\n* `bleach/sanitizer.py` \u2014 `BleachSanitizerFilter.allow_token` (around line 553)\n* `bleach/_vendor/html5lib/filters/sanitizer.py` \u2014 `attr_val_is_uri` (around line 525)\n\nIn `BleachSanitizerFilter.allow_token`, URI protocol sanitization is only applied when:\n\n```python id=\"pft79m\"\nif namespaced_name in self.attr_val_is_uri:\n```\n\nHowever, `(None, \u0027formaction\u0027)` is currently missing from `attr_val_is_uri`.\n\nThis creates an inconsistency where `action` is protocol-sanitized, but `formaction` is not.\n\nAs a result, if a downstream application allows:\n\n* tags such as `\u003cbutton\u003e` or `\u003cinput\u003e`\n* the `formaction` attribute\n\nthen Bleach preserves dangerous URI schemes such as `javascript:` in `formaction`.\n\nExamples of affected submit-capable controls include:\n\n* `\u003cbutton\u003e` (default submit behavior unless `type=\"button\"` is set)\n* `\u003cinput type=\"submit\"\u003e`\n* `\u003cinput type=\"image\"\u003e`\n\nThis appears to be a real library-side sanitizer gap rather than only an application misuse issue, because Bleach already treats similar URI-bearing attributes (such as `action`) as protocol-sensitive and sanitizes them.\n\nSuggested minimal fix:\n\nAdd:\n\n```python id=\"4v4fkn\"\n(None, \u0027formaction\u0027)\n```\n\nto `attr_val_is_uri` in:\n\n* `bleach/_vendor/html5lib/filters/sanitizer.py`\n\nI also prepared a minimal patch and focused regression tests if helpful.\n\n---\n\n### PoC\n\nBelow are minimal reproductions using `bleach.clean()`.\n\n#### 1) `\u003cbutton\u003e`\n\n```python id=\"d3g0v7\"\nfrom bleach import clean\n\nprint(clean(\n    \u0027\u003cform\u003e\u003cbutton formaction=\"javascript:alert(1)\"\u003ego\u003c/button\u003e\u003c/form\u003e\u0027,\n    tags={\u0027form\u0027, \u0027button\u0027},\n    attributes={\u0027button\u0027: [\u0027formaction\u0027]},\n))\n```\n\n**Actual output:**\n\n```html id=\"i4nd7s\"\n\u003cform\u003e\u003cbutton formaction=\"javascript:alert(1)\"\u003ego\u003c/button\u003e\u003c/form\u003e\n```\n\n**Expected output:**\n\n```html id=\"g4d2r1\"\n\u003cform\u003e\u003cbutton\u003ego\u003c/button\u003e\u003c/form\u003e\n```\n\n---\n\n#### 2) `\u003cinput type=\"submit\"\u003e`\n\n```python id=\"l4dy0j\"\nprint(clean(\n    \u0027\u003cform\u003e\u003cinput type=\"submit\" formaction=\"javascript:alert(1)\" value=\"go\"\u003e\u003c/form\u003e\u0027,\n    tags={\u0027form\u0027, \u0027input\u0027},\n    attributes={\u0027input\u0027: [\u0027type\u0027, \u0027formaction\u0027, \u0027value\u0027]},\n))\n```\n\n**Actual output:**\n\n```html id=\"h8lgbt\"\n\u003cform\u003e\u003cinput type=\"submit\" formaction=\"javascript:alert(1)\" value=\"go\"\u003e\u003c/form\u003e\n```\n\n**Expected output:**\n\n```html id=\"6y8mws\"\n\u003cform\u003e\u003cinput type=\"submit\" value=\"go\"\u003e\u003c/form\u003e\n```\n\n---\n\n#### 3) `\u003cinput type=\"image\"\u003e`\n\n```python id=\"g8q0x8\"\nprint(clean(\n    \u0027\u003cform\u003e\u003cinput type=\"image\" formaction=\"javascript:alert(1)\" src=\"/foo.png\"\u003e\u003c/form\u003e\u0027,\n    tags={\u0027form\u0027, \u0027input\u0027},\n    attributes={\u0027input\u0027: [\u0027type\u0027, \u0027formaction\u0027, \u0027src\u0027]},\n))\n```\n\n**Actual output:**\n\n```html id=\"fd22kg\"\n\u003cform\u003e\u003cinput type=\"image\" formaction=\"javascript:alert(1)\" src=\"/foo.png\"\u003e\u003c/form\u003e\n```\n\n**Expected output:**\n\n```html id=\"z6t6je\"\n\u003cform\u003e\u003cinput type=\"image\" src=\"/foo.png\"\u003e\u003c/form\u003e\n```\n\n---\n\n### Impact\n\nThis is a **client-side HTML sanitization bypass / dangerous URI preservation issue**.\n\nIf an application relies on Bleach to sanitize untrusted HTML and explicitly allows:\n\n* `formaction`\n* and submit-capable controls such as `\u003cbutton\u003e` or `\u003cinput\u003e`\n\nthen Bleach can emit sanitized output that still contains a dangerous `javascript:` URI in `formaction`.\n\nThat can lead to **submit-triggered JavaScript execution** when the user activates the control.\n\nImpact is limited to configurations that explicitly allow the relevant tag/attribute combination, but the issue is still security-relevant because:\n\n* `formaction` is a real browser sink\n* Bleach already protocol-sanitizes similar URI-bearing attributes like `action`\n* the omission creates inconsistent sanitizer coverage for dangerous URI schemes\n\nI would currently assess this as **Medium severity**.\n\nIf useful, I also have:\n\n* a minimal patch\n* focused regression tests for:\n\n  * `\u003cbutton formaction=\"javascript:...\"\u003e`\n  * `\u003cinput type=\"submit\" formaction=\"javascript:...\"\u003e`\n  * `\u003cinput type=\"image\" formaction=\"javascript:...\"\u003e`\n  * a safe control case where `formaction=\"/submit\"` is preserved",
  "id": "GHSA-gj48-438w-jh9v",
  "modified": "2026-06-16T14:07:49Z",
  "published": "2026-06-16T14:07:49Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/mozilla/bleach/security/advisories/GHSA-gj48-438w-jh9v"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/mozilla/bleach"
    },
    {
      "type": "WEB",
      "url": "https://github.com/mozilla/bleach/releases/tag/v6.4.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Bleach clean() / Cleaner() fails to sanitize dangerous URI schemes in allowed formaction attributes"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

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…