
Abbiamo verificato zx di Google con e senza una skill di revisione
Il banco di prova delle skill, parte 4 di 4. Stesso modello, stesso prompt, una skill installata o meno. Anche nella serie: una landing page, un bot Telegram e il debug di un gioco snake.
In tre voci di questa serie, il modello è stato coerente: il braccio con skill fa di più, controlla di più, spende più token e produce un risultato più ponderato. Questa voce rompe tale schema. È l'unica volta in tutto il banco di prova in cui l'esecuzione con skill è risultata più economica della baseline, ed è anche l'unica volta in cui il braccio con skill ha mancato la scoperta più significativa della serie. Entrambe le cose sono vere contemporaneamente, e la tensione tra di esse è il risultato più utile che abbiamo ottenuto da questo progetto.
Perché zx di Google
Volevamo un compito di audit del codice con tre proprietà: codice reale, non uno snippet sintetico con bug pre-inseriti; qualcosa che un developer di medio livello potesse plausibilmente essere chiamato a revisionare un martedì; e una codebase sufficientemente nota affinché i lettori potessero verificare le nostre affermazioni rispetto alla fonte reale invece di fidarsi ciecamente.
google/zx soddisfaceva tutti e tre i requisiti. È la libreria di Google per scrivere script di shell in JavaScript, abbastanza popolare da essere un componente significativo degli strumenti Node, e abbastanza piccola da rendere l'audit di un singolo file un compito equo piuttosto che un progetto di ricerca. Abbiamo scelto src/core.ts, il file che gestisce l'avvio dei processi e l'ambiente, prelevato fresco dal repository. Nulla qui è una vulnerabilità artefatta. È un file reale, attivamente mantenuto, e zx è generalmente ben costruito: struttura pulita, impostazioni predefinite sensate nella maggior parte dei casi, il tipo di codice che supera una lettura superficiale. Questo è esattamente lo scenario in cui un audit si ripaga trovando l'unica cosa che una lettura superficiale manca, oppure no.
Metodologia
Due esecuzioni, stesso modello, stessa istruzione: audit di src/core.ts da google/zx e produzione di risultati classificati da P0 a P3, ciascuno con una posizione, cosa si rompe, quando si rompe e una correzione minima. La prima esecuzione ha ricevuto solo questo brief. La seconda esecuzione ha ricevuto lo stesso brief più la nostra checklist di sicurezza e di revisione del codice, quella inclusa nel nostro Security Pack e Developer Toolkit, letta per intero prima dell'inizio dell'audit.
Stessa avvertenza di ogni altra voce di questa serie: si tratta di una singola esecuzione per braccio, un singolo modello, un singolo file. Non affermiamo che i conteggi specifici dei risultati si replichino in una nuova esecuzione. Ciò che riportiamo è ciò che è accaduto, con telemetria reale del harness, e un pattern nel tipo di cose che ogni braccio rileva, che riteniamo si generalizzi meglio dei numeri grezzi.
Cosa ha trovato la baseline
Lasciato solo, il modello ha letto core.ts e, di sua iniziativa, ha anche richiamato util.ts e error.ts per contesto prima di redigere qualsiasi cosa. Nessuno glielo ha detto. Ha deciso che il file non aveva senso in isolamento e ha cercato nei suoi vicini, il che si è rivelato importante.
La scoperta principale della baseline, e la scoperta di massima gravità dell'intero banco di prova, è questa. Alla riga 139, core.ts imposta l'opzione ambiente di default su env: process.env. Questa non è una copia dell'ambiente, ma un riferimento live ad esso. Una volta che lo si sa, la conseguenza è semplice: se un percorso di codice fa qualcosa come $.env.FOO = 'x', non sta impostando una variabile con scope limitato a quella singola chiamata di shell. Sta mutando process.env per l'intera applicazione in esecuzione, il che significa che ogni altra parte del programma, ogni altra libreria, qualsiasi cosa legga le variabili d'ambiente dopo quel punto, vede il cambiamento. Una modifica di configurazione destinata a una chiamata di sottoprocesso si riversa nello stato globale. In un processo server a lunga esecuzione, o in qualsiasi script che distribuisce più chiamate zx con ambienti leggermente diversi, questo è il tipo di bug che non compare in un test rapido e poi corrompe una parte completamente non correlata del sistema giorni dopo. Abbiamo verificato questo direttamente sulla fonte piuttosto che fidarci della parola del modello, e la riga fa esattamente ciò che la scoperta indica.
Il resto dell'elenco della baseline, classificato P2 e P3, era solido senza essere spettacolare: un fallimento nel rilevamento di bash che viene silenziosamente inghiottito e poi emerge come un messaggio di errore fuorviante e non correlato invece della causa reale, e un caso in cui break() lancia sincronicamente dall'interno di un handler .catch(), che è il tipo di errore di flusso di controllo facile da scrivere e fastidioso da debuggare perché lo stack trace punta a un punto inutile.
Conteggio totale baseline: un P1, due P2, quattro P3.
STARTER PACK GRATUITO
Curioso di sapere cosa rileva un audit non guidato di Claude sul tuo codice prima di aggiungere una checklist? Il nostro starter pack gratuito è un modo rapido per ottenere un secondo passaggio.
Ottieni lo starter pack gratuitoCosa ha trovato il braccio con checklist
L'esecuzione guidata da skill ha seguito la nostra checklist di revisione del codice, la stessa trattata nel nostro riepilogo delle skill di sicurezza, e ha agito come un revisore completamente diverso: stesso file, stesso accesso, un insieme di preoccupazioni completamente diverso è emerso.
La sua migliore scoperta è stata un rifiuto di promise non gestito: i siti di chiamata per break() e timeout() invocano kill() senza attenderlo, quindi un rifiuto da quella chiamata non ha dove atterrare e può far crashare il processo al di fuori di qualsiasi try/catch scritto da un chiamante. Ha anche rilevato un TypeError che emerge quando qualcosa tenta di iterare in modo asincrono una ProcessPromise dopo che è già stata fermata, un caso limite reale che si manifesta solo sotto una specifica sequenza di chiamate. E ha segnalato che ZX_PREFIX e ZX_POSTFIX vengono inseriti direttamente in ogni invocazione di shell senza sanitizzazione, meritevole dell'attenzione di un manutentore se uno dei due valori può provenire dall'esterno dell'autore dello script fidato.
Questi sono risultati reali e ben formati, non riempitivi. Il report del braccio con checklist era anche il più utilizzabile dei due documenti per i suoi stessi termini: struttura uniforme, linguaggio di gravità coerente, ogni voce seguiva la stessa forma posizione-impatto-correzione senza che il modello dovesse inventare quel formato al volo.
Conteggio totale checklist: due P2, cinque P3. Nessun P1, e in particolare, nessun risultato relativo a process.env.
La sovrapposizione, e quanto fosse piccola
Allineando entrambe le liste, la sovrapposizione è prossima allo zero. Sei risultati della baseline, sette risultati della checklist, tredici in totale, e nessuno di essi indica la stessa causa radice. La mutazione di process.env. L'inghiottimento del rilevamento di bash. Il lancio sincrono in un catch. Il kill() non atteso. Il TypeError dell'iteratore fermato. L'inserimento non sanitizzato di prefisso/suffisso. Ognuno è un percorso di codice diverso.
Questa è una sorpresa maggiore rispetto a ciascuna lista individuale. Due revisori competenti che esaminano le stesse circa 300 righe di TypeScript, uno guidato da una checklist strutturata e uno no, e arrivano a insiemi di problemi quasi interamente disgiunti. Se ci avreste detto all'inizio che l'esecuzione con checklist avrebbe sostanzialmente ricontrollato l'elenco della baseline e aggiunto rifiniture, ci avremmo creduto. Non è quello che è successo. La checklist non ha affinato la stessa ricerca, ha eseguito una ricerca diversa.
I numeri
| Token usati |
| Risultati P1 |
| Risultati P2 |
| Risultati P3 |
| Risultati totali |
| Ha letto proattivamente file vicini |
| Risultato di massima gravità |
| Baseline (senza skill) | Braccio skill (checklist di revisione codice) | Delta |
|---|---|---|
| 87,899 | 81,743 | −7% |
| 1 | 0 | −1 |
| 2 | 2 | 0 |
| 4 | 5 | +1 |
| 7 | 7 | 0 |
| Sì (util.ts, error.ts) | No | — |
| mutazione di process.env (verificato) | — | — |
Ogni altra voce di questa serie ha mostrato che il braccio con skill costava più token per un risultato più disciplinato. Questa è l'unica eccezione: il braccio con checklist è stato il 7% più economico. La nostra interpretazione è che una checklist restringe lo spazio di ricerca, e uno spazio di ricerca più ristretto è più economico da eseguire, anche quando è uno che chiude alcuni dei percorsi che un passaggio libero avrebbe esplorato.
Minimo vs. massimo
Questa è la cosa più acuta che abbiamo imparato in tutte e quattro le voci, e si manifesta più chiaramente qui. La checklist ha reso l'audit più economico e gli ha conferito un formato più rigoroso e coerente. Ciò che non ha fatto è stato trovare il bug di process.env, perché quella scoperta non proveniva da alcuna categoria della checklist. È venuta dalla baseline che ha notato che core.ts da solo era difficile da analizzare, decidendo di sua iniziativa di richiamare util.ts e error.ts, e seguendo quell'intuizione in un punto dove la struttura della checklist non ha mai indicato.
Una checklist alza il minimo. Garantisce uno standard minimo di copertura: ogni categoria viene controllata, ogni risultato viene redatto allo stesso modo, e non si perde una facile scoperta a causa di una giornata storta. Non alza il massimo. La migliore scoperta possibile su un dato file potrebbe trovarsi al di fuori di ogni categoria elencata dalla checklist, e un processo che segue solo la checklist la supererà, con sicurezza, in un report ben formattato.
Questo non è un argomento contro le checklist. Nessuno dei sette risultati della checklist era negativo, e due di essi erano il tipo di cose che un revisore umano impegnato potrebbe plausibilmente mancare sotto pressione temporale. È un argomento per sapere a cosa serve una checklist. È uno strumento per alzare il minimo, non per alzare il massimo, e trattarla come entrambi è il modo in cui un vero P1 sfugge a una revisione che altrimenti sembra approfondita.
SKILLPROOF PACK
L'esatta checklist che è stata eseguita in questo test, quella che ha trovato il kill() non atteso e l'inserimento di prefisso non sanitizzato, è inclusa nel nostro Security Pack insieme al resto delle nostre skill di revisione più apprezzate.
Ottieni il Security Pack — $10Come eseguire un audit a due passaggi da soli
Dato ciò che abbiamo trovato, la nostra raccomandazione effettiva non è "usa una checklist" o "salta la checklist". È di eseguirli entrambi, su qualsiasi cosa che conti.
Inizia con un passaggio libero. Punta il modello al file, dagli il brief dell'audit e lascialo leggere qualsiasi altra cosa voglia leggere. Non dargli una rubrica. Questo è il passaggio che ha le migliori probabilità di cogliere la cosa che nessuno ha pensato di mettere in una lista, perché non è vincolato alla lista.
Poi esegui un secondo passaggio separato con una checklist strutturata, la nostra o la tua. Questo è il passaggio che garantisce la copertura: le categorie noiose da controllare a mano ma facili da saltare quando si segue un'intuizione, la sanitizzazione, la gestione degli errori, la pulizia delle risorse, vengono esaminate ogni volta.
Confronta i due report prima di considerare uno dei due come definitivo. Se il nostro numero di sovrapposizione si mantiene sul tuo codice come si è mantenuto su zx, aspettati che le due liste condividano meno della metà dei loro risultati. Trattalo come il risultato atteso, non come un segno che uno dei passaggi è fallito. Il protocollo di test completo copre in maggior dettaglio come strutturiamo questo tipo di esecuzione accoppiata, incluso come controlliamo che il modello non legga il proprio output precedente.
Se hai budget solo per un passaggio, il nostro onesto consiglio basato su questo risultato è: esegui prima il passaggio libero. È quello più propenso a trovare la scoperta che non sapevi di dover cercare. Poi, se il tempo lo permette, seguilo con la checklist per assicurarti che nulla di banale sia stato perso. Per una visione più ampia di quali skill gestiscono bene questo tipo di revisione, consulta il nostro riepilogo delle migliori skill di coding.
FAQ
La scoperta di process.env è una vera vulnerabilità in zx?
È un comportamento reale che merita l'attenzione di un manutentore, non una CVE divulgata e non qualcosa che stiamo inquadrando come un exploit attivo. zx è una libreria complessivamente ben costruita e attivamente mantenuta, e questa è una scelta di design, un riferimento live invece di una copia, che ha una genuina conseguenza di mutazione per qualsiasi percorso di codice che scrive in $.env. Abbiamo verificato la riga noi stessi rispetto alla fonte piuttosto che fidarci del report del modello, ed è esattamente per questo che ci sentiamo a nostro agio nel definirla la scoperta principale dell'intero banco di prova.
Perché il braccio con skill è costato meno qui, quando è costato di più in ogni altro punto di questa serie?
La nostra migliore spiegazione è lo scope. Una skill di design nella voce della landing page invita all'iterazione: controlla l'output, rivedi, controlla di nuovo. Una checklist di revisione funziona diversamente. Definisce un insieme fisso di categorie da esaminare una volta, il che restringe la ricerca piuttosto che espanderla. Ricerca più ristretta, meno token. È un meccanismo plausibile, non provato, dato che si tratta di una singola esecuzione.
Dovrei fidarmi di una revisione del codice AI guidata da checklist per rilevare tutto?
No, e questa è la scoperta principale qui. Una checklist è un dispositivo per alzare il minimo: garantisce una scansione minima coerente attraverso categorie note. Non è un dispositivo per alzare il massimo, e la scoperta più grande di tutto questo banco di prova è venuta da un passaggio che non ne seguiva una. Usa una checklist per la copertura e la coerenza. Non usarla come unico passaggio su codice che conta davvero.
Cosa significa questo per la scelta di una skill di revisione del codice Claude in pratica?
Non trattare "quale skill" come l'unica decisione. Tratta "quanti passaggi" come quella più importante. Una buona skill di checklist, come quella nella nostra lista di code-review-checklist, vale la pena installarla per la coerenza e le categorie che garantisce. Accoppiala con almeno un passaggio non guidato su qualsiasi cosa che spediresti effettivamente, e leggi la nostra copertura delle skill di sicurezza per capire come valutiamo le skill di revisione l'una contro l'altra nel catalogo.
Questa è la serie. Quattro compiti, quattro verdetti onesti, e il filo conduttore tra tutti è lo stesso: una skill cambia ciò che un modello controlla, non se è capace di svolgere il lavoro. Se questo compromesso valga la pena dipende interamente da ciò che stai costruendo e da quanto attentamente qualcuno lo esaminerà in seguito.
★ 9.6/10 × 3
Lo starter pack gratuito
I 3 skill con i nostri punteggi di test più alti, più la checklist di installazione: il setup che metteremmo su una macchina appena formattata. Gratis, via email.