Todos os artigos
74 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Tutoriais

Regex não basta para validar CNPJ alfanumérico

Atualizar a regex para aceitar letras não valida CNPJ alfanumérico. Sem calcular os dígitos verificadores, CNPJs inválidos entram no seu banco silenciosamente.

COVER · Tutoriais

Tem um ticket fechado em alguma sprint recente que diz algo como "atualizar validação de CNPJ para suportar letras". O dev que resolveu mudou \d por [A-Z0-9] na regex, os testes passaram, e o PR foi aprovado. O problema é que esse ticket resolveu metade do problema — a metade mais fácil.

A regex verifica formato. O dígito verificador verifica validade. São duas coisas diferentes, e confundir uma com a outra vai colocar dados sujos em produção de um jeito que você só descobre meses depois.


O que a regex verifica — e o que ela não verifica

Uma regex como esta faz uma coisa útil:

/^[A-Z0-9]{2}\.[A-Z0-9]{3}\.[A-Z0-9]{3}\/[A-Z0-9]{4}-\d{2}$/i

Ela garante que o input tem 14 caracteres na posição certa, com os separadores corretos, e que as posições 1–12 aceitam letras maiúsculas e dígitos. Isso elimina entradas claramente erradas — "não é um CNPJ" passa a ser detectável.

O que ela não faz: verificar se os dois últimos caracteres (os dígitos verificadores) são matematicamente corretos para aquele conjunto de 12 caracteres que vieram antes.

O dígito verificador de um CNPJ não é arbitrário. Dado uma raiz (B3QK72A1) e uma ordem (0001), existe exatamente um par de verificadores que torna o CNPJ válido. B3.QK7.2A1/0001-89 pode ser válido; B3.QK7.2A1/0001-12 tem o mesmo formato mas é matematicamente inválido. A regex não distingue os dois.


Quantos strings aleatórias passam no regex?

Vamos ser concretos. Para uma raiz+ordem alfanumérica de 12 posições, há 36^12 combinações possíveis (36 = 10 dígitos + 26 letras). Cada combinação tem exatamente um par de dígitos verificadores válido — os outros 99 pares são matematicamente inválidos.

Com regex apenas: todas as 36^12 × 100 combinações passam (qualquer \d{2} no final está certo).
Com módulo 11: apenas as 36^12 × 1 combinações com DV correto passam.

Em termos práticos: 1 em cada 100 CNPJs aleatórios bem formatados é matematicamente válido. Os outros 99 passam na regex e falham na validação real.

Isso não é hipotético. Qualquer formulário que aceite CNPJ e não valide o DV vai acumular entradas inválidas. Usuário digitou errado, copiou com um dígito trocado, testou com dado fictício — tudo vai entrar.


O que acontece quando isso chega em produção

Dados sujos de CNPJ têm consequências concretas.

Integrações fiscais rejeitam o CNPJ. A SEFAZ valida o dígito verificador na emissão de NF-e. Se o CNPJ do tomador ou emitente for inválido, a nota é rejeitada com erro 226 ("CNPJ do destinatário inválido"). O processo de venda trava em runtime, não em desenvolvimento.

Consultas à Receita Federal retornam erro. Sistemas que consultam o CNPJ para preencher dados cadastrais automaticamente vão falhar silenciosamente — ou pior, vão tratar o erro como "CNPJ não encontrado" e continuar.

Auditoria depois é impossível. Se você tem 50 mil CNPJs no banco, como saber quantos são válidos? Você precisaria recalcular o DV de cada um. Prevenir é mais barato que remediar.


Como o módulo 11 filtra o que a regex não filtra

O algoritmo de dígito verificador do CNPJ usa módulo 11 com pesos específicos. A intuição é simples: dado os 12 primeiros caracteres (raiz + ordem), você calcula uma soma ponderada, aplica módulo 11, e o resultado determina os dois DVs.

Uma troca de um único caractere na raiz ou ordem altera a soma — e portanto altera o DV esperado. Se o DV no input não bate com o calculado, o CNPJ é inválido.

Exemplo concreto: B3.QK7.2A1/0001-89 e B3.QK7.2A1/0001-88 diferem apenas no último dígito. A regex aceita os dois. O módulo 11 aceita apenas um (ou nenhum, dependendo dos caracteres exatos).

Isso é o que o dígito verificador foi projetado para fazer: detectar erros de digitação e entradas fabricadas.


A sequência correta de validação

Regex e módulo 11 não são alternativas — são etapas sequenciais. A regex filtra o que é claramente inválido; o módulo 11 filtra o que parece válido mas não é.

function validarCNPJ(input: string): boolean {
  // 1. Normalizar
  const s = input.replace(/[.\-\/]/g, '').toUpperCase();

  // 2. Comprimento
  if (s.length !== 14) return false;

  // 3. Formato: posições 1-12 alfanuméricas, 13-14 numéricas
  if (!/^[A-Z0-9]{12}\d{2}$/.test(s)) return false;

  // 4. Rejeitar sequências homogêneas de dígitos (ex: 00000000000000)
  if (/^\d+$/.test(s) && new Set(s).size === 1) return false;

  // 5. Calcular e verificar os DVs — essa é a validação real
  const val = (c: string) => c >= 'A' ? c.charCodeAt(0) - 55 : +c;

  const w1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
  const soma1 = w1.reduce((acc, w, i) => acc + val(s[i]) * w, 0);
  const dv1 = soma1 % 11 < 2 ? 0 : 11 - soma1 % 11;

  const w2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
  const soma2 = w2.reduce((acc, w, i) => acc + val(s[i]) * w, 0);
  const dv2 = soma2 % 11 < 2 ? 0 : 11 - soma2 % 11;

  return val(s[12]) === dv1 && val(s[13]) === dv2;
}

O passo 3 é a regex. O passo 5 é o módulo 11. Um sem o outro está incompleto.

Note a conversão de caractere para valor: letras A-Z usam charCodeAt(0) - 55, que mapeia A=10, B=11, ..., Z=35. Dígitos mantêm seu valor numérico. Esse mapeamento é o que a Receita Federal definiu para o novo formato.


Libs que já fazem as duas etapas

Se você não quer implementar o algoritmo manualmente, há bibliotecas npm que já suportam o formato alfanumérico:

  • validador-cnpj-alfanumerico — validação e geração, suporte completo ao novo formato
  • A função validateCNPJ do pacote cnpj-cpf-validator — verifique a versão; as antigas não suportam letras

Antes de usar qualquer lib, teste com um CNPJ alfanumérico válido conhecido. Se a função retornar false para um CNPJ gerado corretamente, a lib não foi atualizada.


Nota: o conteúdo editorial acabou aqui. O que vem abaixo é uma indicação de ferramenta relacionada ao tema do post.


Ferramenta relacionada

Para montar fixtures de teste com CNPJs alfanuméricos matematicamente válidos, o Gerador de CNPJ já calcula os dígitos verificadores no novo formato — útil para ter entradas conhecidas na sua suite antes de subir a validação para produção.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil