12 de agosto de 2026 8 min de leitura

Erros comuns de regex para CPF/CNPJ que passam no teste unitário e quebram em produção

Infográfico com exemplos de regex de CPF que têm falta de âncora, formato só com pontuação ou chave sobressalente — passam no teste unitário, mas quebram em produção — ao lado de um gráfico comparando taxa de acerto entre CPF formatado, sem máscara, CNPJ alfanumérico e entradas com lixo, e um checklist de casos de teste
Figura — regex com falta de âncora, formato só com pontuação ou chave sobressalente passam no teste unitário, mas quebram em produção.

Toda base de código acumula, mais cedo ou mais tarde, uma regex para reconhecer CPF ou CNPJ. Ela costuma nascer rápido — alguém copia um padrão de um fórum, ajusta os dígitos, roda contra dois ou três exemplos que tem à mão, os testes passam, o commit sobe. O problema é que uma regex escrita assim tende a acertar exatamente os casos que o desenvolvedor pensou em testar e falhar silenciosamente em todos os outros, porque regex descreve forma, não valida significado, e a diferença entre as duas coisas raramente aparece nos poucos exemplos usados para escrever o teste.

A regex que só reconhece o formato que o autor imaginou

O erro mais comum é escrever a expressão pensando em uma única forma de entrada — normalmente a que aparece formatada com pontuação, como 123.456.789-09 — e esquecer que o mesmo campo pode chegar sem máscara, colado de uma planilha ou digitado por um usuário que ignora os separadores. Uma regex como /^\d{3}\.\d{3}\.\d{3}-\d{2}$/ rejeita de forma correta um CPF mal formatado, mas também rejeita um CPF perfeitamente válido que só não veio com pontuação — e o time descobre isso quando um usuário real reclama, não quando o teste unitário roda, porque o teste unitário só usou o exemplo formatado.

A correção mais robusta normaliza a entrada antes de validar — remove tudo que não é dígito (ou letra, no caso do CNPJ alfanumérico) e só então aplica a regex sobre o resultado — em vez de tentar prever, na própria expressão, todas as formas de pontuação que um usuário pode digitar.

O CNPJ alfanumérico quebra regex antigas sem avisar

Regex escritas antes da migração da Receita Federal para o CNPJ alfanumérico costumam assumir que as 12 primeiras posições são sempre numéricas, algo como /^\d{14}$/. Esse padrão nunca vai lançar um erro chamativo — ele simplesmente vai rejeitar, de forma silenciosa e educada, todo CNPJ real que contenha uma letra, tratando um documento válido como se fosse malformado. É o tipo de bug que passa despercebido em homologação porque a massa de teste da equipe também é inteiramente numérica, e só aparece quando o primeiro CNPJ alfanumérico real bate na API em produção.

Vale reforçar: mesmo no formato novo, os dois últimos dígitos verificadores continuam sempre numéricos — só as 12 primeiras posições podem conter letras. Uma regex atualizada precisa refletir exatamente essa assimetria, não liberar letras em todas as 14 posições.

Expressões sem âncora aceitam lixo nas pontas

Esquecer ^ e $ — ou seus equivalentes de início e fim de string — é um erro sutil porque a maioria dos exemplos de teste usados para validar a regex são strings limpas, sem nada antes ou depois do CPF. Sem essas âncoras, uma regex como /\d{3}\.\d{3}\.\d{3}-\d{2}/ encontra o padrão em qualquer lugar da string, inclusive dentro de um texto maior — o que significa que uma entrada como DROP TABLE users; --123.456.789-09 passa na validação de "contém um CPF", quando o campo deveria conter exclusivamente um CPF e nada mais. Esse tipo de brecha é particularmente perigoso quando o valor validado por regex depois alimenta uma query SQL ou é interpolado em algum comando sem passar por sanitização adicional.

O bug do lastIndex em JavaScript

Em JavaScript, reutilizar a mesma regex com a flag global (g) chamando .test() várias vezes é um clássico gerador de comportamento intermitente. Uma regex global mantém estado entre chamadas através da propriedade lastIndex, então a mesma instância de regex pode retornar true na primeira chamada e false na segunda, para exatamente o mesmo CPF, dependendo de onde a busca anterior parou. Em um teste unitário isolado, cada teste geralmente recria a regex do zero, então o bug nunca aparece; em produção, onde a mesma instância costuma ser reaproveitada — por exemplo, definida uma única vez no topo de um módulo — o comportamento vira dependente da ordem de validações anteriores. A forma mais segura de evitar isso é não usar a flag g em regex usada só para checagem booleana, ou recriar a expressão a cada chamada.

Regex não sabe repetir dígito, nem calcular dígito verificador

Nenhuma regex, por mais elaborada que seja, consegue expressar "os dois últimos dígitos precisam ser o resultado do cálculo de módulo 11 aplicado aos anteriores" — isso é aritmética, não é um padrão de caracteres. Uma regex de CPF bem escrita confirma apenas que a string tem o formato certo: 11 dígitos, ou 11 dígitos com a pontuação no lugar certo. Ela deixa passar, sem nenhum problema, sequências como 111.111.111-11 ou 123.456.789-00, porque ambas têm o formato certo e a regex nunca teve como saber que o dígito verificador está errado. Times que tratam "passou na regex" como sinônimo de "CPF válido" estão, sem perceber, aceitando qualquer sequência de 11 dígitos bem pontuada — a regex é só o primeiro filtro de forma; o cálculo do dígito verificador é uma etapa separada e obrigatória.

Por que o teste unitário não pega nada disso

A explicação é sempre a mesma: os casos de teste escritos junto com a regex tendem a repetir o raciocínio de quem escreveu a regex. Se o autor só pensou em CPF formatado, os testes só cobrem CPF formatado. Se o autor esqueceu do CNPJ alfanumérico, os testes também esqueceram. Regex e seus testes nascem do mesmo ponto cego, então um cobre exatamente as lacunas do outro — o que faz a suíte parecer verde e completa quando na verdade só está validando a própria hipótese inicial, nunca contestada por um caso de fora do que o autor já esperava.

Checklist de casos de teste que expõem esses erros

  • Mesmo documento, com e sem pontuação. O mesmo CPF ou CNPJ formatado e sem máscara deve produzir o mesmo resultado de validação.
  • CNPJ alfanumérico real. Pelo menos um caso com letra nas 12 primeiras posições, confirmando que a regex (e o cálculo do dígito verificador) trata esse formato.
  • Entrada com lixo nas pontas. Uma string com texto antes ou depois do documento, para confirmar que a regex usa âncoras de início e fim.
  • Mesma regex, múltiplas chamadas. Em JavaScript, validar o mesmo valor duas vezes seguidas com a mesma instância de regex global, para expor o bug do lastIndex.
  • Dígito verificador incorreto com formato correto. Um número com 11 (ou 14) posições e pontuação certa, mas dígito verificador errado, para confirmar que a validação vai além da regex.
  • As sequências de dígito repetido. 000.000.000-00 até 999.999.999-99, que passam na regex e no módulo 11, mas precisam ser rejeitadas por regra explícita.

O Validador de CPF e o Validador de CNPJ deste site já combinam a checagem de formato com o cálculo real do dígito verificador — incluindo o formato alfanumérico do CNPJ — então servem como referência rápida para conferir se um caso de teste específico deveria passar ou falhar. Para montar a massa de teste que exercita esse checklist, o Gerador de CPF e o Gerador de CNPJ produzem documentos estruturalmente válidos, com ou sem pontuação, prontos para virar casos de teste automatizados.