# `lxml_html_clean.Cleaner` não tira URLs `javascript:` a partir de atributos URL espaçados (`xlink:href`) **Reporter:** Guillem Lefait · **Dada:** 2026 - 05 - 10 ** Afectado:** ` lxml` ≤ 6.1.0 e `lxml_html_clean` ≤ 0.4.4 (último estável) ** Confirmado contra:** lxml 6.1.0 + lxml_html_limpe 0.4.4 no Python 3.13.5, 3.14.4, e 3.15.0 a 8 (libxml 2 2.14.6 / 2.9.14 — o erro está na lógica do desinfectador de Python puro, independente do libxml 2 backend) **Cause de causa de rodas:** igual a CVE- 2021 - 28957 (`formação` faltando a `link_attrs`) "Cleaner` filtra os esquemas de URL (`javascript:`, `vbscript:`,...) por meio de links ambulantes através de `rewrite_links()`, que delega para `iterlinks()`, que só produz atributos nomeados em `lxml.html.defs.link_attrs`. Essa lista de permissões não contém nomes preffixados (`xlink:href`) e nenhum `srcset`. Como resultado, quando `Cleaner` for configurado com `safe_attrs_only=False` — uma opção documentada para os que querem o manejo de atributos lentos mas ainda esperam o esfregamento do sistema URL — ` ` sobrevive à desinfectação intocada, e qualquer navegador que siga a especificação SVG- anchor executará o JavaScript quando o link render for clicado. **CWE:** CWE- 79 (XSS), com CWE- 184 (Lista incompleta de entradas desativadas) como classe de defeito subjacente.
Componentes afetados. Pacote Versão testada Arquivo / linha Arquivo / linha 4.9.x, 5.2.1, 6.1.0. ` src/ lxml/html/ defs.py: 20 `............................................................................................................................... 485 - 528 `............................................................................................................................... 0.4.0 – 0.4.4. `lxml_html_ clean/ clean.py: 348,576 ` | O módulo legado `lxml.html.clean` — agrupado em `lxml < 5.2.0 ` e ainda instalável em versões mais recentes via o extra `lxml[html_clean]` — compartilha o mesmo erro. `defs.link_attrs` é um conjunto de strings plano; o 'xlink:href` literal está ausente. ``'python # lxml/html/defs.py link_attrs = congeladoset([ 'ação', 'arquivo', 'background', 'cite', 'classid', 'codebase', 'dados', 'href', 'longdesc', 'profil', 'src', 'usemap', 'dynsrc', 'lowsrc', 'formação', ]) `` `HtmlMixin.iterlinks()` (`lxml/html/__init__.py: 526 - 528 `) apenas dá atributos cuja chave está no conjunto:.
```python para o atrib no link_attrs: se o atrib em atribs: rendimento (el, atribs, atribs[attrib], 0 ) ``` `Cleaner.__call__` registra o filtro do sistema URL através de `rewrite_links` (`lxml_html_clean/clean.py: 348 "), que é um envoltório fino em torno de `iterlinks()'. Porque `xlink:href` nunca é cedido, `_ remove_javascript_link` (` clean.py: 576 `) nunca é invocado para ele. ## Reprodutor mínimo. ````python from lxml import html from lxml_html_clean importar Limpador.
para carga útil em ( x ", ' y ', ): arbor = html.fromstring(payload) Limpeza(safe_attrs_only=False(tree) print(html.tostring(tree).decode()) print(' iterlinks:', list(html.fromstring(payload).iterlinks()) # x ← inalterado # iterlinks: [] ← link rewriter cego # y ← inalterado # iterlinks: [] ← link rewriter cego ``` Tanto os escopos SVG como MathML são vulneráveis — o mesmo lacuna de listas permitidas, ambos renderizam âncoras que os navegadores tratam como navegáveis. Outras variantes sobreviventes confirmadas pelo laboratório (melhor escopo, codificação de esquemas diferentes): caixa mista (` JaVaScRiPt:`), entidade HTML (`java&#x 73;cript:`), aba incorporada (`java\tscript:`). Um chamador que usa o `Cleaner` para neutralizar HTML não confiável e escolhe `safe_attrs_only=False` — normalmente porque o aplicativo quer permitir atributos personalizados de dados/aria/vendor — irá passar silenciosamente `javascript:` cargas úteis carregadas em `xlink:href` até os renders das vítimas. Armazenado XSS em qualquer aplicativo que viajou HTML fornecido pelo usuário através desta configuração. A Alcance é condicionada à opção `safe_attrs_only=False`, mas essa é uma opção pública documentada; os consumidores esperam razoavelmente que o lavagem do sistema URL seja independente do atributo allow- listing.
**Extenda `link_attrs`** para incluir `xlink:href`. No modo HTML, `lxml.html` mantém os nomes de atributos prefixos literalmente — a chave analisada é a string literal `xlink:href`, não um formulário de anotação Clark — então a busca existente por lista de atributos é uma correspondência de string simples. Mismo formato que o CVE- 2021 - 28957 corrigir: ``` diff # lxml/html/defs.py link_attrs = congeladoset([ 'ação', 'arquivo', 'background', 'cite', 'classid', 'codebase', 'dados', 'href', 'longdesc', 'profil', 'src', 'usemap', 'dynsrc', 'lowsrc', 'formação', + 'xlink:href', ]) ```` Esta única mudança fecha o XSS relatado para ambos os SVG ` ` e MathML ` `. `lxml_html_clean` é a casa canônica do código `Cleaner` ( 881 linhas); `lxml.html.clean` é um 21 - linha de background-compat shim (` from lxml_html_clean import *`) que capta a correção automaticamente uma vez que `link_attrs` é atualizado a montante. Uma vez que a mudança a montante requer a ação do mantenedor lxml, veja a alternativa abaixo se for preferido um patch auto- contido em `lxml_html_clean`.
**Alternativa (fixação no pacote, nenhuma coordenação lxml necessária):** adicione um passeio de atributo de nomes espaçados- URL dentro de `Cleaner.__call__` para que o filtro de esquema de URL não dependa de `link_attrs'. Esboço: ```python # lxml_html_clean/clean.py — suplementos rewrite_links() em __call___ _NS_URL_ATTRS = ('xlink:href',) # estender conforme necessário _BAD_SCHEME = re. compile(r'^\s*(javascriptňvbscript (')data):', re.I) para o el no doc.iter(): para o attr no _NS_URL_ATTRS: se o attr no el.attrib e o _BAD_SCHEME.match(el.attrib[attr]): del el.attrib[attr] ```.
Isto desacopla o limpador do conjunto `link_attrs` a montante e combina com o limite de propriedade de segurança estabelecido quando o limpador foi extraído em lxml 5.2.0. ** Defesa em profundidade (opcional, independentemente do caminho de correção que for tomado):** - Também manipular ` srcset`: o valor é um ` url 1 x, url 2 x,...` lista de descritores, então divida em vírgulas e valide cada URL candidato. Não diretamente executável nos navegadores atuais, mas fecha a mesma lacuna. - Também aceite formulários de anotação Clark (`{ 3.org/ 1999 /xlink}href`) para que os chamadores em modo XML usando `lxml.etree` obtenham a mesma proteção. O modo HTML nunca produz este formulário, por isso não é necessário para o erro relatado. CVSS 3.1 pontuação base: ** 8.2 / High** — `AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N` (o XSS armazenado; a vítima deve clicar na âncora SVG; o escopo- alterado porque o script executa na origem de renderização). PR:N reflete o caso comum em que o HTML não confiável entra no desinfectante de fontes anônimas (comentários, tickets de suporte); as implantações que o portão escreve por trás da autenticação podem marcar com PR:L (→ 7.6 ).
A gravidade é **CONDICIONAL** no chamador passando por `safe_attrs_only=False`. Com o padrão de classe (`True`), o atributo permite listar as faixas `xlink:href` antes de a lavagem do esquema correr, e o erro não dispara — verificado no HEAD: configuração padrão `Cleaner()( x )` → ` x `. ## Anterior de arte / novidade. - ** CVE- 2021 - 28957 (lxml) 4.6.3 )** — mesma causa raiz, atributo diferente (`formação`). Fix foi uma extensão de uma linha de `link_attrs`. - **CVE- 2022 - 34473 ** (API do Sanitante Mozilla) — `xlink:href` URL bypass primitive em um desinfectante diferente. - **Bleach (Mozilla, Python)** manipula explicitamente o espaço de nomes `xlink`; `enhrined/svg-sanitize` (PHP) naves `cleanXlinkHrefs()`; DOMPurify sclers `xlink:href` via `ALLOWED_URI_REGEXP`. - **`nh 3 `** (a alternativa recomendada no próprio README de `lxml_html_clean` para uso sensível à segurança) é ** não vulnerável** a este primitivo — verificado 2026 - 05 - 10 no ` nh 3 == 0.3.5 `: com ` `/` `/` ` e `xlink:href` explicitamente adicionados a `tags`/`attributos`, ambas as cargas úteis SVG e MathML, todas as quatro variantes de codificação de esquema, são despojadas (saída por exemplo, ` x `). A apresentação como um GHSA privado em `fedora-python/lxml_html_clean` — `lxml_html_clean` é o mantenedor canônico do código `Cleaner` ( 881 linhas) e a equipe responsável pela segurança desde o lxml 5.2.0 dividir, onde o limpador foi extraído do lxml precisamente para que os relatórios de segurança limpa pudessem aterrissar na equipe certa. O lado lxml não pode ser arquivado via GHSA (` retorna 404 — relatórios privados não estão ativados), por isso um relatório paralelo foi enviado diretamente para o mantenedor lxml para o caminho de patch `defs.link_attrs` a montante. Você pode coordenar diretamente com eles se preferir a correção a montante sobre a alternativa do pacote acima.
Fico feliz em fornecer um parche de rascunho ou RP em qualquer caminho. Não se espera nenhuma recompensa. Registro de aconselhamento: GHSA- 4 jhm- jv 67 - 739 f. Identificadores relacionados: CVE- 2026 - 49825. Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 08 T 20: 23: 22.000 Z e lista a sua última modificação como 2026 - 07 - 08 T 20: 23: 22.000 Z.
Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N. Informações sobre software e versão afetadas: pacote PyPI lxml_html_clean — ECOSYSTEM: introduzido 0, corrigido 0.4.5. Classificação e evidência: identificadores de fraqueza CWE- 184, CWE- 79. O registro contém 2 suporte de referências nestes tipos: WEB, PACKAGE.