Vi reviderte Googles zx med og uten en gjennomgangsferdighet

Vi reviderte Googles zx med og uten en gjennomgangsferdighet

Ferdighetsbenken, del 4 av 4. Samme modell, samme prompt, én ferdighet installert eller ikke. Også i serien: en landingsside, en Telegram-bot, og feilsøking av et slangespill.

Tre innlegg inn i denne serien, har mønsteret vært konsistent: ferdighetsarmen gjør mer, sjekker mer, bruker flere tokens, og produserer et mer gjennomtenkt resultat. Dette innlegget bryter det mønsteret. Det er den ene gangen i hele benken hvor ferdighetskjøringen var billigere enn grunnlinjen, og det er også den ene gangen ferdighetsarmen bommet på det største funnet i serien. Begge deler er sant samtidig, og spenningen mellom dem er det mest nyttige resultatet vi har fått ut av dette prosjektet.

Hvorfor Googles zx

Vi ønsket en kodenrevisjonsoppgave med tre egenskaper: ekte kode, ikke et syntetisk, feilplantet kodeutdrag; noe en mellomnivåutvikler med rimelighet kunne bli bedt om å gjennomgå på en tirsdag; og en kodebase som er kjent nok til at lesere kan sjekke våre påstander mot den faktiske kilden i stedet for å stole blindt på oss.

google/zx passet alle tre. Det er Googles bibliotek for å skrive skallskript i JavaScript, populært nok til at en betydelig del av Node-verktøy avhenger av det, og lite nok til at en enkelt filrevisjon er en rimelig oppgave snarere enn et forskningsprosjekt. Vi valgte src/core.ts, filen som utfører den faktiske prosess-spawningen og miljøhåndteringen, hentet ferskt fra depotet. Ingenting her er en konstruert sårbarhet. Det er en ekte, aktivt vedlikeholdt fil, og zx er generelt godt bygget: ren struktur, fornuftige standardinnstillinger de fleste steder, den typen kode som passerer en uformell gjennomlesning. Det er akkurat det oppsettet hvor en revisjon enten forsvarer sin eksistens ved å finne den ene tingen en uformell gjennomlesning overser, eller så gjør den det ikke.

Metodikk

To kjøringer, samme modell, samme instruksjon: revider src/core.ts fra google/zx og produser funn rangert P0 til P3, hvert med en lokasjon, hva som bryter, når det bryter, og en minimal fiks. Kjøring én fikk den briefen og ingenting annet. Kjøring to fikk den identiske briefen pluss vår egen sikkerhetsgjennomgangs- og kodegjennomgangs-sjekkliste, den som følger med i vår Security Pack og Developer Toolkit, lest i sin helhet før revisjonen startet.

Samme forbehold som for alle andre innlegg i denne serien: dette er én kjøring per arm, én modell, én fil. Vi hevder ikke at de spesifikke antall funn replikeres ved en ny kjøring. Det vi rapporterer er hva som skjedde, med ekte telemetri fra testrammeverket, og et mønster i typen ting hver arm fanger opp som vi mener generaliserer bedre enn de rå tallene gjør.

Hva grunnlinjen fant

Når den ble overlatt til seg selv, leste modellen core.ts og, på eget initiativ, hentet også inn util.ts og error.ts for kontekst før den skrev noe. Ingen ba den om det. Den bestemte at filen ikke ga mening isolert og så på naboene, noe som viste seg å være viktig.

