/ TL;DR

Dez padrões inseguros que Copilot sugere em React por padrão, com exemplos de código e correções. XSS, JWT, race conditions e mais.

O Copilot aprende com bilhões de linhas de código público. Muito desse código é ruim. E React tem um problema específico: sua superfície de ataque é enorme (DOM, storage, estado, efeitos), e boa parte da comunidade escreve exemplos didáticos sem preocupação de produção. Resultado: quando você aceita a sugestão sem revisar, você importa dívida de segurança pronta.

Aqui estão dez padrões que aparecem em quase toda base React auditada em 2026. Correções vêm junto.

1. dangerouslySetInnerHTML sem sanitização

Sugestão típica:

function Comment({ raw }) {
  return <div dangerouslySetInnerHTML={{ __html: raw }} />;
}

O nome já grita, mas Copilot sugere assim mesmo. Qualquer string de terceiros vira XSS.

Correção:

import DOMPurify from 'dompurify';

function Comment({ raw }) {
  const clean = DOMPurify.sanitize(raw, { ALLOWED_TAGS: ['b','i','a','p'] });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

2. useEffect com fetch e race condition

useEffect(() => {
  fetch(`/api/users/${id}`).then(r => r.json()).then(setUser);
}, [id]);

Se id muda antes da primeira resposta chegar, você renderiza o usuário errado. Em contexto de multi-tenant, isso vira vazamento cross-conta.

useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/users/${id}`, { signal: controller.signal })
    .then(r => r.json())
    .then(setUser)
    .catch(e => { if (e.name !== 'AbortError') throw e; });
  return () => controller.abort();
}, [id]);

3. JWT no localStorage

Sugestão clássica:

localStorage.setItem('token', response.data.token);

Qualquer XSS lê o token. Padrão certo: cookie HttpOnly; Secure; SameSite=Strict emitido pelo backend, sem access via JavaScript. Se você precisa saber "está logado" no client, use um cookie separado não-HttpOnly com um boolean.

4. Variáveis de ambiente vazando no bundle

const apiKey = process.env.REACT_APP_STRIPE_SECRET;

Toda variável com prefixo REACT_APP_ ou NEXT_PUBLIC_ vai para o bundle e é lida por qualquer visitante. Copilot completa nomes sem julgamento. Segredos ficam no backend — sempre.

Regra prática: se o nome termina em _SECRET, _KEY, _TOKEN, deve estar em API route, não em componente.

5. useState guardando dado sensível que não deveria persistir

const [creditCard, setCreditCard] = useState('');

Estado React vai para memória do browser, aparece em React DevTools, e — pior — em replay tools (LogRocket, FullStory) por padrão. PAN, CVV, CPF em produção precisam de opt-out explícito nessas ferramentas ou não passar por state.

Use refs não reativos para dados transitórios que não devem renderizar:

const cardRef = useRef();
// enviar direto para gateway sem passar por state

6. Formulários uncontrolled sem validação server-side

Copilot ama:

<form onSubmit={handleSubmit}>
  <input ref={emailRef} />
</form>

O que ele esquece: validação. handleSubmit acaba mandando pro backend sem checar formato, tamanho, tipo. E o backend, sendo também gerado por IA, confia porque "o frontend valida". Nenhum dos dois valida.

Regra: valide em ambos. Frontend para UX, backend para segurança. Trate frontend como se fosse um cliente hostil.

7. Manipulação direta do DOM em componente controlado

useEffect(() => {
  document.getElementById('name').innerHTML = user.name;
}, [user]);

Duas falhas: (1) contorna a virtualização do React, (2) innerHTML com dado de usuário = XSS. Isso aparece quando o Copilot mistura tutorial jQuery com React.

8. Rotas protegidas apenas no client

function AdminRoute({ children }) {
  const { user } = useAuth();
  if (!user?.isAdmin) return <Navigate to="/login" />;
  return children;
}

Isso é UX, não segurança. Um atacante que carrega o bundle vê todos os componentes admin e as rotas de API que eles chamam. A proteção real é no backend, checando o token em cada endpoint.

9. CSP inexistente ou permissiva demais

Copilot raramente sugere Content-Security-Policy. Quando sugere, é copiada de tutorial:

Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval'

Isso é o mesmo que não ter CSP. Baseline mínima para React em 2026:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{random}';
  style-src 'self' 'nonce-{random}';
  img-src 'self' data: https:;
  connect-src 'self' https://api.seudominio.com;
  frame-ancestors 'none';
  base-uri 'self';

unsafe-inline mata a proteção contra XSS. unsafe-eval habilita bibliotecas antigas. Elimine ambos.

10. useEffect com dependência de objeto/função inline

useEffect(() => {
  loadData({ filter: 'active' });
}, [{ filter: 'active' }]);

Objeto inline é sempre novo. O efeito roda infinitamente. Não é vulnerabilidade clássica, mas DoS auto-infligido: browser trava, retry chain faz backend cair. Em SaaS multi-tenant, um cliente pode derrubar o serviço inteiro. Use useMemo ou tire da dependência.

Por que Copilot sugere isso

Não é maldade. É estatística: código público no GitHub tem essas falhas em abundância, e o modelo replica o que viu mais vezes. Autocomplete é média, não mediana da segurança.

Isso conecta com o padrão maior descrito em 12 vulnerabilidades de vibe coding: IA gera código plausível, revisor humano confia porque "compila e passa no teste". O gap é justamente segurança, que raramente está no test suite.

O que fazer na prática

  • Linter customizado: adicione regras eslint-plugin-security e no-dangerously-set-inner-html sem sanitizer.
  • Code review com foco em IA: quando o PR diz "generated with Copilot", olhe com 2x atenção.
  • SAST no CI: Semgrep, CodeQL, Snyk Code.
  • Varredura dinâmica: SAST não pega runtime issues (JWT no storage, CSP fraca, race condition). Precisa de DAST contínuo.

A Varredura testa continuamente sua aplicação React contra XSS, JWT mal implementado, CSP fraca e outras 40+ classes de vulnerabilidades OWASP — sem tocar em código-fonte. Rode agora.

EV

Equipe Varredura

Time de pesquisa de segurança · Varredura

Sua app em produção passa por essa análise?

Rode uma varredura autônoma em ~60 segundos. Sem cadastro.

Analisar minha aplicação →