En god kodegjennomgang ser etter mange ting på en gang, og det er lett å glemme noen av dem. Tre smale lesere, hver med sin sjekkliste, glemmer mindre. Men ikke alt skal en leser ta seg av.
Dette er del 2 av 4 i serien Koding med AI. Del 1 handler om arbeidsflyten, del 3 om å skrive testene først og del 4 om feilsøking. Her får du tre lesere for kode du kan laste ned, en liten sjekk i Python som faktisk er kjørt, og en sjekkliste for deg som leder. Er du leder, kan du hoppe rett til det du bør kreve av et utviklerteam.
En skill er en tekstfil med et navn, en beskrivelse av når den skal brukes, og instruksjoner. Modellen ser bare navnet og beskrivelsen til oppgaven passer. Da leser den resten. Det er en prompt du slipper å lime inn hver gang, og som hele teamet kan dele.
En skill er likevel bare tekst. Den ber modellen gjøre noe, og modellen gjør det som regel. «Som regel» er greit for en vurdering. Det er ikke greit for en regel som aldri skal brytes.
Spørsmålet som avgjør er enkelt: kan det avgjøres uten skjønn, og må det skje hver gang? Da er det kode. Krever det at noen vurderer, er det en leser.
| Hva | Hvor det hører hjemme | Hvorfor |
|---|---|---|
| Nøkler og passord i koden | Kode: hook eller CI | Et mønster, og det skal aldri slippe gjennom |
| SQL bygd med tekst | Kode, og sikkerhetsleseren | Den enkle formen er et mønster. Varianter krever øyne |
| Testene består | Kode: CI eller en Stop-hook | Ja eller nei, hver gang |
| Gjør koden det spesifikasjonen sier? | Korrekthetsleseren | Krever at noen leser spesifikasjonen og koden sammen |
| Kan en innlogget bruker misbruke dette? | Sikkerhetsleseren | Krever kjennskap til hvem som kaller koden |
| Skjønner neste person dette? | Lesbarhetsleseren | Smak og kontekst |
| Finnes den nye pakken? | Begge | Et skript kan slå opp navnet. Om det er riktig pakke, må noen vurdere |
En hook er et skript Claude Code kjører automatisk på bestemte punkter, for eksempel rett før en fil endres eller før den sier seg ferdig. Avslutter skriptet med kode 2, stoppes handlingen, og feilmeldinga går tilbake til Claude. Kode 1 stopper ingenting der. Dokumentasjonen sier rett ut at Claude Code regner kode 1 som en feil som ikke blokkerer, og går videre, selv om 1 er den vanlige feilkoden ellers.
Den samme sjekken kan også ligge i en vanlig git-hook før commit, eller i CI, altså den automatiske kjøringen når koden sendes inn. Der stopper alt som ikke er 0, og den virker uansett hvem som skrev koden. Hvorfor en sjekk i kode slår en linje i en instruks, står i del 1.
Fila under er konstruert, med plantede feil: en nøkkel rett i koden, SQL bygd med tekst, en variabel som
aldri brukes, og to ting en maskin ikke kan se. Rabatten skal gjelde fra og med 10 stk, men koden sier
over 10. Og f2 sier ingenting om hva den gjør.
"""Konstruert eksempel med plantede feil. IKKE BRUK DENNE KODEN.
Hører til gribben.no/tech/losninger/kodegjennomgang-med-skills.html.
Feilene er lagt inn med vilje for å vise hva en sjekk i kode finner, og
hva den ikke kan finne fordi det krever en spesifikasjon eller skjønn.
Nøkkelen er ikke ekte.
Spesifikasjon: kunder får 10 prosent rabatt fra og med 10 stk.
"""
import sqlite3
API_NOKKEL = "demo-ikke-ekte-9f8e7d6c5b4a"
def rabatt(antall, pris):
if antall > 10:
return pris * antall * 0.9
return pris * antall
def finn_ordrer(db: sqlite3.Connection, kunde):
hentet = "2026-10-08"
sql = f"SELECT id, sum FROM ordre WHERE kunde = '{kunde}'"
return db.execute(sql).fetchall()
def f2(x):
return [o for o in x if o[1] > 0]
Sjekken er rundt hundre linjer Python, bare standardbiblioteket. Den leser koden som et tre med modulen
ast, ser etter tre mønstre og avslutter med kode 1 hvis den finner noe. Det stopper en
git-hook og et CI-steg. Som Claude Code-hook måtte den avsluttet med 2.
$ python sjekk.py ordre.py ordre.py:12 SIKKERHET hemmelighet i koden: API_NOKKEL ordre.py:22 OPPRYDDING «hentet» får en verdi og brukes aldri ordre.py:23 SIKKERHET SQL bygd med tekst, bruk parametre (?) 3 funn. (exit-kode 1)
Alle tre fanget. Så retter vi bare de tre, og kjører igjen:
$ python sjekk.py ordre_mekanisk_rettet.py Ingen funn. Det betyr bare at disse tre tingene ikke ble funnet. (exit-kode 0)
Ingen funn. Og rabattfeilen står der fortsatt. En kunde som kjøper nøyaktig 10 stk, får ikke rabatten hun har krav på. Ingen mønstersjekk kunne funnet det, for feilen ligger mellom koden og en setning på norsk. Det er jobben til en leser.
"""Samme fil som ordre.py, med bare de tre mekaniske funnene rettet.
Hører til gribben.no/tech/losninger/kodegjennomgang-med-skills.html.
sjekk.py finner ingenting her. Rabattfeilen og det uklare navnet står
fortsatt. Det er derfor «ingen funn» ikke er det samme som godkjent.
Spesifikasjon: kunder får 10 prosent rabatt fra og med 10 stk.
"""
import os
import sqlite3
API_NOKKEL = os.environ.get("API_NOKKEL", "")
def rabatt(antall, pris):
if antall > 10:
return pris * antall * 0.9
return pris * antall
def finn_ordrer(db: sqlite3.Connection, kunde):
sql = "SELECT id, sum FROM ordre WHERE kunde = ?"
return db.execute(sql, (kunde,)).fetchall()
def f2(x):
return [o for o in x if o[1] > 0]
Alle filene ligger i tech/losninger/kode/gjennomgang_demo/: ordre.py, ordre_mekanisk_rettet.py, sjekk.py og pre-commit.txt.
Ved en testkjøring 8. oktober 2026, under arbeidet med sida, fikk de tre skillene den samme rettede fila, hver i en egen Claude-økt uten noe annet å gå på. Svarene under er forkortet og gjenfortalt, med sitat der det står anførselstegn. Kjøringen er ikke gjentatt, de fullstendige svarene er ikke lagret, og svarene blir ikke like neste gang. Les dem som et eksempel på hva slike lesere kan finne, ikke som en måling.
Fant rabattfeilen og merket den må rettes: rabatt(10, 100) gir 1000, «Svaret skulle vært 900». Foreslo en test for 9, 10 og 11 stk. Pekte på flyttall for pengebeløp som verdt å vurdere, og skrev selv at det var en antakelse fordi koden ikke var kjørt. Kunne ikke si om f2 var riktig, fordi den mangler spesifikasjon.
Ingen tilgangskontroll på kundeoppslaget: kommer kundenummeret rett fra en forespørsel, kan hvem som helst lese andres ordrer. Merket bør rettes, «Middels sikker, siden kallstedet ikke er med». Nøkkelen faller stille tilbake til tom tekst hvis den mangler. Bekreftet at SQL-en nå er parametrisert, og sendte rabattfeilen videre til korrekthetsleseren.
f2(x) sier ingenting. rabatt gir totalprisen, ikke rabatten, og navnet lurer leseren. Tallene 10 og 0.9 mangler navn, og med navn på dem «ser du med en gang at > og fra og med ikke sier det samme». API_NOKKEL brukes aldri.
Én ting som ser ut som en overlapp, men ikke er det: lesbarhetsleseren kom også innom rabattgrensa. Den sendte selve feilen videre til korrekthetsleseren, men la til det den andre ikke sa: grunnen til at feilen var lett å gjøre. Tall uten navn. Det er derfor tre smale lesere er bedre enn én bred.
Så sorterer du. Rabattfeilen må rettes. Tilgangskontrollen avhenger av hvem som kaller funksjonen, og det vet du, ikke leseren. Flyttall for penger er et valg. Navnene koster fem minutter. Anthropic advarer selv om at en leser som blir bedt om å finne hull, som regel finner noen, også når arbeidet er godt. Derfor ber skillene om få funn med alvorsgrad, og om å si hva de så etter når de ikke fant noe.
/security-review og gjennomgang i GitHub
Claude Code har to innebygde gjennomganger. /security-review kom 6. august 2025 og ser på
endringene i grenen din mot standardgrenen på origin, etter blant annet injeksjon, svak
innlogging og data på avveie. Den trenger at prosjektet har en origin. /code-review
ser etter feil i logikken i diffen. Anthropic har også en GitHub-handling som legger sikkerhetskommentarer
direkte i en pull request.
To forbehold fra Anthropic selv er verdt å ta med. Hjelpesenteret skriver at de automatiske gjennomgangene skal komme i tillegg til, ikke i stedet for, sikkerhetsrutinene og kodegjennomgangen dere har fra før. Og GitHub-handlingen er ifølge sin egen beskrivelse ikke sikret mot instruksjoner skjult i koden den leser, så den skal bare brukes på pull requests fra folk dere stoler på. Den filtrerer også bort enkelte typer funn som standard, som tjenestenekt og manglende begrensning av antall forespørsler.
En gjennomgang som ikke fant noe, sier bare at den ikke fant det den så etter. Studien til Perry og kollegaene, som står i del 1, viser hvorfor det er farlig: de som brukte AI-assistent, skrev oftere usikker kode og trodde oftere at den var trygg. En tom liste fra en gjennomgang gjør den selvtilliten bare større.
Den andre fella er nyere. Spracklen og kollegaene genererte 576 000 kodeeksempler med 16 språkmodeller og fant at modellene foreslo pakker som ikke finnes, i snitt minst 5,2 prosent av gangene for kommersielle modeller og 21,7 prosent for åpne. Til sammen 205 474 ulike pakkenavn som ikke fantes. Et slikt navn kan hvem som helst registrere og fylle med skadelig kode. Det kalles «slopsquatting». Hver ny pakke må sjekkes av noen som vet hva som var ment.
Du trenger ikke lese koden selv. Du trenger at disse ni tingene er sanne, og at noen kan vise deg det. Lista samler lederspørsmålene fra alle fire delene i serien.
Mer om lederens rolle står i AI for ledere.
Finner spesifikasjonen først, og ser etter grenseverdier, feilhåndtering, tall, tid og tester som er endret for å passe koden. Last ned SKILL.md
Leser som en angriper: hemmeligheter, injeksjon, tilgang, personopplysninger og pakker som ikke finnes. Sier fra når et funn heller burde vært en sjekk i kode. Last ned SKILL.md
Leser som den neste som skal endre koden: navn, størrelse, kommentarer, magiske verdier og prosjektets egne konvensjoner. Høyst fem funn. Last ned SKILL.md
En skill er instrukser, og noen har med skript eller gir seg selv tilgang til verktøy. Dokumentasjonen ber deg se over hva skills i et repo får lov til før du kjører Claude Code der. Disse tre er rene tekstfiler uten skript. Les dem likevel, og endre det som ikke passer.
Slik legger du dem inn i Claude Code eller claude.ai: se skillhylla. Vil du bare prøve, åpne en fulltekst under, trykk Kopier, og lim den inn som første melding før koden. Det virker i hvilken som helst chat.
--- name: kodegjennomgang-korrekthet description: Leser en kodeendring og sjekker at den gjør det den skal: grenseverdier, feilhåndtering og tester. Bruk ved review av en diff eller pull request, eller før en commit. --- # Kodegjennomgang: korrekthet Du er én av tre lesere. Din jobb er å finne ut om koden gjør det den skal. Sikkerhet og lesbarhet har egne lesere, så hold deg til korrekthet. Brukeren eier koden og bestemmer hva som rettes. Les koden, diffen og testene. Ikke endre filer, og ikke kjør noe som endrer noe. Testene kan du kjøre hvis brukeren ber om det. ## 1. Finn ut hva koden skal gjøre Les først det som sier hva endringen skal oppnå: oppgaven, en spesifikasjon, kommentarer, testene eller commit-meldingen. Skriv én setning om hva du forstår at endringen skal gjøre. Finner du ingen spesifikasjon, si det rett ut, og spør brukeren før du fortsetter. Uten den kan du bare finne krasj, ikke feil. ## 2. Se etter dette - **Grenseverdier:** null, tom liste, én verdi, den største verdien, negative tall. Sammenlign `<` og `<=` mot det spesifikasjonen sier, ord for ord. «Fra og med» og «over» er ikke det samme. - **Feilhåndtering:** unntak som lekker ut med feil type, feil som svelges stille, returverdier som kan være `None` uten at noen sjekker. - **Tall:** flyttall for penger, avrunding som gjøres i feil rekkefølge, heltallsdivisjon, enheter som blandes. - **Tid og tekst:** tidssoner, datoformat, tegnkoding, store og små bokstaver. - **Tilstand:** verdier som endres et sted og leses et annet, rekkefølge som betyr noe, kode som kjøres to ganger. - **Tester:** dekker testene det som ble endret? Er det en test som ville feilet før endringen? Har noen endret en test slik at den passer koden i stedet for spesifikasjonen? Si fra med en gang hvis du ser det. ## 3. Svar slik For hvert funn: 1. Fil og linje, og et kort sitat fra koden. 2. Hva som er feil, sett opp mot spesifikasjonen. 3. Et konkret eksempel på inndata som gir feil svar, og hva svaret burde vært. 4. En test som ville fanget det, skrevet ut. 5. Alvor: **må rettes**, **bør rettes** eller **verdt å vurdere**. Sorter etter alvor. Ta med høyst sju funn. Har du flere, si hvor mange og hvor de ligger. ## Husk - Rapporter bare det som påvirker om koden gjør det den skal. Stil og navn er ikke din jobb. - Ikke finn feil for å se grundig ut. Er en del av koden riktig, si det. - Skill det du har sjekket fra det du antar. Har du ikke kjørt koden, skriv det. - Fant du ingenting, skriv hva du så etter. Ingen funn er ikke en godkjenning, og brukeren skal ikke lese det slik. - Ikke skriv om koden. Foreslå endringen, og la brukeren gjøre den.
--- name: kodegjennomgang-sikkerhet description: Leser en kodeendring som en angriper: hemmeligheter i koden, injeksjon, svak tilgangskontroll og pakker som ikke finnes. Bruk ved sikkerhetsgjennomgang av en diff eller pull request. --- # Kodegjennomgang: sikkerhet Du er én av tre lesere. Din jobb er å lese endringen slik en angriper ville gjort, og finne det som kan misbrukes. Korrekthet og lesbarhet har egne lesere. Brukeren eier koden og bestemmer hva som rettes. Les koden og diffen. Ikke endre filer, ikke installer noe, og ikke send noe ut av maskinen. Gjennomgangen din erstatter ikke en sikkerhetsskanner, en sjekk for hemmeligheter eller en gjennomgang gjort av et menneske som kan faget. ## 1. Forstå hva som står på spill Skriv én setning om hva endringen gjør, og hvilke data og hvilke brukere den rører. Spør hvis du ikke vet om koden kjører på en server, hos en bruker eller bare lokalt. Det endrer hva som er alvorlig. ## 2. Se etter dette - **Hemmeligheter:** nøkler, passord og tokens i koden, i konfigurasjon som sjekkes inn, i testdata og i logger. - **Injeksjon:** SQL bygd med tekst, kommandoer med `shell=True` eller tekst limt inn i en kommando, filstier fra brukeren (`../`), HTML som ikke escapes, `pickle` eller `yaml.load` på data utenfra. - **Tilgang:** kan hvem som helst kalle dette? Sjekkes det at brukeren eier det den ber om? Er standardvalget åpent eller lukket? - **Avhengigheter:** for hver ny pakke, sjekk at den finnes i det offisielle registeret, at navnet er stavet riktig, og at det er den pakken som var ment. Kodemodeller foreslår pakker som ikke finnes, og noen registrerer slike navn med skadelig innhold. Se også etter versjoner som ikke er låst. - **Personopplysninger:** navn, e-post, telefon, fødselsnummer eller helse i logger, feilmeldinger eller analyse. Logges mer enn nødvendig? - **Tekst til en språkmodell:** sendes data utenfra rett inn i en prompt, kan det inneholde instruksjoner. Kan modellen da gjøre noe den ikke burde? ## 3. Svar slik For hvert funn: 1. Fil og linje, og et kort sitat. 2. Hvordan det kan misbrukes, i én eller to setninger. Konkret, ikke dramatisk. 3. Hva som må til for å rette det. 4. Alvor: **må rettes før commit**, **bør rettes** eller **verdt å vurdere**, og hvor sikker du er. 5. Om funnet heller burde vært en sjekk i kode som kjøres hver gang (en hook, et CI-steg eller en skanner), fordi det ikke krever skjønn. Sorter etter alvor. Ta med høyst sju funn. ## Husk - Ikke rapporter teoretiske problemer uten en vei inn. Si hva en angriper må ha tilgang til. - Er du usikker på om en pakke finnes, skriv at den må sjekkes. Ikke gjett. - Gjenta aldri en hemmelighet du finner i svaret ditt. Skriv hvor den står. - Fant du ingenting, skriv hva du så etter. Ingen funn er ikke en godkjenning, og en AI-gjennomgang erstatter ikke en sikkerhetsgjennomgang. - Er koden fra jobb, minn brukeren på å bruke et verktøy virksomheten har godkjent.
--- name: kodegjennomgang-lesbarhet description: Leser en kodeendring som den neste som skal endre den: uklare navn, kode som gjør for mye og brudd på prosjektets konvensjoner. Bruk ved review av en diff eller pull request. --- # Kodegjennomgang: lesbarhet Du er én av tre lesere. Du leser endringen som den som skal endre koden om et halvt år, uten å ha vært med da den ble skrevet. Korrekthet og sikkerhet har egne lesere. Brukeren eier koden og bestemmer hva som rettes. Les koden og diffen. Ikke endre filer, og ikke kjør noe. ## 1. Les prosjektets regler først Se etter `CLAUDE.md`, en stilguide, en linter-konfigurasjon eller tilsvarende. Prosjektets egne konvensjoner går foran din smak. Det som en linter eller formaterer allerede sjekker, skal du ikke gjenta. Si heller fra hvis den ikke kjøres. ## 2. Se etter dette - **Navn:** sier navnet på en funksjon eller variabel hva den er eller gjør? `f2(x)` gjør ikke det. - **Størrelse:** gjør en funksjon mer enn én ting? Må du holde mange ting i hodet samtidig for å lese den? - **Kommentarer:** forklarer de hvorfor koden er slik, eller gjentar de bare hva den gjør? Mangler det en forklaring der et valg ikke er opplagt? - **Magiske verdier:** tall og tekst uten navn, som `0.9` eller `"2026-10-08"`, der leseren må gjette hva de betyr. - **Død kode:** variabler, parametre, importer og grener som aldri brukes. - **Konsistens:** gjør den nye koden ting på samme måte som resten av prosjektet? ## 3. Svar slik Ta med høyst fem funn, de som gjør mest for den neste leseren. For hvert funn: 1. Fil og linje, og et kort sitat. 2. Hva som er vanskelig å forstå, og for hvem. 3. Et konkret forslag, for eksempel et bedre navn. Avslutt med én setning om hva som er bra med endringen, hvis noe er det. ## Husk - Smak er ikke et funn. Er det bare en annen måte å skrive det på, la det ligge. - Ikke skriv om koden. Foreslå, og la brukeren velge. - Bruk ikke mer av brukerens tid enn funnene er verdt. Fem gode funn er bedre enn tjue små. - Fant du ingenting som er verdt å endre, si det.
Kjør den med python sjekk.py fil.py. Filer som ikke slutter på .py, hoppes over, og en fil den ikke klarer å tolke, teller som et funn. Den er bevisst enkel og erstatter ikke en ordentlig skanner for hemmeligheter.
Vil du at den skal stoppe en commit, legg sjekk.py i tools/ og lagre denne fila som .git/hooks/pre-commit, uten filendelse. Den sender bare Python-filene i commiten videre. Merk at sjekken leser filene slik de ligger i mappa, ikke slik de er lagt til med git add.
#!/bin/sh # Git-hook som kjører sjekk.py på Python-filene i commiten. # Hører til gribben.no/tech/losninger/kodegjennomgang-med-skills.html. # # Lagre fila som .git/hooks/pre-commit (uten .txt), og legg sjekk.py i tools/. # På Mac og Linux må den gjøres kjørbar: chmod +x .git/hooks/pre-commit # Git stopper commiten når denne avslutter med noe annet enn 0. # # Merk: sjekk.py leser filene slik de ligger i arbeidskopien, ikke slik de # er lagt til med git add. Har du endret en fil etter git add, sjekkes den # nyeste versjonen. Filnavn med mellomrom støttes ikke. filer=$(git diff --cached --name-only --diff-filter=ACM -- '*.py') [ -z "$filer" ] && exit 0 python tools/sjekk.py $filer
Prøvd i et tomt testrepo 9. oktober 2026: commit med ordre.py ble stoppet med de tre funnene over, commit med den rettede fila gikk gjennom.
"""Mekanisk sjekk av Python-filer. Bare standardbiblioteket.
python sjekk.py ordre.py [flere filer ...]
Finner tre ting som ikke krever skjønn:
- hemmeligheter skrevet rett i koden (navn som nøkkel, token, passord)
- SQL bygd med tekst (f-streng, + eller .format rundt SQL-ord)
- variabler som får en verdi og aldri brukes
Avslutter med kode 1 hvis den finner noe. I en git-hook før commit og i CI
stopper det, for der stopper alt som ikke er 0. Som Claude Code-hook ville
kode 1 IKKE stoppet noe: der blokkerer bare kode 2, og kode 1 regnes som en
feil som ikke blokkerer (code.claude.com/docs/en/hooks, lest 9. oktober
2026). Skriptet er laget for git og CI.
Det er hele poenget: en regel som MÅ holde, skal ikke avhenge av at noen
husker å be om den. Det som krever skjønn, som om koden gjør det
spesifikasjonen sier, er jobben til skillene.
Filer som ikke slutter på .py, hoppes over. En .py-fil som ikke kan tolkes,
teller som et funn, så en commit ikke slipper gjennom fordi sjekken krasjet.
Sjekken er bevisst enkel og vil både bomme og ta feil. Den erstatter ikke
en ordentlig skanner for hemmeligheter eller en linter. Den viser prinsippet.
"""
import ast
import pathlib
import re
import sys
HEMMELIG_NAVN = re.compile(r"(n[oø]kkel|key|secret|hemmelig|passord|password|token)", re.I)
SQL_ORD = re.compile(r"\b(SELECT|INSERT|UPDATE|DELETE)\b", re.I)
def tekst_i(node) -> str:
"""Den bokstavelige teksten i en streng-node, også delene av en f-streng."""
return " ".join(n.value for n in ast.walk(node)
if isinstance(n, ast.Constant) and isinstance(n.value, str))
def er_bygd_tekst(node) -> bool:
if isinstance(node, ast.JoinedStr):
return any(isinstance(v, ast.FormattedValue) for v in node.values)
if isinstance(node, ast.BinOp) and isinstance(node.op, (ast.Add, ast.Mod)):
return True
if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
return node.func.attr == "format"
return False
def sjekk_fil(sti: pathlib.Path):
# utf-8-sig tåler filer som starter med et BOM-merke, slik noen editorer lagrer dem.
try:
tre = ast.parse(sti.read_text(encoding="utf-8-sig"), filename=str(sti))
except (SyntaxError, UnicodeDecodeError, ValueError) as feil:
return [(getattr(feil, "lineno", None) or 0, "FEIL", f"kunne ikke tolkes: {type(feil).__name__}")]
funn = []
for node in ast.walk(tre):
# Hemmeligheter: et navn som ser hemmelig ut, satt til en lang tekst
if isinstance(node, ast.Assign) and isinstance(node.value, ast.Constant) \
and isinstance(node.value.value, str) and len(node.value.value) >= 8:
for mål in node.targets:
if isinstance(mål, ast.Name) and HEMMELIG_NAVN.search(mål.id):
funn.append((node.lineno, "SIKKERHET", f"hemmelighet i koden: {mål.id}"))
# SQL bygd med tekst
if er_bygd_tekst(node) and SQL_ORD.search(tekst_i(node)):
funn.append((node.lineno, "SIKKERHET", "SQL bygd med tekst, bruk parametre (?)"))
# Ubrukte variabler, én funksjon om gangen. En indre funksjon blir gått
# gjennom både alene og som del av den ytre, derfor set() nederst.
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
satt, brukt = {}, set()
for n in ast.walk(node):
if isinstance(n, ast.Name):
if isinstance(n.ctx, ast.Store):
satt.setdefault(n.id, n.lineno)
else:
brukt.add(n.id)
for navn, linje in satt.items():
if navn not in brukt and not navn.startswith("_"):
funn.append((linje, "OPPRYDDING", f"«{navn}» får en verdi og brukes aldri"))
return sorted(set(funn))
def main(filer) -> int:
totalt = 0
for f in filer:
sti = pathlib.Path(f)
if sti.suffix != ".py":
print(f"{sti.name}: hoppet over, ikke en Python-fil")
continue
for linje, type_, tekst in sjekk_fil(sti):
print(f"{sti.name}:{linje:<4} {type_:<11} {tekst}")
totalt += 1
print(f"{totalt} funn." if totalt else "Ingen funn. Det betyr bare at disse tre tingene ikke ble funnet.")
return 1 if totalt else 0
if __name__ == "__main__":
sys.stdout.reconfigure(encoding="utf-8")
sys.exit(main(sys.argv[1:]))
Kjørt på nytt med Python 3.13, 9. oktober 2026. Begge utskriftene på sida er de faktiske.
Skillene er nye, og ingen har målt hvor godt de virker utover testkjøringen over. Prøv dem på noe lite først.
Oppdatert oktober 2026.
Claude Code-dokumentasjonen, lest 8. oktober 2026: Commands (/security-review sammenligner med standardgrenen på origin og trenger en origin, /code-review; lest på nytt 9. oktober 2026), Hooks og Hooks reference (kode 2 stopper handlingen, kode 1 blokkerer ikke; lest på nytt 9. oktober 2026), Best practices (hooks som garanti, lesere som alltid finner noe), Skills (plassering, se over skills i et repo).
Anthropic, «Automate security reviews with Claude Code», 6. august 2025. Claude hjelpesenter, Automated security reviews in Claude Code (oppdatert 16. mars 2026: «complement, not replace») og Creating custom skills. GitHub, anthropics/claude-code-security-review (forbeholdet om skjulte instruksjoner og filtrerte funn).
Spracklen m.fl., «We Have a Package for You!», USENIX Security 2025.
Lesernes svar er fra én testkjøring 8. oktober 2026 under arbeidet med sida, forkortet, ikke gjentatt, og blir ikke like neste gang. Alt i eksempelet er konstruert, og nøkkelen er ikke ekte.