
Injeção de código (RCE) no datamodel-code-generator via customBasePath não validado (CVE-2026-63720)
Gravidade: Alta, CVSS 3.1 7.5 / CVSS 4.0 7.5 (atribuída pela VulnCheck, a CNA)
Teto ambiental (implantação como serviço de rede): até 9.8
Vetor (v4.0): CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Vetor (v3.1): CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Afetados: datamodel-code-generator < 0.70.0
Corrigido em: 0.70.0
CWE: CWE-94 (Controle Impróprio da Geração de Código, 'Injeção de Código')
Relatado por: Rahul Karne
CNA: VulnCheck
Publicado: 26 de julho de 2026
O datamodel-code-generator validou todas as strings de importação controladas pelo schema que poderiam carregar um payload de injeção de código, exceto uma.
A ferramenta converte um schema de entrada (JSON Schema, OpenAPI, YAML) em código-fonte de modelos Python. Vários campos do schema são renderizados diretamente nesse código gerado, então o projeto os valida como identificadores Python pontilhados antes do uso, especificamente para prevenir injeção. O campo de extensão do schema customBasePath é o único irmão que pula essa verificação. Seu valor flui sem sanitização para uma instrução from ... import ... na saída gerada. Um atacante que controla o schema de entrada pode embutir Python arbitrário usando quebras de linha e uma expressão sem pontos, e ele é executado no momento em que o módulo gerado é importado, que é o passo seguinte habitual após gerar os modelos.
Esta é uma correção incompleta da CVE-2026-55415 (GHSA-5578-w22f-pfx9), que endureceu os campos irmãos customTypePath e x-python-import contra exatamente essa classe. Essa correção não cobriu o customBasePath, que atinge o mesmo sink sem validação e permaneceu explorável até 0.68.1 e na main até 0.70.0.
Execução arbitrária de código Python no processo que importa ou executa os modelos gerados: a máquina do desenvolvedor, um runner de CI ou qualquer serviço que gera e depois carrega modelos. A confidencialidade, a integridade e a disponibilidade do host são totalmente comprometidas, limitadas apenas pelos privilégios desse processo.
A gravidade depende inteiramente de onde o codegen roda sobre entrada não confiável:
Quem é afetado: Qualquer uso do datamodel-code-generator < 0.70.0 que (1) gera modelos a partir de um schema cujo valor de customBasePath é influenciado pelo atacante e (2) importa ou executa o módulo gerado. O fluxo padrão de gerar-e-depois-importar satisfaz (2) inerentemente.
Quem não é afetado:
customBasePath é validado.| Métrica | Valor | Fonte |
|---|---|---|
| Downloads, todos os tempos | 194 milhões | pepy.tech/projects/datamodel-code-generator |
| Downloads, últimos 30 dias | 16.3 milhões | pepy.tech |
| Implantação típica | Máquinas de desenvolvedores, pipelines de CI/CD e plataformas de geração de SDKs que fazem codegen a partir de OpenAPI / JSON Schema | inerente à função da ferramenta |
O valor do campo de schema customBasePath é levado para o código gerado sem nenhuma restrição de identificador. Três pontos do codebase importam (caminhos relativos a src/datamodel_code_generator/):
parser/jsonschema.py define o campo custom_base_path com alias="customBasePath" (~linha 644), consumido via _resolve_base_class(...) em vários pontos de chamada.parser/base.py, _resolve_base_class (~linha 1665), retorna o valor após apenas um normalize() local (dedup/trim). Nenhuma validação de identificador é aplicada.imports.py, Import.from_full_path() (~linha 35), emite o valor literalmente como uma linha from ... import .... O valor também é usado como base da classe em model/base.py set_base_class (~linha 1324) e renderizado cru pelo template do modelo (class {{ class_name }}({{ base_class }}):).Como o valor é escrito no código-fonte Python sem nenhuma restrição, quebras de linha embutidas e uma expressão sem pontos sobrevivem na saída como linhas próprias e individualmente analisáveis, e a linha do meio é executada na importação.
O payload é necessariamente sem pontos. Import.from_full_path divide o valor por ., então uma chamada normal os.system(...) seria desmembrada. Usar getattr(__import__('os'),'system')(...) evita qualquer . enquanto ainda resolve a mesma chamada, e as quebras de linha ao redor mantêm as linhas from ... import ... emitidas sintaticamente válidas, de modo que a linha do meio injetada roda sem problemas.
Este não é um projeto que negligenciou injeção. O mantenedor endureceu exatamente essa classe repetidamente em vários advisories (GHSA-5578, m34r, 8m8r, wjv6), sempre encaminhando strings de importação ou de tipo controladas pelo schema por _validate_dotted_python_identifier_path antes de chegarem à geração de código. Os campos irmãos customTypePath (validado em parser/jsonschema.py ~linhas 4956, 5202) e x-python-import (~linha 2096) passam por esse validador.
O customBasePath é o único irmão sem essa chamada. Ele atinge o mesmo sink Import.from_full_path por um caminho diferente (_resolve_base_class) que nunca foi ligado à validação que os outros campos receberam. O defeito sobreviveu justamente porque a defesa ao redor parecia completa: um revisor que procura strings de importação não validadas vê validadores nos campos que verifica primeiro, e este aqui passa por um helper que parece resolução de classe base, não tratamento de importação. É uma lacuna em uma correção sistemática, não uma ausência dela, e é por isso que persistiu até a versão mais recente.
Um atacante precisa de:
< 0.70.0.customBasePath em um schema que o alvo processará, na prática fornecendo ou influenciando o schema de entrada (um documento OpenAPI/JSON Schema de terceiros, ou um schema enviado a um serviço).Nenhuma autenticação ou privilégios elevados são exigidos do atacante (PR:N). A pontuação base reflete que a vítima executa a ação comum de gerar-e-importar (UI:R na v3.1 / UI:A na v4.0); a implantação como serviço de rede remove até isso, e é daí que vem o 9.8 ambiental.
O seguinte foi executado contra o pacote real, sem modificações. Reprodução: