
Iniezione di codice (RCE) in datamodel-code-generator tramite customBasePath non convalidato (CVE-2026-63720)
Gravità: Alta, CVSS 3.1 7.5 / CVSS 4.0 7.5 (assegnata da VulnCheck, il CNA)
Tetto ambientale (distribuzione come servizio di rete): fino a 9.8
Vettore (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
Vettore (v3.1): CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Versioni interessate: datamodel-code-generator < 0.70.0
Corretta in: 0.70.0
CWE: CWE-94 (Improper Control of Generation of Code, 'Code Injection')
Segnalata da: Rahul Karne
CNA: VulnCheck
Pubblicata: 26 luglio 2026
datamodel-code-generator ha convalidato ogni stringa di import controllata dallo schema che potesse trasportare un payload di iniezione di codice, tranne una.
Lo strumento converte uno schema di input (JSON Schema, OpenAPI, YAML) in codice sorgente di modelli Python. Diversi campi dello schema vengono renderizzati direttamente in quel codice generato, quindi il progetto li convalida come identificatori Python con notazione a punti prima dell'uso, proprio per prevenire l'iniezione. Il campo di estensione dello schema customBasePath è l'unico campo correlato che salta questo controllo. Il suo valore confluisce non sanificato in un'istruzione from ... import ... nell'output generato. Un attaccante che controlla lo schema di input può incorporare Python arbitrario usando nuove righe e un'espressione senza punti, e questo viene eseguito nel momento in cui il modulo generato viene importato, che è il normale passo successivo dopo la generazione dei modelli.
Questa è una correzione incompleta di CVE-2026-55415 (GHSA-5578-w22f-pfx9), che ha indurito i campi correlati customTypePath e x-python-import contro questa esatta classe di attacco. Tale correzione non copriva customBasePath, che raggiunge lo stesso identico sink senza convalida ed è rimasto sfruttabile fino alla 0.68.1 e su main fino alla 0.70.0.
Esecuzione di codice Python arbitrario nel processo che importa o esegue i modelli generati: la macchina dello sviluppatore, un runner CI o qualsiasi servizio che genera e poi carica i modelli. Riservatezza, integrità e disponibilità dell'host sono completamente compromesse, limitate solo dai privilegi di quel processo.
La gravità dipende interamente da dove la generazione del codice viene eseguita su input non attendibili:
Workflow locale dello sviluppatore. Uno sviluppatore genera modelli da uno schema di cui non è autore (un documento OpenAPI scaricato o di terze parti) e importa il risultato. Il codice viene eseguito con i privilegi dello sviluppatore. Questo è il caso su cui si basa il punteggio assegnato.
Pipeline CI / di build. Una pipeline genera modelli da specifiche di terze parti ed esegue i test. Il codice viene eseguito sul runner CI con le credenziali di cui dispone.
Servizio di rete (tetto ambientale, fino a 9.8). Un servizio che accetta uno schema via HTTP, genera modelli e li carica, ad esempio una piattaforma B2B che genera automaticamente SDK da specifiche OpenAPI fornite dal cliente, esegue il codice dell'attaccante sul server a partire da una singola richiesta non autenticata e senza interazione dell'utente. Questa è la distribuzione che l'advisory correlato dello stesso mantainer (GHSA-m34r) indica come in scope.
Chi è interessato: qualsiasi utilizzo di datamodel-code-generator < 0.70.0 che (1) generi modelli da uno schema il cui valore customBasePath è influenzato dall'attaccante e (2) importi o esegua il modulo generato. Il flusso di lavoro predefinito genera-e-importa soddisfa (2) intrinsecamente.
Chi non è interessato:
0.70.0 o successive, dove customBasePath viene convalidato.| Metrica | Valore | Fonte |
|---|---|---|
| Download, complessivi | 194 milioni | pepy.tech/projects/datamodel-code-generator |
| Download, ultimi 30 giorni | 16,3 milioni | pepy.tech |
| Distribuzione tipica | Macchine degli sviluppatori, pipeline CI/CD e piattaforme di generazione di SDK che generano codice da OpenAPI / JSON Schema | intrinseco alla funzione dello strumento |
Il valore del campo customBasePath dello schema viene introdotto nel codice generato senza alcun vincolo di identificatore. Tre punti della base di codice sono rilevanti (percorsi relativi a src/datamodel_code_generator/):
parser/jsonschema.py definisce il campo custom_base_path con alias="customBasePath" (~riga 644), consumato tramite _resolve_base_class(...) in diversi punti di chiamata.parser/base.py, _resolve_base_class (~riga 1665), restituisce il valore dopo solo una normalize() locale (deduplicazione/rimozione spazi). Nessuna convalida dell'identificatore viene applicata.imports.py, Import.from_full_path() (~riga 35), emette il valore così com'è come riga from ... import .... Il valore viene anche usato come classe base in model/base.py set_base_class (~riga 1324) e renderizzato grezzo dal template del modello (class {{ class_name }}({{ base_class }}):).Poiché il valore viene scritto nel sorgente Python senza alcun vincolo, le nuove righe incorporate e un'espressione senza punti sopravvivono nell'output come proprie righe separate e singolarmente analizzabili, e la riga centrale viene eseguita all'importazione.
Il payload è privo di punti per necessità. Import.from_full_path divide il valore sul ., quindi una normale chiamata os.system(...) verrebbe spezzata. Usare getattr(__import__('os'),'system')(...) evita qualsiasi . risolvendo comunque la stessa chiamata, e le nuove righe circostanti mantengono sintatticamente valide le righe from ... import ... emesse, così la riga centrale iniettata viene eseguita correttamente.
Non è un progetto che ha trascurato l'iniezione. Il mantainer ha indurito questa esatta classe di attacco ripetutamente in molteplici advisory (GHSA-5578, m34r, 8m8r, wjv6), instradando ogni volta una stringa di import o di tipo controllata dallo schema attraverso _validate_dotted_python_identifier_path prima che raggiunga la generazione del codice. I campi correlati customTypePath (convalidato in parser/jsonschema.py ~righe 4956, 5202) e x-python-import (~riga 2096) passano entrambi attraverso quel validatore.
customBasePath è l'unico campo correlato senza tale chiamata. Raggiunge lo stesso sink Import.from_full_path attraverso un percorso diverso (_resolve_base_class) che non è mai stato collegato alla convalida ricevuta dagli altri campi. Il difetto è sopravvissuto proprio perché la difesa circostante sembrava completa: un revisore che cerca stringhe di import non convalidate vede i validatori sui campi che controlla per primi, e questo campo passa attraverso un helper che sembra risolvere le classi base piuttosto che gestire gli import. È una lacuna in una correzione sistematica, non una correzione assente, ed è per questo che è persistito fino all'ultima release.
Un attaccante ha bisogno di:
< 0.70.0.customBasePath in uno schema che l'obiettivo elaborerà, in pratica fornendo o influenzando lo schema di input (un documento OpenAPI/JSON Schema di terze parti o uno schema inviato a un servizio).