I flera decennier har statisk säkerhetstestning av applikationer (SAST) varit ett av de mest effektiva sätten för säkerhetsteam att skala kodgranskningen.
Men när vi utvecklade Codex-säkerhet gjorde vi ett medvetet designval: vi började inte med att importera en statisk analysrapport och be agenten att göra en prioritering utifrån den. Vi utvecklade systemet så att det utgår från själva förvaret – arkitekturen, tillitsgränserna och det avsedda beteendet – och validerar resultaten innan det ber en människa att lägga tid på det.
Anledningen är enkel: de allvarligaste sårbarheterna är som regel inte problem med dataflödet. De uppstår när koden verkar genomföra en säkerhetskontroll, men den kontrollen garanterar i själva verket inte den egenskap som systemet är beroende av. Utmaningen är med andra ord inte bara att spåra hur data rör sig genom ett program – utan att ta reda på om försvarsmekanismerna i koden faktiskt fungerar.
SAST beskrivs ofta som en tydlig process: identifiera en källa till opålitliga indata, spåra data genom programmet och markera fall där dessa data når en känslig destination utan att ha sanerats. Det är en elegant modell som täcker många verkliga fel.
I praktiken måste SAST göra vissa förenklingar för att förbli hanterbart i stor skala. Detta gäller särskilt i verkliga kodbaser med indirekt användning, dynamisk fördelning, återanrop, reflektion och ramverksintensivt kontrollflöde. De här synsätten är inte ett angrepp på SAST, det är helt enkelt verkligheten när man försöker analysera kod utan att köra den.
Det är i sig inte anledningen till att Codex-säkerhet inleder med en SAST-rapport.
Det större problemet är vad som händer efter att du lyckats spåra källan till ett problem.
Även när statisk analys korrekt spårar indata över flera funktioner och lager måste den fortfarande besvara den fråga som faktiskt avgör om det finns en sårbarhet:
Ta ett vanligt mönster: koden anropar något i stil med sanitize_html() innan den renderar opålitligt innehåll. En statisk analysator kan se att saneringen har genomförts. Det den normalt inte kan avgöra är om saneringen faktiskt är tillräcklig för just det specifika renderingssammanhanget, mallmotorn, kodningsbeteendet och de efterföljande omvandlingarna som ingår.
Det är här det blir svårt. Problemet är inte bara om uppgifterna når mottagaren. Det handlar om huruvida kontrollerna i koden faktiskt begränsar värdet på det sätt som systemet antar.
Med andra ord: det är stor skillnad mellan ”koden anropar en saneringsfunktion” och ”systemet är säkert”.
Här är ett mönster som ofta förekommer i riktiga system.
En webbapplikation tar emot en JSON-nyttolast, extraherar en redirect_url, validerar den mot en tillåtelselista-regex, URL-avkodar den och skickar resultatet till en omdirigeringshanterare.
En klassisk rapport från källa till mottagare kan beskriva flödet:
opålitlig indata → kontroll mot regex → URL-avkodning → omdirigering
Men den verkliga frågan är inte om kontrollen finns. Det beror på om den kontrollen fortfarande begränsar värdet efter de efterföljande omvandlingarna.
Om regexen körs före avkodning, begränsar den då faktiskt den avkodade URL:en på det sätt som omdirigeringshanteraren tolkar den?
För att besvara den frågan måste man resonera kring hela transformationskedjan: vad regexen tillåter, hur avkodning och normalisering fungerar, hur URL-parsningen hanterar specialfall, och hur omdirigeringslogiken hanterar scheman och auktoriteter.
Många av de sårbarheter som är relevanta i praktiken ser ut så här: fel i operationsordningen, partiell normalisering, tvetydigheter vid tolkning och avvikelser mellan validering och tolkning. Dataflödet är synligt. Svagheten ligger i hur begränsningarna sprider sig – eller inte sprider sig – genom transformationskedjan.
Det här är inte bara ett teoretiskt mönster. I CVE-2024-29041(öppnas i ett nytt fönster) drabbades Express av ett problem med öppen omdirigering, där felaktigt konstruerade URL:er kunde kringgå vanliga implementeringar av tillåtelselistor på grund av hur omdirigeringsmålen kodades och därefter tolkades. Dataflödet var enkelt. Den svårare frågan – och den som avgjorde om buggen existerade – var om valideringen fortfarande gällde efter transformationskedjan.
Codex-säkerhet bygger på ett enkelt mål: att minska arbetsbördan vid prioritering genom att identifiera problem med hjälp av bättre dokumentation. I produkten innebär detta att man använder en repospecifik kontext (inklusive en hotmodell) och validerar problem med starka signaler i en isolerad miljö innan de presenteras.
När Codex-säkerhet stöter på ett begrepp som liknar ”validering” eller ”sanering” betraktar det inte detta som en ren formalitet. Den försöker förstå vad koden syftar till att garantera, och därefter försöker den förfalska den garantin.
I praktiken brukar det se ut som en blandning av:
- Att läsa den relevanta kodvägen med fullständig förvarkontext, precis som en säkerhetsforskare skulle göra, och leta efter avvikelser mellan avsikt och implementering. Detta inkluderar kommentarer, men modellen tror inte nödvändigtvis på kommentarer, så att lägga till //Halvar says: this is not a bug ovanför din kod förvirrar den inte, om det verkligen finns en bugg.
- Att reducera problemet till den minsta testbara delen (till exempel transformationspipeline kring en enskild indata), så att du kan analysera den utan att resten av systemet står i vägen. I det avseendet extraherar Codex-säkerhet små kodavsnitt och skriver sedan mikrofuzzers för dem.
- Resonemang om begränsningar som gäller för flera transformationer, istället för att behandla varje kontroll separat. I tillämpliga fall kan detta innebära att frågan om tillfredsställelse formaliseras. Med andra ord ger vi modellen tillgång till en Python-miljö med z3-solver, och den är bra på att använda den vid behov, precis som en människa skulle behöva göra när de svarar på ett särskilt komplicerat problem med indatarestriktioner. Det är särskilt användbart för att upptäcka heltalöverskridningar eller liknande fel på icke-standardiserade arkitekturer.
- Testa hypoteser i en sandbox-miljö när det är möjligt, för att skilja mellan ”det här kan vara ett problem” och ”det här är ett problem”. Det finns inget bättre bevis än en fullständig PoC från början till slut, där koden har kompilerats i felsökningsläge.
Detta är den avgörande förändringen: istället för att nöja sig med att konstatera att ”det finns en kontroll”, går systemet vidare till att säga ”invarianten gäller (eller inte), och här är beviset”. Och modellen väljer det bästa verktyget för jobbet.
En rimlig reaktion är att fråga: varför inte göra både och? Börja med en SAST-rapport och använd sedan agenten för att göra en djupare analys.
Det finns tillfällen då förutberäknade resultat är användbara – särskilt för avgränsade, kända buggklasser. Men för en agent som är utformad för att upptäcka och validera sårbarheter i sitt sammanhang leder det till tre förutsägbara fel tillstånd om man börjar med en SAST-rapport.
För det första kan det leda till för tidig avgränsning. En fyndlista är en karta över var ett verktyg redan har tittat. Om du utgår från det kan du riskera att snedvrida systemet så att det lägger oproportionerligt mycket resurser på samma områden, använder samma abstraktioner och förbiser problemklasser som inte passar in i verktygets världsbild.
För det andra kan det leda till implicita bedömningar som är svåra att undvika. Många SAST-resultat kodar in antaganden om sanering, validering eller förtroendegränser. Om dessa antaganden är felaktiga, eller bara ofullständiga, kan det faktum att man införlivar dem i resonemangskedjan få agenten att gå från ”undersök” till ”bekräfta eller avvisa”, vilket inte är vad vi vill att agenten ska göra.
För det tredje kan det göra det svårare att utvärdera resonemangssystemet. Om pipelinen börjar med SAST-utdata blir det svårt att skilja på vad agenten har upptäckt genom sin egen analys och vad den har ärvt från ett annat verktyg. Den här åtskillnaden är viktig om du vill mäta systemets kapacitet på ett korrekt sätt, vilket behövs för att systemet ska förbättras över tid.
Därför grundade vi Codex-säkerhet för att börja där säkerhetsforskningen börjar: med koden och systemets syfte, där validering används för att höja konfidensgränsen innan vi involverar en människa.
SAST-verktyg kan vara utmärkta för det de är avsedda för: att säkerställa efterlevnaden av säkerhetsstandarder för kodning, att identifiera enkla problem i kedjan från källa till mottagare samt att upptäcka kända mönster i stor skala med förutsägbara avvägningar. De kan vara en stark del av en djupgående försvarsstrategi.
Det här inlägget är mer avgränsat: det handlar om varför en agent som är utformad för att analysera beteende och validera resultat inte bör inleda sitt arbete förankrad i en statisk lista över resultat.
Det är också värt att lyfta fram en relaterad begränsning med rent källa-till-mottagare-tänkande: inte alla sårbarheter är ett problem med dataflödet. Många verkliga fel handlar om tillstånd och invarians – kringgående av arbetsflöden, luckor i behörighetshanteringen och fel av typen ”systemet befinner sig i fel tillstånd”. För dessa typer av buggar når ett kontaminerat värde inte en enda ”farlig mottagare”. Risken ligger i vad programmet antar alltid kommer att vara sant.
Vi räknar med att ekosystemet för säkerhetsverktyg kommer att fortsätta att förbättras: statisk analys, fuzzing, körningsskydd och agentbaserade arbetsflöden kommer alla att spela en viktig roll.
Det vi vill att Codex-säkerhet ska vara bra på är just det som kostar säkerhetsteamen mest: att omvandla ”det här ser misstänkt ut” till ”det här är verkligt, så här fungerar det fel, och här är en korrigering som överensstämmer med systemets syfte.”
Om du vill veta mer om hur Codex-säkerhet söker igenom förvar, validerar fynd och föreslår korrigeringar kan du läsa vår dokumentation(öppnas i ett nytt fönster).


