# GitHub AI Scan trova due rischi Shell, ma il controllo di sicurezza è verde

> Canonical: https://vxi.me/it/posts/github-ai-scan-pr-security-checks/
> Locale: it-it
> Published: 2026-10-16
> Updated: 2026-10-16
> Markdown: https://vxi.me/it/posts/github-ai-scan-pr-security-checks.md

# GitHub AI Scan trova due rischi Shell, ma il controllo di sicurezza è verde

Oggi ho aperto il [PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) in un repository pubblico di test, inserendo intenzionalmente due pattern Bash pericolosi. Circa 81 secondi dopo, `github-advanced-security[bot]` aveva pubblicato due commenti in Files changed: entrambe le anomalie erano state individuate.

Eppure, nell'[esecuzione GitHub Actions #38835686338](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/actions/runs/38835686338), il job `github-advanced-security` risultava `Success`. Gli avvisi erano reali, e lo era anche il controllo verde.

Il giorno prima GitHub aveva annunciato la [disponibilità generale (GA) di AI Scan for pull requests](https://github.blog/changelog/2026-10-15-ai-scan-for-pull-requests-generally-available/). Quello che volevo capire non era quanto fosse brillante uno scanner AI, ma **come potesse trovare codice rischioso e lasciare comunque il controllo di sicurezza del PR nello stato riuscito**.

La distinzione conta: **la riuscita del job AI Scan non certifica che il codice sia sicuro**. Attualmente i suoi risultati sono avvisi (`advisory`), non regole che impediscono automaticamente il merge.

## Due pattern Bash rischiosi: che cosa ha trovato davvero

Ho messo i file soltanto nella cartella `ai-scan-test/` di un PR temporaneo. Non ho toccato il codice applicativo di produzione né impiegato credenziali o server reali. Il sorgente completo è disponibile al [commit fisso `29e24722d3`](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/blob/29e24722d3f8868322da3aa28ec01c9648f90726/ai-scan-test/vulnerable-deploy.sh). Le righe seguenti sono **volutamente insicure e non vanno usate in produzione**:

```bash
eval "systemctl restart ${service_name}"

cmd="curl -fsSL http://${host}/${path} -o /tmp/svc.conf"
$cmd
```

Il primo problema si trova nella funzione che riavvia un servizio. Il valore esterno `service_name` viene interpolato in `eval`, che fa interpretare alla shell il testo risultante come un nuovo comando. Se un nome di servizio contiene sintassi dannosa, non è più un semplice argomento. Il [commento del bot alla riga 17](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992720) lo classifica come CWE-78 e consiglia di eliminare `eval`, passare parametri tra virgolette e limitare i servizi autorizzati.

Il secondo caso è diverso. Lo script costruisce un comando `curl` completo nella variabile `cmd`, poi esegue `$cmd` senza virgolette. Il word splitting durante l'espansione può consentire a `host` e `path`, se controllabili, di modificare gli argomenti e le opzioni ricevuti da `curl`. Il [commento alla riga 27](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992738) segnala anche il rischio di manomissione di un file di configurazione scaricato in chiaro via `http://`.

| Punto | Risultato specifico di AI Scan | Commento originale |
| --- | --- | --- |
| Riga 17, `eval` | Input reinterpretato come sintassi shell (CWE-78) | [Leggi il commento](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992720) |
| Riga 27, `$cmd` | Iniezione di argomenti/opzioni e trasporto HTTP non cifrato | [Leggi il commento](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992738) |

Non bisogna descrivere i due casi come se fossero identici. Nel primo, l'input viene passato di nuovo al parser della shell. Nel secondo, il meccanismo verificato riguarda soprattutto l'**iniezione di argomenti o opzioni**: l'espansione senza virgolette non dimostra, da sola, l'esecuzione arbitraria di un altro comando shell indipendente.

C'è poi un limite che cambia l'interpretazione del test. Il fixture **non nascondeva le vulnerabilità**. Nel [README](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/blob/29e24722d3f8868322da3aa28ec01c9648f90726/ai-scan-test/README.md) e nei commenti del codice erano già presenti `INTENTIONAL VULNERABILITY (CWE-78)` e la categoria prevista `String injection`. Gli esempi di sfruttamento descritti dal bot non erano nel sorgente, ma quelle etichette potrebbero aver aiutato il rilevamento. I 2 risultati su 2 del test non rappresentano quindi un tasso generale di rilevamento di vulnerabilità reali senza etichette.

Anche i tempi sono verificabili pubblicamente: il [PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) è stato creato il **2026-10-16 alle 01:32:31 UTC**, e i due commenti sono apparsi alle **01:33:52 UTC**, cioè **81 secondi** dopo. È l'intervallo osservato, una sola volta, fra creazione del PR e commenti, non la media di esecuzione dello scanner.

Entrambi i difetti inseriti apposta sono stati segnalati, ma non ho aperto un PR separato contenente solo codice normale come controllo negativo. Non ho quindi misurato neppure il tasso di falsi positivi.

## Due commenti sulle vulnerabilità, ma Actions risulta verde

Nell'[esecuzione dinamica #38835686338](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/actions/runs/38835686338), il workflow si chiama `Code scanning AI findings on PR #1`. GitHub indica che il job `github-advanced-security` è terminato con `success` già alle **01:33:34 UTC**. I due commenti del bot sono comparsi alle **01:33:52 UTC**, ben **18 secondi dopo**.

Prima il controllo verde, poi i commenti sui rischi. Già quest'ordine mostra che `Success` non vuol dire «nessuna vulnerabilità rilevata».

![Schema GitHub AI Scan del PR #1: due rischi Bash etichettati ricevono commenti advisory; il job ha successo alle 01:33:34 UTC, i commenti arrivano alle 01:33:52 UTC e le regole di merge sono separate](/images/posts/github-ai-scan-pr/ai-scan-advisory.it-it.svg)

*Schema ricavato dal [PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) pubblico e dalla relativa [esecuzione Actions](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/actions/runs/38835686338), **non uno screenshot di GitHub**. Gli 81 secondi misurano la distanza fra apertura del PR e commenti, non il tempo interno dello scanner. Ogni lingua usa il proprio SVG localizzato.*

Questo non dimostra che lo scanner abbia funzionato male. La [documentazione ufficiale di AI Scan](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections) specifica che i risultati sono **advisory**: non fanno fallire il code scanning check e non possono, da soli, diventare una condizione di Ruleset che impedisce il merge. Sono indicazioni da valutare durante la review, non una decisione automatica sulla fusione.

| Segnale visibile su GitHub | Che cosa significa |
| --- | --- |
| Commenti inline di AI Scan | Possibili rischi per autore e reviewer; non bloccano direttamente il merge |
| Job dinamico AI Scan: `Success` | L'operazione di scansione si è conclusa anche se ha prodotto avvisi |
| CodeQL o scanner supportati con Ruleset | Possono applicare condizioni di blocco in base alla configurazione; va verificata |
| Required checks e review umana | Il divieto di merge in caso di controlli mancanti, falliti o review assente dipende dalle impostazioni |

GitHub descrive separatamente la [Code scanning merge protection](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/set-merge-protection). Se l'obiettivo è non fondere codice con problemi gravi ancora aperti, occorre configurare e verificare una regola davvero vincolante; non basta considerare gli avvisi di AI Scan come required checks.

**Non ho testato** concretamente il blocco di un merge tramite Ruleset su questo PR pericoloso. Di conseguenza, non posso dedurre dal `Success` che GitHub permetta qualsiasi fusione insicura. Ho osservato qualcosa di più circoscritto: **due commenti di rischio e un job AI Scan verde nello stesso PR**.

## Prima di attivarlo: CodeQL, requisiti e crediti

Il test è stato eseguito in un **repository pubblico di un account personale**. In base alla [documentazione della GA di ottobre 2026](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections), AI Scan può essere attivato sui repository pubblici personali, ma non su quelli privati personali. È disponibile nei repository pubblici delle organizzazioni, mentre quelli private/internal devono soddisfare i requisiti di licenza GitHub Code Security o GitHub Advanced Security. Non è richiesta una licenza Copilot aggiuntiva solo per AI Scan.

Neppure CodeQL default setup è **un prerequisito**. Al momento del test risultava `not-configured`, eppure AI Scan ha pubblicato due commenti. La [documentazione ufficiale](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections) conferma che non dipende dall'impostazione predefinita di CodeQL né dal buon esito di una sua analisi.

AI Scan, inoltre, non effettua una scansione storica dell'intero repository. Esamina modifiche dei PR ammissibili, comprese alcune situazioni Shell/Bash, Terraform HCL e Dockerfile non coperte da CodeQL. I PR creati da un Fork o da Dependabot non rientrano nell'ambito supportato indicato per questa GA. I team che ne usano molti devono prevedere controlli e review aggiuntivi.

Per abilitarlo, verificare l'interruttore AI Scan nelle impostazioni Code scanning del repository. In un'organizzazione bisogna controllare anche le policy organizzative e le autorizzazioni alle funzioni di sicurezza.

Dalla GA, i consumi sono misurati in **Code Security AI Credits**. Chi sostiene il costo, gli eventuali crediti inclusi e le limitazioni di budget dipendono dalle [regole di fatturazione di GitHub](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections). Non ho confermato l'addebito finale del test, quindi non lo indicherò come `$0`. Per repository con molti PR conviene prima controllare in Billing i crediti e i limiti rigidi di spesa.

## Come impedire davvero il merge di codice pericoloso

I [due commenti](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) indicano correzioni ordinarie: evitare di rinviare parametri a `eval` e non assemblare un intero comando shell come stringa da espandere. Le chiamate possono invece assumere questa forma:

```bash
# 调用形式示意：service_name 和 url 仍需单独校验
systemctl restart "$service_name"
curl -fsSL "$url" -o /tmp/svc.conf
```

È **un'indicazione di correzione, non una patch sicura che ho rieseguito e verificato**. `service_name` richiede un elenco dei valori consentiti. L'URL di download richiede HTTPS, limitazioni su host e redirect e, a seconda dell'importanza del file, la verifica dell'integrità.

Per la protezione del repository, occorre collegare CodeQL o uno scanner che supporti davvero il blocco a Ruleset/required checks appropriati, quindi verificare che un branch protetto rifiuti controlli falliti o assenti. I commenti AI Scan devono comunque essere valutati dai reviewer: il verde del job dinamico non li rende irrilevanti.

Al termine del test ho [chiuso il PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) **senza eseguire il merge** e ho eliminato il branch di prova. Il `main` pubblico era rimasto sul commit originale `87624c26a5e6`. Una ricevuta REST del proprietario ha confermato che AI Scan era stato riportato a `disabled`. Chiudere un PR non cancella i commenti storici né i commit fissati; i link pubblici restano consultabili.

Questo piccolo caso positivo, esplicitamente etichettato, mostra che AI Scan può dare avvisi utili e puntuali. Ma **il completamento della scansione e il blocco effettivo di un merge pericoloso sono due controlli diversi**. Il primo scopre possibili problemi; il secondo va configurato e provato nel repository.

Per un altro punto della catena di sicurezza, il [test dell'hook locale pre-push di OpenQodex](/posts/openqodex-prepush-review-fail-open/) esamina cosa succede quando la code review AI fallisce prima di un Git push.

*Data del test: 16 ottobre 2026 (UTC).*
