Detalhes de vulnerabilidade. **Arquivo**: `lib/previous-map.js` **Linha**: 87 - 98 (`carregar o Ficheiro`), 129 - 144 (`LoadMap`) ### Causa Raiz PostCS detecta automaticamente um `/*# fonteMappingURL=... */` comentário dentro do texto CSS que é solicitado para analisar e, a menos que o chamador passe explicitamente `map: false`, tenta carregar esse caminho do disco como um "mapa fonte anterior." Isso acontece em cada `postcss.parse()` / `postcsss().process()` chamada por padrão (opt-out, não opt- in).
` loadMap()` compila o caminho do candidato através de `join(dirname(opts.from), anotação)`, onde `annotação' é a cadeia de texto bruto, controlada pelo atacante, do comentário CSS. `path.join()` normaliza, mas não area segmentos `..', por isso um prefixo `../../././` caminha pelo caminho resolvido fora do diretório pretendido. Se ` opts.from ' não estiver definido, a anotação é usada completamente não modificada — um caminho absoluto no comentário CSS é lido literalmente. 8.5.12 já corrigido uma variante estritamente pior deste (qualquer arquivo, qualquer extensão, poderia ser lida) exigindo que o caminho resolvido terminasse em `.map` (` loadFile()`). Essa correção não endereçou o próprio cruzamento, apenas a extensão do alvo. Desde que a lógica `join( dirname( file), map)` existiu inalterada desde o PostCSS 8.0.0 (Febre 2020 ), qualquer arquivo terminando em `.map` permanece legível através deste caminho na versão atual ( 8.5.16 ).
Uma vez carregado, `MapGenerator.isMap()` trata a mera presença de um "mapa anterior" carregado como um pedido implícito para gerar `result.map`, mesmo quando o chamador nunca definiu a opção `map`. Se o mapa carregado tem um campo `sourcesContent` (comum para mapas emitidos por pacotes/transpilares), esse conteúdo é fundido em `result.map` e devolvido ao chamador — divulgando o conteúdo do arquivo atravessado para quem fornecia o CSS. ### Cenário de ataque 1. Um serviço aceita CSS enviado pelo usuário e o executa através do PostCSS para lint/format/transform, por exemplo `postcss().process(userCss, { from: '/app/uploads/user 123 /input. css', para: '/ app/uploads/ user 123 /output.css'})` — uso idiomático; opção `map` intocada. 2. O atacante envia o CSS contendo `/*# fonteMappingURL=../../././// alguns/outros/app/dist/bundle.js.map */` (ou um caminho absoluto se `from` estiver desativado). 3. O PostCSS lê o arquivo `.map` e dobra seu `sourcesContent` em `result.map`. 4. O serviço faz o que a maioria dos pipelines de construção faz com um verdadeiro `result.map` — escreve- o ao lado da saída CSS ou retorna- o via API (os mapas de fonte são destinados a ser consumidos pelos devtools do navegador, portanto este é comumente público/servido). 5. O atacante recupera o mapa emitido e lê o conteúdo do arquivo atravessado.
### Divulgação de impacto do conteúdo dos arquivos arbitrários `.map` acessíveis através do caminho transversal (ou caminho absoluto quando `from` estiver desativado) do sistema de arquivos do processo. Afecta qualquer aplicativo que processa CSS que não confie totalmente sem passar explicitamente `map: false`. Não é necessária nenhuma autenticação ou interação do usuário além de enviar o texto CSS. ### Código Vulnerável ```js loadFile(path, cssFile, confiável) { se (! confied & & this.unsafeMap.) { se (!/\.map$/i.test(path)) { retornar } } este.root = dirname(path) se (existeSync(path)) { este.mapFile = caminho retornar readFileSync(path, 'utf- 8 '. toString().trim()} }.
loadMap(file, prev) {... } senão se (este.annotation) { deixar mapa = este.annotation se (file) map = join(dirname(file), map) deixar desconhecido = este.loadFile(map, file, false)... } } ```` ### Recomendado Correção Restringe o caminho resolvido para permanecer dentro do próprio diretório do arquivo CSS em vez de confiar apenas em uma verificação de extensão do nome do arquivo: ```js loadFile(path, cssFile, confiável) { se (! confied & & this.unsafeMap) { se (!/\.map$/i.test(path)) { devolver indefinido } se (!cssFile) devolver indefinido deixem root = resolver(dirname(cssFile))) deixarem resolvedoPath = resolver(root, path) se (resolvedPath!== root &&!resolvedPath.startswith(root + sep)) { devolver indefinido } } este.root = dirname(path) se (existeSyncpath) { este.mapFile = caminho retornarFileSync(path), 'utf-f) 8 '. toString().trim() } } ``` Eu implementei, testei (suíte de teste completo existente - 660 / 660 passando, além de novas verificações de regressão baseadas em PoC para ambos os casos transversales e legítimos do mesmo diretório), e pode compartilhar esta correção no pedido ou através de um garfo privado se convidado.
### Verificação Confirmada dinâmicamente em v 8.5.16 (release npm atual / repo HEAD) via um arness standalone Node.js contra `lib/postcss.js`: um arquivo "secreto" `.map` colocado dois diretórios fora de um diretório de projeto simulado foi lido através de um comentário criado `sourceMappingURL` em CSS de outra forma inocuo, com seu `sourcesContent` aparecendo literalmente em `result.map.toString()` — sem opção `map` definida pelo chamador. Um segundo arnês confirmou que o caso mais simples no- `from` lê diretamente um caminho absoluto. Um terceiro arnês confirmou `map: false` é a única solução atual. O ramo de correção anexado fecha ambos os vetores enquanto mantém todos 660 a unidade existente testa verde. Registro de aconselhamento: GHSA-r 28 c- 9 q 8 g- f 849. Identificadores relacionados: CVE- 2026 - 73646.
Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 24 T 16: 24: 55.000 Z e lista a sua última modificação como 2026 - 08 - 13 T 17: 55: 54.000 Z.
Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N.
Software afetado e informações de versão: npm package postcss — ECOSISTEM: introduzido 0, corrigido 8.5.18.
Classificação e evidência: identificadores de fraqueza CWE- 22. O registro contém 4 suporte de referências nestes tipos: WEB, PACKAGE.