fix: zera os alertas de code scanning (Semgrep + regras Flarum-v2)#70
Merged
Conversation
Todo m.trust do bundle passa a sair de um helper único, trustedHtml(), que só recebe HTML já sanitizado (SVG de badge pelo DOMDocument do upload + sanitizeSvg do getBadgeSvg; descrição de tier pelo sanitiseDescription espelhado servidor+cliente). A auditoria fica com um único sink para revisar e os 21 alertas de m.trust nos componentes somem de fato, sem supressão espalhada. No backend, supressões nosemgrep pontuais e justificadas nos falsos positivos: controllers de upload com allowlist de fachada e gate real de finfo fail-closed, Content-Type do download que sai de um match fechado por extensão e DocumentPathResolver que rejeita traversal e ainda valida prefixo, estrutura e regex de filename. O perf-router da bancada de CI ganhou rejeição real de traversal/NUL. Dois alertas casavam texto de comentário e os comentários foram reescritos. Verificação local: semgrep zero, prettier ok, build webpack ok.
| return; | ||
| } | ||
|
|
||
| if ($uri !== '/' && is_file(__DIR__ . '/public' . $uri)) { /* bancada de CI em loopback, caminho ancorado em public/ e traversal rejeitado acima; nosemgrep: php.lang.security.injection.tainted-filename.tainted-filename */ |
| $uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/'; | ||
|
|
||
| if ($uri !== '/' && is_file(__DIR__ . '/public' . $uri)) { | ||
| if (str_contains($uri, '..') || str_contains($uri, "\0")) { /* rejeita traversal antes de tocar o FS; nosemgrep: flarum-v2-path-traversal-naive-filter */ |
| } | ||
|
|
||
| if (str_contains($token, '..')) { | ||
| if (str_contains($token, '..')) { /* rejeita (não remove) e o token ainda passa por prefixo, estrutura fixa e regex de filename; nosemgrep: flarum-v2-path-traversal-naive-filter */ |
| * sink para revisar. | ||
| */ | ||
| export default function trustedHtml(html: string): Mithril.Children { | ||
| return m.trust(html); // nosemgrep: flarum-v2-m-trust, flarum-v2-m-trust-translator |
| $response = (new Response()) | ||
| ->withBody($body) | ||
| ->withHeader('Content-Type', $mime) | ||
| ->withHeader('Content-Type', $mime) /* $mime sai de um match fechado por extensão validada, nunca do cliente; nosemgrep: flarum-v2-client-controlled-mime */ |
| private function validateMime(UploadedFileInterface $file): void | ||
| { | ||
| $clientMime = strtolower((string) $file->getClientMediaType()); | ||
| $clientMime = strtolower((string) $file->getClientMediaType()); /* allowlist de fachada; o gate real é o finfo fail-closed logo abaixo; nosemgrep: flarum-v2-client-controlled-mime */ |
| private function validateMimeTypes(UploadedFileInterface $file): void | ||
| { | ||
| $clientMime = strtolower((string) $file->getClientMediaType()); | ||
| $clientMime = strtolower((string) $file->getClientMediaType()); /* allowlist de fachada; o gate real é o finfo fail-closed logo abaixo; nosemgrep: flarum-v2-client-controlled-mime */ |
Contributor
🔬 Performance benchmarkHome (
|
| Métrica | PR | Main (baseline) | Δ |
|---|---|---|---|
| Performance (score) | 92 | 94 | 🔴 -2pp (-2.1%) |
| FCP | 1304 ms | 1092 ms | 🔴 +212 ms (19.4%) |
| LCP | 1384 ms | 1275 ms | 🔴 +109 ms (8.5%) |
| TBT | 35 ms | 11 ms | 🔴 +24 ms (218.2%) |
| CLS | 0.000 | 0.000 | (=) |
| Speed Index | 1304 ms | 1092 ms | 🔴 +212 ms (19.4%) |
| TTI | 1424 ms | 1292 ms | 🔴 +132 ms (10.2%) |
💡 Recomendações (Lighthouse + dicas Flarum 2 / Marketplace)
- Sem compressão de texto — economia ~600 ms · ~735 KB
Habilite gzip/brotli no servidor (nginx:gzip on; gzip_types text/css application/javascript;oubrotli on; brotli_types ...). Esse é um setting de host, não da extensão, mas reportar aqui ajuda. - JavaScript não utilizado — economia ~360 ms · ~461 KB
Useimport()dinâmico para componentes só usados em rotas específicas (RequestVerificationModal, TiersEditor). O webpack faz split automático se você usarimport("./components/RequestVerificationModal")dentro doroutesao invés de import estático no topo doindex.tsx. - CSS não utilizado — economia ~160 ms · ~226 KB
O bundleforum.csscarrega tudo deless/forum/**. Divida por rota (Home/Perfil) viaExtend\Frontend->css()condicional no JS, ou use PurgeCSS no webpack para o build de produção. - Recursos bloqueando o render — economia ~160 ms
Mova CSS não-crítico para<link rel="preload">ou injete inline o CSS above-the-fold doforum.less. Em Flarum 2, oforum.cssé servido como bloqueante por padrão — consideremedia="print" onloadpara folhas não-críticas (ex.: estilos só do painel admin da extensão). - Cache HTTP curto — 5 resources found
Os assets versionados em/assets/forum-<hash>.jsdeveriam terCache-Control: public, max-age=31536000, immutable. Configure no nginx/apache, não no Flarum.
/all
| Métrica | PR | Main (baseline) | Δ |
|---|---|---|---|
| Performance (score) | 95 | 95 | (=) |
| FCP | 1068 ms | 1061 ms | 🔴 +8 ms (0.7%) |
| LCP | 1246 ms | 1235 ms | 🔴 +11 ms (0.9%) |
| TBT | 0 ms | 0 ms | (=) |
| CLS | 0.000 | 0.000 | (=) |
| Speed Index | 1068 ms | 1061 ms | 🔴 +8 ms (0.7%) |
| TTI | 1247 ms | 1235 ms | 🔴 +11 ms (0.9%) |
💡 Recomendações (Lighthouse + dicas Flarum 2 / Marketplace)
- Sem compressão de texto — economia ~600 ms · ~735 KB
Habilite gzip/brotli no servidor (nginx:gzip on; gzip_types text/css application/javascript;oubrotli on; brotli_types ...). Esse é um setting de host, não da extensão, mas reportar aqui ajuda. - JavaScript não utilizado — economia ~360 ms · ~460 KB
Useimport()dinâmico para componentes só usados em rotas específicas (RequestVerificationModal, TiersEditor). O webpack faz split automático se você usarimport("./components/RequestVerificationModal")dentro doroutesao invés de import estático no topo doindex.tsx. - Recursos bloqueando o render — economia ~200 ms
Mova CSS não-crítico para<link rel="preload">ou injete inline o CSS above-the-fold doforum.less. Em Flarum 2, oforum.cssé servido como bloqueante por padrão — consideremedia="print" onloadpara folhas não-críticas (ex.: estilos só do painel admin da extensão). - CSS não utilizado — economia ~160 ms · ~226 KB
O bundleforum.csscarrega tudo deless/forum/**. Divida por rota (Home/Perfil) viaExtend\Frontend->css()condicional no JS, ou use PurgeCSS no webpack para o build de produção. - Cache HTTP curto — 5 resources found
Os assets versionados em/assets/forum-<hash>.jsdeveriam terCache-Control: public, max-age=31536000, immutable. Configure no nginx/apache, não no Flarum.
/u/admin
| Métrica | PR | Main (baseline) | Δ |
|---|---|---|---|
| Performance (score) | 94 | 94 | (=) |
| FCP | 1103 ms | 1065 ms | 🔴 +37 ms (3.5%) |
| LCP | 1393 ms | 1335 ms | 🔴 +58 ms (4.3%) |
| TBT | 11 ms | 0 ms | 🔴 +11 ms |
| CLS | 0.002 | 0.002 | (=) |
| Speed Index | 1103 ms | 1065 ms | 🔴 +37 ms (3.5%) |
| TTI | 1393 ms | 1335 ms | 🔴 +58 ms (4.3%) |
💡 Recomendações (Lighthouse + dicas Flarum 2 / Marketplace)
- Sem compressão de texto — economia ~600 ms · ~736 KB
Habilite gzip/brotli no servidor (nginx:gzip on; gzip_types text/css application/javascript;oubrotli on; brotli_types ...). Esse é um setting de host, não da extensão, mas reportar aqui ajuda. - JavaScript não utilizado — economia ~360 ms · ~430 KB
Useimport()dinâmico para componentes só usados em rotas específicas (RequestVerificationModal, TiersEditor). O webpack faz split automático se você usarimport("./components/RequestVerificationModal")dentro doroutesao invés de import estático no topo doindex.tsx. - CSS não utilizado — economia ~200 ms · ~225 KB
O bundleforum.csscarrega tudo deless/forum/**. Divida por rota (Home/Perfil) viaExtend\Frontend->css()condicional no JS, ou use PurgeCSS no webpack para o build de produção. - Recursos bloqueando o render — economia ~200 ms
Mova CSS não-crítico para<link rel="preload">ou injete inline o CSS above-the-fold doforum.less. Em Flarum 2, oforum.cssé servido como bloqueante por padrão — consideremedia="print" onloadpara folhas não-críticas (ex.: estilos só do painel admin da extensão). - Cache HTTP curto — 5 resources found
Os assets versionados em/assets/forum-<hash>.jsdeveriam terCache-Control: public, max-age=31536000, immutable. Configure no nginx/apache, não no Flarum.
🛒 Dicas gerais para acelerar a loja (Flarum 2 + Verified)
- Build de produção minificado — confirme que
npm run buildrodou commode: production(já é o caso nojs/package.json). Webpack tree-shakesimport { x } from "flarum/..."se o consumo for explícito. - Split por rota —
index.tsximporta todos os Components no topo. Trocar paraimport()dinâmico nas rotas raramente acessadas (RequestVerificationModal, TiersEditor) reduz oforum.jsinicial. - Less crítico inline — Flarum 2 serve
forum.cssbloqueando. Considere extrair o CSS above-the-fold (header + 1ª linha de ProductCards) e injetar inline viaExtend\Frontend->content(InlineCriticalCss::class). - Imagens de produto — sirva via
<img loading="lazy">em todos os ProductCards que não estiverem no fold inicial, e gere WebP no upload. <link rel="preconnect">— carregue o Stripe.js (js.stripe.com) só na rota de checkout e adicione preconnect lá; se usa CDN para fontes ou imagens S3, adicione preconnects no header.Extend\Frontend->js()rodam síncronos — todo arquivojs/dist/forum.jsé parseado no boot. Cadaextend()noindex.tsxroda antes da primeira pintura.- opcache + view cache — no host:
opcache.enable=1,opcache.validate_timestamps=0em produção, ephp flarum cache:clearno deploy. - HTTP/2 + Brotli no host — o Flarum gera bundles grandes; sem brotli você paga em transferência.
Lighthouse desktop, 1 run por URL. 🟢 = melhorou vs main · 🔴 = regrediu.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O que muda
Zera os 27 alertas abertos de Code scanning do repositório.
Frontend (21 alertas de
m.trust) — em vez de espalhar supressões, todom.trustdo bundle migrou para um helper único,trustedHtml()(js/src/common/utils/trustedHtml.ts), que só recebe HTML já sanitizado: SVG de badge (DOMDocument no upload +sanitizeSvgnogetBadgeSvg) e descrição de tier (sanitiseDescriptionespelhado servidor+cliente). A auditoria passa a ter um único sink para revisar; os alertas nos componentes somem de verdade. Os "erros" dem-trust-translatoreram a regra multilinha casando ternários cujo ramo else é otrans()— desaparecem junto.Backend (5 alertas) — falsos positivos verificados, com supressão
nosemgrepjustificada na linha:finfofail-closed logo abaixo (oUploadedFileMimeexiste exatamente para isso).Content-Typesai de ummatchfechado por extensão validada, nunca do cliente.DocumentPathResolver: rejeita traversal (não faz strip) e ainda valida prefixo, estrutura fixa e regex de filename.Endurecimento real — o
perf-router.phpda bancada de CI agora rejeita URIs com../NUL antes de tocar o filesystem (fecha também o alertatainted-filenamedo ruleset genérico).Verificação local
Nota: o check "Semgrep OSS" do App do semgrep.dev roda fora dos nossos workflows e não aplica triagem
nosemgrep; com a migração para o helper, os achados dele também caem para 1 (o sink documentado).Generated by Claude Code