Grunnlinjens toppfunn, og det høyest-alvorlige funnet i hele benken, er dette. På linje 139, setter core.ts sin miljøopsjon til env: process.env som standard. Det er ikke en kopi av miljøet, det er en levende referanse til det. Når du vet det, er konsekvensen enkel: hvis en hvilken som helst kodesti gjør noe som $.env.FOO = 'x', setter den ikke en variabel begrenset til det ene skallkallet. Den muterer process.env for hele den kjørende applikasjonen, noe som betyr at alle andre deler av programmet, alle andre biblioteker, alt som leser miljøvariabler etter det punktet, ser endringen. En konfigurasjonsjustering ment for ett underprosesskall lekker inn i global tilstand. I en langvarig serverprosess, eller i et hvilket som helst skript som sprer ut flere zx-kall med litt forskjellige miljøer, er det den typen feil som ikke dukker opp i en rask test og deretter korrumperer en helt urelatert del av systemet dager senere. Vi verifiserte dette direkte mot kilden i stedet for å stole på modellens ord, og linjen gjør nøyaktig det funnet sier.

Resten av grunnlinjens liste, vurdert P2 og P3, var solid uten å være spektakulær: en bash-deteksjonsfeil som svelges stille og senere dukker opp som en misvisende, urelatert feilmelding i stedet for den virkelige årsaken, og et tilfelle hvor break() kaster synkront fra innsiden av en .catch()-håndterer, som er den typen kontrollflytfeil som er lett å skrive og irriterende å feilsøke fordi stakksporingen peker et ubrukelig sted.

Full grunnlinjetelling: én P1, to P2-er, fire P3-er.

GRATIS STARTPAKKE

Nysgjerrig på hva en uledet Claude-revisjon fanger opp i din egen kode før du legger til en sjekkliste? Vår gratis startpakke er en rask måte å få en ny gjennomgang i gang.

Få den gratis startpakken

Hva sjekkliste-armen fant

Den ferdighetsstyrte kjøringen fulgte vår kodegjennomgangs-sjekkliste, den samme som er dekket i vår oppsummering av sikkerhetsferdigheter, og den leste som en helt annen anmelder: samme fil, samme tilgang, et helt annet sett med bekymringer dukket opp.

Dets beste funn var en uhåndtert promise-avvisning: kallsteder for break() og timeout() påkaller kill() uten å vente på den, så en avvisning fra det kallet har ingen steder å lande og kan krasje prosessen utenfor en eventuell try/catch en kaller skrev. Den fanget også en TypeError som dukker opp når noe prøver å asynkront-iterere en ProcessPromise etter at den allerede er stoppet, et reelt grensetilfelle som bare viser seg under en spesifikk sekvens av kall. Og den flagget at ZX_PREFIX og ZX_POSTFIX blir spleiset direkte inn i hvert skallkall uten sanitering, verdt en vedlikeholders oppmerksomhet hvis noen av verdiene noensinne kan komme fra utenfor den betrodde skriptforfatteren.

Dette er ekte, velformede funn, ikke fyllstoff. Sjekkliste-armens rapport var også den mest brukbare av de to dokumentene på egne premisser: ensartet struktur, konsistent alvorlighetsgradsspråk, hver oppføring fulgte samme lokasjon-påvirkning-fiks-form uten at modellen måtte finne opp det formatet underveis.

Full sjekkliste-telling: to P2-er, fem P3-er. Ingen P1-er, og bemerkelsesverdig nok, ingen process.env-funn i det hele tatt.

Overlappen, og hvor liten den var

Still opp begge listene, og overlappen er nær null. Seks grunnlinjefunn, syv sjekklistefunn, tretten totalt, og ikke ett av dem navngir samme grunnårsak. process.env-mutasjonen. Bash-deteksjonssvelgingen. Det synkrone kastet i en catch. Den uventede kill(). Den stoppede-iterator TypeError. Den usaniterte prefiks/postfiks-spleisen. Hver eneste er en forskjellig kodesti.

Det er en større overraskelse enn noen av de individuelle listene. To kompetente anmeldere som ser på de samme rundt 300 linjene med TypeScript, den ene styrt av en strukturert sjekkliste og den andre ikke, og de ender opp med nesten helt usammenhengende sett med problemer. Hvis du hadde fortalt oss på forhånd at sjekklistekjøringen i bunn og grunn ville dobbeltsjekke grunnlinjens liste og legge til polering, ville vi ha trodd det. Det var ikke det som skjedde. Sjekklisten forfinet ikke det samme søket, den kjørte et annet søk.

