Approfondimenti e Trend sull'Intelligenza Artificiale

Package hallucination: come verificare un pacchetto inventato dall’IA

12 min di lettura
Pacco di cartone chiuso lasciato davanti a una porta azzurra tra due muri di mattoni, con un’etichetta fragile sul lato
Windows macOS Linux
Foto: Tima Miroshnichenko · Pexels · Licenza Pexels

L’IA ti propone un pip install o un npm install con un nome mai visto? Tre controlli da terminale per capire se il pacchetto esiste ed è quello giusto.

Nel protocollo di revisione del codice generato dall’IA il quarto passaggio dice di verificare che le dipendenze siano reali. Qui lo trasformi in tre controlli concreti, da fare prima di qualsiasi pip install o npm install che arriva da una chat. Un nome di pacchetto proposto dall’IA è un’ipotesi: diventa una dipendenza solo dopo che hai controllato che esiste, che è quello giusto e chi lo pubblica.

La risposta breve: chiedi al registro se il nome esiste (pip index versions per Python, npm view per JavaScript), confrontalo con la documentazione ufficiale della libreria, poi guarda da quanto tempo esiste, quante versioni ha e chi lo mantiene. Nella prova un nome inventato ha prodotto un errore in meno di un secondo, su PyPI e su npm. Se il nome esiste da pochi giorni con una sola versione, ti fermi e controlli a mano.

ℹ️ Nota / Prerequisito operativo: ti serve un terminale e Python installato. Su Windows usa py (il launcher installato con Python); su macOS e Linux python3. Per i controlli su npm serve Node.js. Nessuno dei comandi di questa pagina installa qualcosa: leggono solo le informazioni pubbliche dei registri.

Perché l’IA inventa nomi di pacchetti

Un modello linguistico non consulta PyPI o npm mentre scrive: produce il nome che gli sembra più probabile, che a volte non esiste. Il fenomeno si chiama package hallucination ed è stato misurato: lo studio We Have a Package for You!, presentato a USENIX Security 2025, ha analizzato 576.000 esempi di codice Python e JavaScript generati da 16 modelli. La quota media di pacchetti inesistenti è risultata di almeno il 5,2% per i modelli commerciali e del 21,7% per quelli open source.

Il nome inventato diventa un rischio quando qualcuno lo registra davvero. In una ricerca pubblicata nel 2024, Bar Lanyado ha notato che i modelli suggerivano pip install huggingface-cli, un pacchetto che non esisteva (quello corretto è huggingface_hub). Lo ha registrato vuoto per prova: in tre mesi ha ricevuto più di 30.000 download, come racconta nella ricerca di Lasso Security. Al suo posto poteva esserci codice malevolo. Questa pratica oggi ha un nome, slopsquatting: registrare in anticipo i nomi inventati dai modelli.

Controllo 1: il nome esiste?

Prima di installare, chiedi al registro se il nome c’è. Il comando pip index versions elenca le versioni disponibili su PyPI senza scaricare nulla.

Windows:

py -m pip index versions super-fast-pdf-cleaner

macOS / Linux:

python3 -m pip index versions super-fast-pdf-cleaner

super-fast-pdf-cleaner è il nome inventato che uso negli esempi di questo sito. Output su Debian 13 con pip 26.2.2:

ERROR: No matching distribution found for super-fast-pdf-cleaner

Con un pacchetto reale la risposta cambia. Per requests la prima riga è requests (2.34.2), seguita dall’elenco di tutte le versioni pubblicate.

Per JavaScript il comando equivalente è npm view, uguale su tutti i sistemi:

npm view super-fast-pdf-cleaner
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/super-fast-pdf-cleaner - Not found

Un ERROR o un E404 qui è una buona notizia: il nome non esiste e non c’è nulla da installare. Il caso pericoloso è il contrario, un nome inventato che qualcuno ha registrato. Per questo servono gli altri due controlli.