Tallene

Grunnlinje (ingen ferdighet) Ferdighetsarm (kodegjennomgangs-sjekkliste) Delta
Brukte tokens 87,899 81,743 −7%
P1 funn 1 0 −1
P2 funn 2 2 0
P3 funn 4 5 +1
Totalt antall funn 7 7 0
Proaktivt lest nabofiler Ja (util.ts, error.ts) Nei
Funn med høyest alvorlighetsgrad process.env mutasjon (verifisert)

Hvert annet innlegg i denne serien viste at ferdighetsarmen kostet flere tokens for et mer disiplinert resultat. Dette er det ene unntaket: sjekkliste-armen var 7% billigere. Vår tolkning er at en sjekkliste innsnevrer søkeområdet, og et smalere søkeområde er billigere å utføre, selv når det også er et som stenger av noen av stiene en frittgående gjennomgang ville ha vandret ned.

Gulv vs. tak

Dette er det skarpeste vi har lært på tvers av alle fire innleggene, og det viser seg tydeligst her. Sjekklisten gjorde revisjonen billigere og ga den et mer rigorøst, mer konsistent format. Det den ikke gjorde, var å finne process.env-feilen, fordi det funnet ikke kom fra noen kategori på sjekklisten. Det kom fra at grunnlinjen la merke til at core.ts alene var vanskelig å resonnere rundt, bestemte seg på egen hånd for å hente util.ts og error.ts, og fulgte den intuisjonen et sted sjekklistens struktur aldri pekte.

En sjekkliste hever gulvet. Den garanterer en minimumsstandard for dekning: hver kategori blir sjekket, hvert funn blir skrevet opp på samme måte, og du mister ikke et enkelt funn på grunn av en dårlig dag. Den hever ikke taket. Det best mulige funnet på en gitt fil kan ligge utenfor hver kategori sjekklisten lister opp, og en prosess som bare følger sjekklisten vil gå rett forbi det, selvsikkert, i en pent formatert rapport.

Det er ikke et argument mot sjekklister. Null av sjekklistens syv funn var dårlige, og to av dem var den typen ting en travel menneskelig anmelder med rimelighet overser under tidspress. Det er et argument for å vite hva en sjekkliste er til for. Det er et gulvhevende verktøy, ikke et takhevende, og å behandle det som begge deler er hvordan en ekte P1 slipper gjennom en gjennomgang som ellers ser grundig ut.

SKILLPROOF PACK

Den nøyaktige sjekklisten som kjørte i denne testen, den som fant den uventede kill() og den usaniterte prefiks-spleisen, følger med i vår Security Pack sammen med resten av våre topprangerte gjennomgangsferdigheter.

Få Security Pack — $10

Hvordan kjøre en to-pass revisjon selv

Gitt hva vi fant, er vår faktiske anbefaling ikke "bruk en sjekkliste" eller "dropp sjekklisten." Det er å kjøre begge, på alt som betyr noe.

Start med en fri gjennomgang. Pek modellen mot filen, gi den revisjonsbriefen, og la den lese hva den ellers vil lese. Ikke gi den en rubrikk. Dette er gjennomgangen som har best sjanser til å fange opp det ingen tenkte å sette på en liste, fordi den ikke er begrenset til listen.

Kjør deretter en andre, separat gjennomgang med en strukturert sjekkliste, vår eller din egen. Dette er gjennomgangen som garanterer dekning: kategoriene som er kjedelige å sjekke manuelt, men lett å hoppe over når du følger en intuisjon, sanitering, feilhåndtering, ressursrydding, blir gjennomgått hver gang.

Sammenlign de to rapportene før du leser noen av dem som endelige. Hvis vårt overlappstall holder på din kode slik det holdt på zx's, forvent at de to listene deler mindre enn halvparten av funnene sine. Behandle det som det forventede utfallet, ikke et tegn på at noen av gjennomgangene feilet. Den fulle testprotokollen dekker hvordan vi strukturerer denne typen parede kjøringer mer detaljert, inkludert hvordan vi kontrollerer for at modellen leser sin egen tidligere utdata.

Hvis du bare har budsjett for én gjennomgang, er vårt ærlige råd basert på dette resultatet: kjør den frie gjennomgangen først. Det er den som er mer sannsynlig å finne funnet du ikke visste du skulle se etter. Deretter, hvis tiden tillater det, følg opp med sjekklisten for å sikre at ingenting kjedelig ble oversett. For et bredere syn på hvilke ferdigheter som håndterer denne typen gjennomgang godt, se vår oppsummering av de beste kodeferdighetene.

Ofte stilte spørsmål

Er process.env-funnet en reell sårbarhet i zx? Det er en reell oppførsel verdt en vedlikeholders blikk, ikke en offentliggjort CVE og ikke noe vi fremstiller som et aktivt utnyttelse. zx er et velbygd, aktivt vedlikeholdt bibliotek totalt sett, og dette er et designvalg, en levende referanse i stedet for en kopi, som har en ekte mutasjonskonsekvens for enhver kodesti som skriver til $.env. Vi verifiserte linjen selv mot kilden i stedet for å stole på modellens rapport, noe som er nøyaktig grunnen til at vi er komfortable med å kalle det det toppfunnet i hele benken.

Hvorfor kostet ferdighetsarmen mindre her, når den kostet mer overalt ellers i denne serien? Vår beste forklaring er omfang. En designferdighet i landingsside-innlegget inviterer til iterasjon: sjekk utdata, revider, sjekk igjen. En gjennomgangs-sjekkliste fungerer annerledes. Den definerer et fast sett med kategorier å gjennomgå én gang, noe som innsnevrer søket i stedet for å utvide det. Smalere søk, færre tokens. Det er en plausibel mekanisme, ikke en bevist, siden dette er en enkelt kjøring.

Bør jeg stole på en sjekkliste-styrt AI-kodegjennomgang for å fange opp alt? Nei, og det er hovedfunnet her. En sjekkliste er et gulvhevende verktøy: den garanterer en konsistent minimumsgjennomgang på tvers av kjente kategorier. Det er ikke et takhevende verktøy, og det største funnet i hele denne benken kom fra en gjennomgang som ikke fulgte en. Bruk en sjekkliste for dekning og konsistens. Ikke bruk den som din eneste gjennomgang på kode som faktisk betyr noe.

Hva betyr dette for valg av en Claude-kodegjennomgangsferdighet i praksis? Ikke behandle "hvilken ferdighet" som den eneste beslutningen. Behandle "hvor mange gjennomganger" som den viktigere. En god sjekkliste-ferdighet, som den i vår kodegjennomgangs-sjekkliste-oppføring, er verdt å installere for konsistensen og kategoriene den garanterer. Par den med minst én uledet gjennomgang på alt du faktisk ville shippe, og les vår sikkerhetsferdighetsdekning for hvordan vi veier gjennomgangsferdigheter mot hverandre i katalogen.

Det var serien. Fire oppgaver, fire ærlige konklusjoner, og den røde tråden på tvers av alle er den samme: en ferdighet endrer hva en modell sjekker, ikke om den er i stand til arbeidet. Om den byttehandelen er verdt det, avhenger helt av hva du bygger og hvor nøye noen kommer til å se på det etterpå.

★ 9.6/10 × 3

Den gratis startpakken

De 3 skillsene med våre høyeste testscorer pluss installasjonssjekklisten — oppsettet vi selv ville lagt på en fersk maskin. Gratis, på e-post.

Én e-post med pakken + en kort ukentlig oppsummering av nye testresultater. Meld deg av når du vil.