⚠️ Attenzione — No matching distribution non significa sempre «non esiste»: pip dà lo stesso errore anche quando il pacchetto esiste ma non ha versioni compatibili con il tuo Python. Prima di concludere, apri https://pypi.org/project/nome-pacchetto/ nel browser: se la pagina risponde con un errore 404, il nome non è registrato.

Controllo 2: esiste, ma è quello giusto?

Una fonte di nomi sbagliati facile da riconoscere è la differenza tra il nome che usi nel codice e il nome con cui il pacchetto si installa. Il codice scrive import yaml, ma su PyPI la libreria si chiama PyYAML; scrive import cv2, ma il pacchetto è opencv-python. Se l’IA copia il nome dell’import nel comando di installazione, ottieni pip install yaml. Il giorno della prova yaml e cv2 non esistevano su PyPI (la JSON API rispondeva 404), quindi quel comando sarebbe fallito. Se qualcuno registrasse quei nomi, lo stesso comando installerebbe il suo pacchetto.

Lato di uno scatolone con tre etichette bianche: un codice a barre e due codici QR, su sfondo chiaro
Come per un pacco, l’etichetta va letta prima di aprire: il nome giusto si confronta con la fonte, non con la memoria. Foto: Kampus Production · Pexels

La regola pratica: il nome da installare si copia dalla documentazione ufficiale della libreria, non dalla chat. Se non trovi la libreria da nessuna parte fuori dalla chat, trattala come inventata.

Due segnali di nome sospetto, anche quando il pacchetto esiste:

  • Il nome è quasi uguale a una libreria famosa: una lettera in meno, un trattino al posto del trattino basso, un -python o un -cli aggiunto. È lo schema del typosquatting, descritto nella guida su quando usare pip install e quando no.
  • Il nome descrive troppo bene il tuo problema, come flask-secure-upload-guard: un nome che «suona giusto» per il compito che hai chiesto non prova nulla.

Controllo 3: età, versioni e manutentori

Quando il nome esiste, servono tre informazioni: da quando è pubblicato, quante versioni ha e chi lo mantiene. PyPI e npm le espongono in formato JSON (la JSON API di PyPI e il registro di npm). Questo script usa solo la libreria standard di Python e le legge per te. Salvalo come verifica_pacchetto.py in una cartella qualsiasi:

"""Controlla un pacchetto su PyPI o npm prima di installarlo."""
import json
import sys
import urllib.error
import urllib.request
from datetime import date

def scarica_json(url):
    try:
        with urllib.request.urlopen(url, timeout=15) as risposta:
            return json.load(risposta)
    except urllib.error.HTTPError as errore:
        if errore.code == 404:
            return None
        raise

def stampa(nome, versione, prima, n_versioni, manutentori, link):
    giorni = (date.today() - date.fromisoformat(prima)).days
    print(f"Nome: {nome}  ultima versione: {versione}  versioni: {n_versioni}")
    print(f"Prima pubblicazione: {prima} ({giorni} giorni fa)")
    print(f"Manutentori: {manutentori or 'non indicati'}")
    print(f"Link: {link or 'nessuno'}")
    if giorni < 90 or n_versioni < 3:
        print("ATTENZIONE: pacchetto giovane o con poche versioni, controllalo a mano")

def controlla_pypi(nome):
    dati = scarica_json(f"https://pypi.org/pypi/{nome}/json")
    if dati is None:
        return print(f"NON ESISTE su PyPI: {nome}")
    date_file = [f["upload_time_iso_8601"][:10] for files in dati["releases"].values() for f in files]
    if not date_file:
        return print(f"{nome}: esiste su PyPI ma non ha file da installare")
    ruoli = dati.get("ownership", {}).get("roles", [])
    stampa(dati["info"]["name"], dati["info"]["version"], min(date_file), len(dati["releases"]),
           ", ".join(r["user"] for r in ruoli), dati["info"].get("project_urls"))

def controlla_npm(nome):
    dati = scarica_json(f"https://registry.npmjs.org/{nome.replace('/', '%2F')}")
    if dati is None:
        return print(f"NON ESISTE su npm: {nome}")
    repo = dati.get("repository")
    if isinstance(repo, dict):
        repo = repo.get("url")
    stampa(dati["name"], dati.get("dist-tags", {}).get("latest"), dati["time"]["created"][:10],
           len(dati.get("versions", {})), ", ".join(m["name"] for m in dati.get("maintainers", [])), repo)

if __name__ == "__main__":
    if len(sys.argv) != 3 or sys.argv[1] not in ("pypi", "npm"):
        sys.exit("Uso: python verifica_pacchetto.py pypi|npm nome-pacchetto")
    (controlla_pypi if sys.argv[1] == "pypi" else controlla_npm)(sys.argv[2])

Lancialo dalla sua cartella, con il registro e il nome:

Windows:

py verifica_pacchetto.py pypi requests

macOS / Linux:

python3 verifica_pacchetto.py pypi requests

Output del 27 ottobre 2026 (identico su Debian 13 e su macOS 15.7.7):

Nome: requests  ultima versione: 2.34.2  versioni: 163
Prima pubblicazione: 2011-02-14 (5734 giorni fa)
Manutentori: Lukasa, graffatcolmingov, nateprewitt
Link: {'Documentation': 'https://requests.readthedocs.io', 'Source': 'https://github.com/psf/requests'}

Con npm al posto di pypi controlli un pacchetto JavaScript: per express lo script ha riportato 290 versioni dal 2010. Per super-fast-pdf-cleaner ha stampato NON ESISTE su entrambi i registri. Su un pacchetto caricato su PyPI quella mattina, con una sola versione, è comparsa la riga ATTENZIONE: non lo nomino, perché nulla indica che sia malevolo. La riga segnala solo che manca una storia da valutare.

Terminale Linux con l’output dello script verifica_pacchetto.py per due pacchetti: requests mostra 163 versioni dal 2011, super-fast-pdf-cleaner risulta inesistente su PyPI
Lo stesso script su un pacchetto reale e su un nome inventato: nel secondo caso non c’è nulla da installare. Screenshot: ai riparo

Come leggere il risultato:

Cosa vedi Esito Cosa fai
Anni di storia, molte versioni, repository collegato e nome uguale a quello della documentazione ufficiale Procedi Installa dentro l’ambiente virtuale del progetto, con la versione indicata nella documentazione.
Esiste, ma è giovane, ha poche versioni o nessun link a un repository Fermati Cerca il nome fuori dalla chat: documentazione, repository, chi lo cita. Se non trovi nulla, non installarlo.
NON ESISTE, E404 o No matching distribution con pagina PyPI inesistente Inventato Chiedi all’IA quale libreria reale svolge quel compito, poi riparti dal controllo 1.

I download non sono una prova: il pacchetto vuoto di Lasso Security ne ha raccolti più di 30.000 in tre mesi.

Una rete in più: ignorare i pacchetti appena pubblicati

Le versioni più recenti di pip e npm permettono di ignorare i pacchetti pubblicati negli ultimi giorni. Un nome registrato ieri da qualcuno che sfrutta l’IA non viene proprio considerato.

Con pip 26.1 o successivo (opzione --uploaded-prior-to, documentata nella pagina di pip index), P30D significa «caricato almeno 30 giorni fa».

Windows:

py -m pip install --uploaded-prior-to P30D requests

macOS / Linux:

python3 -m pip install --uploaded-prior-to P30D requests

Per npm l’opzione equivalente è --min-release-age, in giorni, descritta nella documentazione di npm install e presente in npm 11.21.1, la versione della prova. Insieme a --ignore-scripts impedisce anche l’esecuzione degli script di installazione dei pacchetti:

npm install express --min-release-age=30 --ignore-scripts

Nella prova entrambi i comandi, lanciati in modalità simulata (--dry-run), hanno funzionato. Con pip index versions requests --uploaded-prior-to P3650D, cioè dieci anni, l’ultima versione mostrata è diventata la 2.11.1 invece della 2.34.2.

🚨 Avviso critico — L’installazione esegue codice: la documentazione di pip lo scrive nella pagina sulle installazioni sicure: installare un pacchetto può eseguire codice arbitrario contenuto nel pacchetto stesso. Su npm accade con gli script preinstall e postinstall. Per questo i controlli vanno fatti prima del comando di installazione, non dopo.

Errori tipici

  • ERROR: No matching distribution found anche per un pacchetto che esiste → il pacchetto non ha versioni per la tua versione di Python, oppure hai scritto male il nome → apri la pagina pypi.org/project/nome/ e confronta nome e versioni di Python supportate.
  • ERROR: unknown command "index" → la tua copia di pip è precedente alla 21.2, la versione che ha introdotto pip index → aggiornala dentro l’ambiente virtuale con py -m pip install --upgrade pip su Windows o python3 -m pip install --upgrade pip su macOS e Linux.
  • no such option: --uploaded-prior-to → pip è precedente alla 26.1 → aggiorna pip come sopra, oppure usa solo i controlli 1, 2 e 3.
  • npm error code 128 e un messaggio che cita git ls-remote → hai scritto un nome con scope senza la chiocciola, per esempio types/node invece di @types/node, e npm lo ha letto come un repository GitHub → copia il nome completo dalla documentazione, @ incluso.

Domande Frequenti (FAQ)

Basta che pip install vada a buon fine per sapere che il pacchetto è giusto?

No. Se un’installazione riesce, significa solo che un pacchetto con quel nome esiste. Il caso peggiore della package hallucination è proprio questo: il nome inventato dall’IA è stato registrato da qualcun altro e l’installazione va liscia.

Posso chiedere all’IA di verificare da sola se il pacchetto esiste?

Puoi chiederle quale libreria usare e dove trovare la documentazione, ma la verifica va fatta sul registro. Il modello può confermare con sicurezza un nome che non esiste: è lo stesso meccanismo che lo ha portato a inventarlo.

Che differenza c’è tra package hallucination e typosquatting?

Nel typosquatting qualcuno registra un nome simile a una libreria famosa e aspetta un errore di battitura. Nella package hallucination il nome sbagliato lo produce il modello, e chi attacca registra i nomi che i modelli ripetono. La difesa è la stessa: confrontare il nome con la documentazione ufficiale prima di installare.

Vale anche per i pacchetti nel requirements.txt o nel package.json generati dall’IA?

Sì, nome per nome. Un file di dipendenze generato in blocco può contenere un nome inventato tra dieci reali. Lancia lo script su ogni riga che non riconosci; per trovare le righe pip install sparse in README e script c’è lo script greppabile per cercare le 7 falle in un repo.

Se ho già installato un pacchetto sospetto, cosa faccio?

Disinstallarlo non basta, perché il codice di installazione potrebbe essere già stato eseguito. Se lo hai installato dentro un ambiente virtuale, cancella la cartella .venv e ricreala; poi cambia le password e le chiavi API presenti sul computer o nel progetto. Le 7 falle di sicurezza nel codice scritto dall’IA descrivono il contesto della falla 7.

Dopo il controllo

Rendi i tre controlli un’abitudine: nome sul registro, nome confrontato con la documentazione, età e manutentori con lo script. Installa sempre dentro un ambiente virtuale (venv), così un errore resta confinato al progetto. Per i progetti JavaScript, il passo successivo è controllare le dipendenze già installate con la guida su supply chain e npm audit. Se ti serve uno strumento che faccia questi controlli su tutto il requirements.txt in un colpo solo, è uno dei piccoli programmi che realizzo su richiesta. Per gli altri quattro passaggi della revisione, torna al protocollo per il codice generato dall’IA.

Alex Lunalbi

Alex Lunalbi

Security & privacy

Dona su PayPal

Scrivo di sicurezza digitale pratica e codice IA. Su ai riparo ogni procedura è verificata su macchine reali.

Tag
  • sicurezza,
  • pip,
  • npm,
  • supply chain,
  • vibe coding