En utvikler i Trondheim kjører npm install fredag ettermiddag. Pakken heter nesten det samme som en hun bruker hver dag, bare med en bokstav byttet ut. Et postinstall-skript starter i bakgrunnen, henter ut miljøvariabler og sender dem til en server i et annet land. Ingen advarsel dukker opp. npm audit viser null kjente sårbarheter, fordi pakken aldri har vært registrert som sårbar. Den er ikke sårbar. Den er ondsinnet fra første linje.
Dette scenarioet er ikke hypotetisk. Sonatype logget 704 102 skadelige åpen kildekode-pakker i 2024, en økning på 156 prosent fra året før, og over halvparten av uovervåkede registre i undersøkelsen deres hadde allerede lagret minst én skadelig pakke i hurtigbufferen. I denne guiden bygger du en konkret deteksjonspipeline med Semgrep Supply Chain, egne regler og en CI/CD-jobb som stopper skadelige npm- og Python-pakker før de når produksjon. Du får 12 steg, seks kodeblokker, et komplett prosjekt du kan kopiere, og en feilsøkingsliste for når noe går galt.
Hva er et forsyningskjedeangrep via npm og Python-pakker?
Et forsyningskjedeangrep i denne sammenhengen betyr at en angriper plasserer skadelig kode inne i en avhengighet du laster ned frivillig, i stedet for å angripe applikasjonen din direkte. Koden kommer inn via npm install, pip install eller en transitiv avhengighet du aldri har hørt om, men som en av pakkene dine krever. Fordi koden kjører med samme rettigheter som bygget eller applikasjonen selv, kan den lese miljøvariabler, SSH-nøkler, CI/CD-hemmeligheter og kildekode uten å røre en enkelt linje i din egen kodebase.
Typiske angrepsvektorer faller i fire kategorier. Typosquatting registrerer en pakke med et navn som ligger tett opp mot et populært bibliotek. Kontoovertakelse skjer når en angriper phisher eller gjetter innloggingen til en pakke-vedlikeholder og publiserer en forgiftet versjon av et bibliotek millioner allerede bruker. Dependency confusion utnytter at et internt pakkenavn også finnes offentlig, slik at byggesystemet laster ned den offentlige, skadelige versjonen i stedet for den interne. Og skadelige installasjonsskript, altså postinstall eller preinstall i package.json, kjører automatisk uten at noen trykker på noe som helst.
Det som gjør denne trusselen vanskelig å fange med vanlige verktøy, er at npm audit og lignende CVE-baserte skannere kun flagger kjente, rapporterte sårbarheter. En fersk, ondsinnet pakke har ingen CVE fordi den aldri har blitt klassifisert som sårbar. Den er designet for å være skadelig fra dag én, og den forsvinner ofte fra registeret etter noen timer eller dager, lenge etter at den allerede er lastet ned av byggejobber verden over.
De fire angrepsmønstrene i praksis
Typosquatting er den enkleste varianten å forstå, men ikke den enkleste å stoppe manuelt. En angriper registrerer reqeusts i stedet for requests, eller lodahs i stedet for lodash, og venter. Hver gang en utvikler skriver fort eller kopierer en kommando fra et minne som svikter litt, lastes den gale pakken ned. Registrene hos npm og PyPI gjør lite for å hindre registrering av navn som ligger tett opp mot eksisterende pakker, så ansvaret for å fange dette flyttes til verktøyene i byggejobben.
Kontoovertakelse er alvorligere, fordi den forgifter en pakke millioner allerede stoler på og bruker daglig. Mønsteret har gjentatt seg i økosystemet flere ganger de siste årene: en vedlikeholder med tilgang til en mye brukt pakke blir phishet eller svekket på en annen måte, og en ny, ondsinnet versjon publiseres under det legitime pakkenavnet. Fordi versjonsøkningen ser normal ut i en changelog, går den ofte gjennom automatiserte oppdateringer uten at noen stiller spørsmål.
Dependency confusion er det mest strukturelle problemet av de fire, fordi det utnytter selve måten pakkeoppløsning fungerer. Hvis bedriften din har en intern pakke kalt intern-auth-lib som kun ligger i et privat register, men ingen har registrert navnet offentlig på npm eller PyPI, kan en angriper registrere nøyaktig det navnet offentlig med en høyere versjonsnummer. Mange pakkeløsere prioriterer høyeste versjonsnummer uavhengig av kilde, og laster da ned den offentlige, skadelige versjonen i stedet for den interne.
Skadelige installasjonsskript er mekanismen de tre andre mønstrene ofte bruker for å faktisk gjøre noe skadelig. Et postinstall-felt i package.json kjøres automatisk av npm rett etter nedlasting, uten at noen bekrefter eller godkjenner det. Dette er den samme mekanismen som legitime pakker bruker til å kompilere native tillegg, noe som gjør det vanskelig å blokkere helt uten å ødelegge legitime byggeprosesser.
Tallene bak trusselen: hvorfor Norden bør bry seg nå
Omfanget av økosystemet gjør problemet strukturelt umulig å løse manuelt. Ifølge Sonatypes årlige rapport om programvareforsyningskjeden økte forbruket av Python-pakker med 87 prosent fra året før, til over 530 milliarder pakkeforespørsler. JavaScript-økosystemet vokste nesten like mye: npm-nedlastinger steg 70 prosent til 4,5 billioner forespørsler, ifølge samme kilde, omtalt også i Sonatypes pressemelding om rapporten. Med den slags volum blir det statistisk sikkert at noen av pakkene som flyter gjennom byggejobbene dine, er ondsinnet laget.
Sonatype fant også at rundt 80 prosent av applikasjonsavhengigheter forblir uoppgraderte i mer enn ett år, selv når 95 prosent av de sårbare avhengighetene faktisk har en trygg, fikset versjon tilgjengelig. Teamene vet altså ofte at noe bør oppgraderes, men gjør det ikke fordi oppgraderingsarbeidet konkurrerer med alt annet i backloggen. Det samme mønsteret gjelder skadelig kode: en pakke som en gang ble markert som trygg, blir sjelden skannet på nytt etter at den er lagt til, selv om kontoen til vedlikeholderen senere blir kompromittert.
For norske og nordiske utviklingsmiljøer kommer dette på toppen av et trusselbilde som allerede er under press. FM CyberSecuritys oversikt over cyberangrep har i 2026 dokumentert både løsepengevirus mot norske virksomheter og tjenestenektangrep mot offentlig infrastruktur. Et ekstra lag med automatisert deteksjon i CI/CD-pipelinen er derfor ikke et nice-to-have, det er en forutsetning for å holde tritt med hvor fort nye pakker publiseres og hvor fort de kan bli forgiftet igjen.
Regelverket legger også et ekstra press på dokumentasjon. Digitalsikkerhetsloven, som gjennomfører NIS2 i norsk rett, pålegger mange mellomstore og store virksomheter å varsle myndighetene innen strenge tidsfrister etter en betydelig sikkerhetshendelse. En hendelse som starter med en skadelig avhengighet i en byggejobb, er fortsatt en sikkerhetshendelse i lovens forstand, selv om angrepet aldri rører produksjonsdatabasen direkte. Uten en SBOM og en skannelogg å vise til, blir det langt vanskeligere å dokumentere omfanget innen fristen loven setter.
Forutsetninger: verktøy, kontoer og versjoner
Du trenger ikke et stort sikkerhetsteam for å følge denne guiden, men du trenger disse verktøyene installert og klare før du starter på steg 1:
- Node.js 22 LTS eller nyere, med npm 10.x
- Python 3.12 eller nyere, med pip 24.x
- Semgrep CLI, versjon 1.181.0 (siste stabile versjon per oktober 2026, installeres med
pip install semgrep) - Git 2.40 eller nyere
- En GitHub-konto med Actions aktivert for repositoriet du vil beskytte
- Gratis konto på Semgrep AppSec Platform for å aktivere Supply Chain-reglene og Semgrep Guardian
- Docker 24 eller nyere, valgfritt, til lokal testing av karanteneskriptet
- Omtrent 60 minutter sammenhengende tid for å komme gjennom alle 12 stegene
Semgrep har hatt høyt tempo på utgivelser den siste måneden. Ifølge Semgreps offisielle versjonsnotater kom versjon 1.178.0 den 23. september 2026, 1.179.0 den 1. oktober, 1.180.0 den 7. oktober og 1.181.0 i midten av oktober, alle innenfor samme tomåneders vindu. Det betyr at hvis du følger denne guiden med en eldre versjon installert, bør du kjøre pip install --upgrade semgrep før du går videre, spesielt fordi 1.181.0 fikser hvordan Supply Chain-skanning leser requirements.txt-filer.
Hva skiller Semgrep Supply Chain fra vanlig SCA-skanning?
Tradisjonell Software Composition Analysis (SCA) lister opp alle avhengighetene dine og sjekker hver enkelt mot en database med kjente sårbarheter. Resultatet er ofte en lang liste med funn der nesten ingen av dem faktisk utgjør en risiko for applikasjonen din, fordi den sårbare funksjonen aldri kalles. Dette kalles varseltretthet, og det er en av hovedgrunnene til at mange utviklingsteam etter en stund slutter å lese skannerapportene i det hele tatt.
Semgreps reachability-analyse bygger en forenklet kallgraf av koden din og sjekker om den sårbare eller skadelige koden faktisk ligger på en sti som applikasjonen din kan nå under kjøring. En sårbarhet i en loggingsfunksjon du aldri bruker, rangeres lavere enn en sårbarhet i en funksjon som kalles ved hver innkommende forespørsel. For skadelig kode fungerer logikken omvendt og strengere: et postinstall-skript regnes alltid som reachable, fordi det kjører automatisk uansett hvordan applikasjonen din bruker resten av pakken. Det er nettopp denne kombinasjonen, kjent malware-database pluss reachability for sårbarheter pluss automatisk kjøring-logikk for installasjonsskript, som gjør Supply Chain-modulen til noe mer enn en vanlig CVE-liste.
Steg 1-3: Installer Semgrep og skann prosjektet for første gang
Start med et rent testprosjekt slik at du ser hvordan Semgrep reagerer før du peker verktøyet mot noe som faktisk er i produksjon.
- Steg 1: Installer Semgrep CLI. Kjør installasjonskommandoen og bekreft versjonen.
- Steg 2: Logg inn mot Semgrep AppSec Platform. Dette aktiverer Supply Chain-reglene, som krever en kontotilknytning for å hente reachability-data (om den sårbare eller skadelige koden faktisk kan nås fra din egen kode).
- Steg 3: Kjør første skanning. Dette gir deg en baseline du kan sammenligne senere skanninger mot.
pip install semgrep
semgrep --version
# Forventet: 1.181.0 eller nyere
semgrep login
# Åpner nettleseren for autentisering mot Semgrep AppSec Platform
cd mitt-prosjekt
semgrep ci --supply-chain
Kommandoen semgrep ci --supply-chain skiller seg fra en vanlig semgrep scan ved at den også vurderer om en sårbar eller skadelig funksjon faktisk kalles fra koden din, ikke bare om pakken ligger i avhengighetstreet. En pakke med skadelig kode i en funksjon du aldri importerer, får lavere prioritet enn en som faktisk kjører ved hver oppstart. Det sparer deg fra å drukne i varsler om kode som i praksis aldri utføres.
Den første skanningen på et prosjekt med noen hundre avhengigheter tar typisk mellom to og fem minutter, siden Semgrep må laste ned og bygge en lokal kopi av Supply Chain-databasen før den kan sammenligne mot den. Senere skanninger går raskere fordi databasen allerede ligger i hurtigbufferen lokalt. Lagre output fra denne første kjøringen i et eget dokument. Den fungerer som en referanse hvis en kollega spør hvorfor en bestemt pakke plutselig ble flagget etter en oppgradering av Semgrep selv, i stedet for etter en endring i avhengighetene.
Steg 4-6: Lås avhengighetene og blokker farlige installasjonsskript
Deteksjon er bare halvparten av jobben. Den andre halvparten er å fjerne de vanligste åpningene angripere bruker for å komme forbi deteksjon i utgangspunktet.
- Steg 4: Bytt fra
npm installtilnpm cii alle byggesteg.npm ciinstallerer eksakt det som står i låsfilen og feiler hvispackage.jsonogpackage-lock.jsoner ute av synk, i stedet for å stille oppgradere avhengigheter uten varsel. - Steg 5: Slå av automatiske installasjonsskript som standard. De fleste skadelige npm-pakker bruker
postinstallfor å kjøre koden sin umiddelbart etter nedlasting. - Steg 6: Whitelist kun de pakkene som faktisk trenger et byggeskript. Native moduler som krever kompilering er legitime unntak, men listen bør være kort og gjennomgått manuelt.
# package.json
{
"scripts": {
"preinstall": "node scripts/verify-lockfile.js"
},
"config": {
"ignore-scripts": true
}
}
# Kjør installasjon uten automatiske skript
npm ci --ignore-scripts
# Kjør kun skript for eksplisitt godkjente pakker
npm rebuild sqlite3 --build-from-source
På Python-siden finnes det ikke et eksakt motstykke til postinstall, men setup.py kan kjøre arbitrær kode ved installasjon. Løsningen der er å foretrekke pakker som distribueres som wheels (.whl) i stedet for kildedistribusjon, siden en wheel allerede er bygget og ikke kjører setup.py ved installasjon. Legg --only-binary=:all: til pip install-kommandoen i CI for å tvinge dette.
Den vanskeligste balansegangen i dette steget er at --ignore-scripts også blokkerer legitime byggeskript for native moduler, som database-drivere, bildebehandling og kryptografibiblioteker som kompilerer C- eller Rust-kode lokalt. Løsningen er ikke å gi opp og tillate alle skript igjen, men å bygge en kort, eksplisitt liste over pakker som faktisk trenger det, og kjøre npm rebuild <pakkenavn> kun for dem etter at hovedinstallasjonen er ferdig. Hold listen i versjonskontroll, slik at en endring i den krever samme kodegjennomgang som resten av byggekonfigurasjonen.
Steg 7-9: Skriv egne Semgrep-regler for skadelig kode
Semgreps innebygde Supply Chain-regler dekker kjente mønstre, men de kan ikke forutse alt. Egne regler lar deg fange mønstre som er spesifikke for din stack, for eksempel kall til miljøvariabler kombinert med utgående nettverkstrafikk i en installasjonskontekst.
- Steg 7: Lag en mappe for egne regler. Hold dem separat fra Semgreps standardregler slik at de overlever oppgraderinger.
- Steg 8: Skriv en regel som fanger postinstall-skript som eksfiltrerer miljøvariabler. Dette er det klareste signalet på en informasjonsstjeler forkledd som en nyttig pakke.
- Steg 9: Test regelen mot en bevisst sårbar testfil før du stoler på den i CI.
# custom-rules/suspicious-postinstall.yaml
rules:
- id: postinstall-exfiltrates-env
languages: [javascript]
severity: ERROR
message: >
Installasjonsskript leser process.env og sender data over nettverket.
Dette er et typisk mønster for informasjonsstjelere i npm-pakker.
patterns:
- pattern-either:
- pattern: $FETCH(... , { body: $BODY, ... })
- pattern: require('https').request(...)
- pattern-inside: |
process.env
metadata:
category: supply-chain
confidence: HIGH
Kjør egne regler sammen med standardreglene med semgrep scan --config=auto --config=custom-rules/. Begynn strengt. Det er bedre å justere ned en regel som gir falske positiver enn å starte løst og aldri fange det første reelle angrepet.
Egne regler bør testes mot et bevisst sårbart eksempel før de slås på i CI, akkurat som du ville testet en enhetstest mot et kjent utfall. Lag en liten testfil med et postinstall-skript som faktisk leser process.env og sender det over nettverket, og bekreft at regelen treffer før du legger den inn i hovedpipelinen. Lag også en testfil som ser mistenkelig ut, men som er trygg, for eksempel et skript som leser en miljøvariabel og logger den lokalt uten noe nettverkskall, og bekreft at regelen ikke treffer der. Denne to-sidige testingen er det som skiller en regel som faktisk blir brukt over tid, fra en regel som blir deaktivert etter den første falske positive.
Steg 10-12: Bygg CI/CD-pipelinen og karantenerutinen
Nå kobler du sammen alt du har satt opp, slik at hver pull request og hver avhengighetsoppdatering går gjennom samme kontroll automatisk, uten at noen må huske å kjøre noe manuelt.
- Steg 10: Legg Semgrep-skanningen inn i GitHub Actions. Jobben kjører ved hver pull request og blokkerer sammenslåing hvis en skadelig pakke flagges.
- Steg 11: Generer en SBOM (Software Bill of Materials) for hver bygg. Dette gir deg et nøyaktig øyeblikksbilde av hva som faktisk gikk inn i produksjonsbygget, nyttig både for revisjon og for hendelsesrespons senere.
- Steg 12: Bygg en karanterutine som automatisk fjerner en flagget pakke fra låsfilen og åpner en pull request med forklaring, i stedet for å bare stoppe bygget og la en utvikler grave i loggen selv.
# .github/workflows/supply-chain.yml
name: Supply Chain Guard
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Installer Semgrep
run: pip install semgrep==1.181.0
- name: Kjør Supply Chain-skanning
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
run: semgrep ci --supply-chain --config=custom-rules/
- name: Generer SBOM
run: |
pip install cyclonedx-bom
cyclonedx-py requirements -o sbom/python-sbom.json
npx @cyclonedx/cyclonedx-npm --output-file sbom/npm-sbom.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom/
Legg merke til at skanningen kjører med SEMGREP_APP_TOKEN som en hemmelighet, ikke som klartekst. Semgrep Guardian, fra versjon 2.5.4 og nyere, kan i tillegg oppdatere sin egen binærfil automatisk og kryptografisk verifisere at binærfilen faktisk kommer fra Semgreps egen byggepipeline, noe som lukker et angrepsvindu der selve sikkerhetsverktøyet kunne vært byttet ut med en forgiftet versjon.
Branch protection-reglene i GitHub må også peke på denne jobben for at den faktisk skal bety noe. Gå til repositoriets innstillinger, aktiver “Require status checks to pass before merging” og velg Supply Chain Guard-jobben som obligatorisk. Uten dette steget kan en utvikler fortsatt slå sammen en pull request manuelt selv om skanningen feilet, og hele øvelsen blir frivillig i stedet for en reell barriere.
#!/bin/bash
# scripts/quarantine.sh
# Fjerner en flagget pakke og åpner en pull request med forklaring
PACKAGE=$1
REASON=$2
npm uninstall "$PACKAGE" --save
git checkout -b "quarantine/$PACKAGE-$(date +%s)"
git add package.json package-lock.json
git commit -m "Karantene: fjernet $PACKAGE ($REASON)"
git push origin HEAD
gh pr create --title "Karantene: $PACKAGE flagget av Supply Chain Guard" \
--body "Automatisk fjernet fordi: $REASON. Se Semgrep-rapport i CI-loggen."
Komplett arbeidseksempel: hele prosjektet satt sammen
Filstruktur
Når alle 12 stegene er gjennomført, bør repositoriet ditt se ut som dette. Hvert element i treet tilsvarer en kodeblokk du allerede har satt opp over.
skadelig-pakke-vakt/
├── package.json
├── package-lock.json
├── requirements.txt
├── custom-rules/
│ └── suspicious-postinstall.yaml
├── scripts/
│ └── quarantine.sh
├── sbom/
│ ├── npm-sbom.json
│ └── python-sbom.json
└── .github/
└── workflows/
└── supply-chain.yml
Hvordan delene snakker sammen
En utvikler åpner en pull request som legger til en ny avhengighet. GitHub Actions trigger workflowen i .github/workflows/supply-chain.yml, som installerer Semgrep 1.181.0, kjører både standardreglene og de egendefinerte reglene fra custom-rules/, og genererer en SBOM for både npm- og Python-avhengigheter. Hvis skanningen finner et treff, feiler bygget, og en utvikler eller en automatisert jobb kan kjøre scripts/quarantine.sh for å fjerne pakken og åpne en ny pull request med forklaringen ferdig skrevet. SBOM-filene lastes opp som et artefakt, slik at dere har et nøyaktig øyeblikksbilde av avhengighetstreet for hver bygg, uavhengig av om skanningen fant noe eller ikke.
Før du stoler på pipelinen i et ekte repositorium, er det verdt å teste hele kjeden lokalt i en container som ligner CI-miljøet. Dette fanger miljøforskjeller, som en manglende avhengighet i byggebildet, lenge før de dukker opp som en kryptisk feil i en pull request fredag ettermiddag.
# Kjør hele pipelinen lokalt i et Ubuntu-basert testbilde
docker run --rm -v "$(pwd):/repo" -w /repo ubuntu:24.04 bash -c "
apt-get update -qq && apt-get install -y -qq python3-pip nodejs npm git > /dev/null
pip install semgrep==1.181.0
npm ci --ignore-scripts
semgrep ci --supply-chain --config=custom-rules/
"
Hvis denne kommandoen kjører rent lokalt, er sjansen stor for at den også kjører rent i GitHub Actions, siden begge miljøene da starter fra samme rene utgangspunkt uten lokale verktøy eller hurtigbufre som skjuler feil.
Eksempel på output: slik ser en reell oppdagelse ut
Når Semgrep faktisk finner noe, ser terminalutskriften omtrent slik ut. Legg merke til at verktøyet markerer treffet som “reachable”, altså at koden faktisk kalles fra applikasjonen din, ikke bare at den finnes et sted i avhengighetstreet.
$ semgrep ci --supply-chain
Skanner 412 avhengigheter i package-lock.json og requirements.txt...
✗ FUNNET: postinstall-exfiltrates-env
Fil: node_modules/forms-utilz/package.json
Alvorlighet: ERROR | Reachable: true
Pakke: [email protected] (typosquat av "forms-util")
Postinstall-skript sender process.env til ekstern server
via https.request() i scripts/setup.js:14
✗ FUNNET: supply-chain-known-malware
Fil: requirements.txt
Pakke: reqeusts==2.31.0 (typosquat av "requests")
Kilde: Semgrep Supply Chain-database, lagt til 2026-10-09
Oppsummering: 2 funn (2 ERROR, 0 WARNING)
Bygg avbrutt. Se sbom/npm-sbom.json for fullt avhengighetstre.
Begge eksemplene over er typiske typosquatting-mønstre: “forms-utilz” ligger tett opp mot et ekte biblioteknavn, og “reqeusts” er en enkel bokstavbytting av “requests”. Dette er nøyaktig den typen feil en utvikler gjør når hun skriver fort i en terminal klokken fire en fredag, og det er nøyaktig den typen feil angripere regner med at skjer regelmessig nok til at det er verdt å registrere pakken og vente.
Overvåking og varsling i praksis
En skanning som bare kjører i CI, oppdages først når en utvikler faktisk åpner loggen. I praksis betyr det at et funn på en fredag kveld kan ligge uoppdaget til mandag morgen. Legg derfor til et varslingssteg som poster funnet rett inn i en kanal teamet allerede følger med på, i stedet for å gjemme det i en CI-logg ingen leser med vilje.
# Legg til som eget steg i supply-chain.yml, etter skanningen
- name: Varsle Slack ved funn
if: failure()
run: |
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"Supply Chain Guard stoppet bygget. Se CI-loggen for detaljer.\"}" \
"$SLACK_WEBHOOK_URL"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Sett opp denne varslingen til kun å utløses ved faktiske funn (if: failure()), ikke ved hver kjøring. En kanal som får et grønt varsel ved hver pull request, mister raskt all oppmerksomhet, mens en kanal som kun får varsler ved reelle funn, blir tatt på alvor når den endelig sier noe.
Semgrep-versjoner og deteksjonsmetoder sammenlignet
Semgrep har flyttet seg fort gjennom høsten 2026. Tabellen under oppsummerer de siste fire utgivelsene og hvorfor de er relevante for en deteksjonspipeline for pakkesikkerhet.
| Versjon | Utgitt | Relevant endring |
|---|---|---|
| 1.178.0 | 23. september 2026 | Grunnlag for Malware Detection and Response Automation |
| 1.179.0 | 1. oktober 2026 | Ytelsesforbedringer i skanningsmotoren |
| 1.180.0 | 7. oktober 2026 | Stabilitetsfikser i Supply Chain-modulen |
| 1.181.0 | Oktober 2026 | Riktig parsing av requirements.txt, fiks for Gradle-lås og git gc-feil i secrets-skanning |
Ingen enkelt deteksjonsmetode fanger alt. De fungerer best lagvis, der hver metode fanger det de andre overser.
| Metode | Fanger kjente CVE-er | Fanger fersk ondsinnet kode | Krever vedlikehold |
|---|---|---|---|
| npm audit / pip-audit | Ja | Nei | Lavt |
| Semgrep Supply Chain | Ja | Ja, via reachability og malwaredatabase | Middels |
| Egendefinerte Semgrep-regler | Nei | Ja, for kjente mønstre du selv definerer | Høyt |
| Lockfile-tvang (npm ci) | Nei | Nei, men hindrer uventede oppgraderinger | Lavt |
| SBOM-generering | Delvis, via senere sammenligning | Delvis, gir sporbarhet etter funn | Lavt |
7 vanlige fallgruver i pakkesikkerhet
- Å stole på en grønn npm audit som bevis på trygghet. Audit sjekker kun kjente, rapporterte sårbarheter. En fersk, ondsinnet pakke har per definisjon ingen CVE å matche mot.
- Å kun skanne production-avhengigheter. Mye skadelig kode gjemmer seg i devDependencies, fordi utviklere sjelden gransker test- og byggeverktøy like nøye som de gransker biblioteker som går i produksjon.
- Å bruke
npm installi CI i stedet fornpm ci. Det første kan stille oppgradere en transitiv avhengighet til en nyere, kompromittert versjon uten at noen merker det. - Å whiteliste en pakke for alltid etter én ren skanning. En pakke som var trygg i går, kan være kompromittert i dag hvis vedlikeholderens konto blir tatt over. Skanning er et øyeblikksbilde, ikke en garanti.
- Å ignorere postinstall- og preinstall-skript fordi “alle pakker har dem”. De fleste legitime pakker har ikke behov for å kjøre vilkårlig kode ved installasjon. Native moduler er unntaket, ikke regelen.
- Å behandle SBOM-generering som en engangsoppgave. En SBOM er kun nyttig hvis den genereres for hver bygg og lagres, slik at du kan svare nøyaktig på hva som gikk i produksjon den dagen et angrep ble oppdaget, ofte uker eller måneder senere.
- Å la Semgrep-versjonen i CI drifte fra versjonen utviklerne har lokalt. Når reglene oppdateres oftere enn en gang i måneden, som de har gjort gjennom hele høsten 2026, ender ulike versjoner opp med å gi ulike resultater på nøyaktig samme kodebase, noe som gjør feilsøking unødvendig forvirrende.
Feilsøking: 10 problemer og løsninger
Disse problemene dukker opp hos nesten alle som setter opp denne pipelinen for første gang. De fleste løses på under fem minutter når du vet hvor du skal se, men de kan koste en hel ettermiddag hvis du feilsøker blindt. Tabellen under går rett til løsningen.
| Problem | Sannsynlig årsak | Løsning |
|---|---|---|
| “semgrep: command not found” i CI | Semgrep ble installert i et annet trinn eller container-lag enn der kommandoen kjøres | Installer Semgrep i samme steg eller samme container som skanningen kjører i |
| “Authentication failed” ved semgrep login i CI | SEMGREP_APP_TOKEN mangler eller er utløpt | Generer ny token i Semgrep AppSec Platform og lagre som repository secret |
| Falske positiver på interne registerpakker | Semgrep kjenner ikke navnekonvensjonen til ditt private registry | Legg private pakkenavn i en ignore-liste i .semgrepignore |
| Skanningen tar over 10 minutter på et monorepo | Hele avhengighetstreet skannes på nytt ved hver kjøring | Bruk –baseline-commit for å kun skanne endrede avhengigheter |
| Guardian-verifisering feiler etter auto-oppdatering | Lokal klokke er ute av synk, eller nettverksbrannmur blokkerer verifiseringsendepunktet | Synkroniser systemklokken med NTP og tillat utgående trafikk til Semgreps verifiseringstjeneste |
| requirements.txt med uprikkede versjoner ignoreres | Semgrep-versjon eldre enn 1.181.0 kastet ut unpinned pakkenavn under parsing | Oppgrader til 1.181.0 eller nyere |
| GitHub Actions får “Permission denied” ved opplasting av resultater | Workflowen mangler security-events: write i permissions-blokken | Legg til riktig permissions-felt i workflow-filen |
| Gradle-lås feiler når byggmappen flyttes | Kjent feil i versjoner før 1.181.0 ved lockfile-løs Gradle-oppløsning | Oppgrader Semgrep, eller fest Gradle-byggmappen til en fast sti |
| Secrets-skanning feiler midt i jobben | En forutgående git gc feilet og korrumperte midlertidig tilstand | Oppgrader til 1.181.0, som retter denne spesifikke feilen, eller kjør git gc manuelt før skanning |
| En transitiv avhengighet dypt nede i treet fanges ikke | Skanningsdybden er begrenset som standard for å spare tid | Øk –max-depth eller skann hele den oppløste låsfilen i stedet for kun direkte avhengigheter |
Avanserte tips utover grunnoppsettet
- Prøv Semgreps Agentic Workflows. Disse er i åpen beta og leveres med ni ferdigbygde arbeidsflyter for dypere sårbarhetsjakt, nyttig når teamet har kapasitet til å gå videre enn standardreglene.
- Koble funn til en Slack- eller Teams-kanal i sanntid. Et funn som ligger og venter i en CI-logg blir ofte oversett til neste morgen, mens et funn i en kanal alle ser, blir håndtert samme dag.
- Roter CI/CD-hemmeligheter på en fast tidsplan, ikke bare etter en hendelse. En kompromittert pakke kan ha lekket en hemmelighet uker før den ble oppdaget.
- Bruk et automatisert verktøy for avhengighetsoppdateringer med en manuell godkjenningsport. Automatikk alene øker overflaten for angrep. En menneskelig godkjenning av diffen før merge fanger mønstre skanneren overser.
- Korreler pakkeskanning med kjøretidsobservabilitet. Hvis en prosess plutselig åpner utgående nettverkstrafikk den aldri har gjort før, er det et sterkere signal enn noe en statisk skanning kan se før koden faktisk kjører.
- Dokumenter avvik, ikke bare funn. Hver gang teamet bevisst godtar en pakke som krever et byggeskript eller en avhengighet med en eldre, men kjent trygg versjon, skriv ned hvorfor. Dette gjør neste sikkerhetsgjennomgang til en samtale om hva som har endret seg, ikke en jakt gjennom gamle commit-meldinger for å forstå en beslutning ingen lenger husker.
- Gi nye avhengigheter en karantenetid før de regnes som godkjent. En pakke som er publisert for første gang samme uke den tas inn i prosjektet, har ikke hatt tid til at økosystemet rundt den har oppdaget og rapportert eventuelle problemer. Noen ekstra dager med observasjon fanger overraskende mye.
Hendelsesrespons: når dere faktisk finner en skadelig pakke
Deteksjon uten en plan for hva som skjer etterpå løser bare halve problemet. Når Semgrep eller karanteneskriptet flagger et reelt funn i produksjon, følg denne rekkefølgen.
- Fjern pakken umiddelbart fra alle miljøer, ikke bare fra det repositoriet som flagget den. Samme pakke kan ligge i flere tjenester.
- Roter alle hemmeligheter prosessen hadde tilgang til: API-nøkler, databasepassord, CI/CD-tokens og SSH-nøkler på byggemaskinen.
- Gå gjennom SBOM-historikken for å finne nøyaktig hvilke bygg som inkluderte pakken, og hvor lenge den var der.
- Sjekk utgående nettverkslogger fra byggemaskinene for trafikk til ukjente endepunkter i samme periode.
- Varsle alle team som kan ha brukt samme pakke, selv om de ikke er direkte involvert i hendelsen.
- Dokumenter tidslinjen mens detaljene er ferske. Dette er informasjonen som gjør neste hendelse raskere å håndtere.
Dette er også grunnen til at et NIS2-pålagt varslingsregime, som allerede gjelder for mange norske virksomheter, forutsetter at dere faktisk har en SBOM og en logg å gå tilbake til. Uten det blir “hvilke systemer var påvirket” et spørsmål ingen kan svare på raskt.
En realistisk tidslinje for en håndtering som fungerer, ser omtrent slik ut: funnet dukker opp i CI klokken 14.03, en utvikler bekrefter at det er reelt innen 14.20, pakken fjernes og hemmeligheter roteres innen 15.00, SBOM-historikken er gjennomgått innen 16.30, og en kort intern rapport er skrevet innen slutten av dagen. Teamene som klarer dette på timer i stedet for dager, er nesten alltid de som allerede hadde SBOM-er, varslingskanaler og et karanteneskript klare før hendelsen skjedde, ikke de som improviserte alt samtidig som de håndterte selve angrepet.
Ofte stilte spørsmål
Er Semgrep gratis å bruke for denne typen skanning?
Semgrep CLI og kjernereglene er åpen kildekode og gratis. Supply Chain-reglene med reachability-analyse og Semgrep Guardian krever en konto på Semgrep AppSec Platform, som har et gratis spor for mindre team og et betalt spor for større organisasjoner.
Kan jeg bruke denne pipelinen på et eksisterende prosjekt med tusenvis av avhengigheter?
Ja. Kjør første skanning utenfor CI, som en engangsjobb, siden den første kjøringen uten en baseline tar lengre tid. Deretter bruker du –baseline-commit i CI slik at kun endrede avhengigheter skannes ved hver pull request.
Hva er forskjellen mellom npm audit og Semgrep Supply Chain?
npm audit sjekker avhengighetene dine mot en database med kjente, rapporterte sårbarheter (CVE-er). Semgrep Supply Chain gjør det samme, men legger til reachability-analyse og en database for kjent skadelig kode, samt mulighet for egne regler som fanger mønstre ingen database har klassifisert ennå.
Må jeg skrive egne Semgrep-regler, eller holder standardreglene?
Standardreglene dekker de mest vanlige og kjente mønstrene og er et godt utgangspunkt. Egne regler blir viktige når teamet ditt har en spesifikk stack eller har vært utsatt for et angrepsmønster standardreglene ikke dekker.
Hvor ofte bør vi skanne på nytt, selv uten nye avhengigheter?
Minst én gang i døgnet, fordi en pakke som var trygg i går kan være kompromittert i dag hvis vedlikeholderens konto blir tatt over. Mange team legger en nattlig jobb i tillegg til skanningen ved hver pull request.
Fungerer dette opplegget også for monorepo med flere språk?
Ja. Semgrep skanner både JavaScript/TypeScript og Python i samme kjøring, og CI/CD-eksempelet i denne guiden kan utvides med flere –config-flagg for andre språk i samme workflow.
Hva gjør vi hvis vi finner en skadelig pakke som allerede har vært i produksjon i flere uker?
Følg hendelsesresponsrekkefølgen i avsnittet over: fjern pakken, roter hemmeligheter, gå gjennom SBOM-historikk og nettverkslogger, og varsle alle team som kan ha brukt samme pakke. Tidlig dokumentasjon av tidslinjen gjør etterarbeidet langt raskere.
Kan karanteneskriptet kjøre automatisk uten menneskelig godkjenning?
Teknisk ja, men de fleste team lar skriptet åpne en pull request i stedet for å slå sammen direkte, slik at en utvikler bekrefter at den riktige pakken ble fjernet og at ingenting annet i bygget går i stykker som følge av fjerningen.
Hva er egentlig forskjellen mellom dependency confusion og vanlig typosquatting?
Typosquatting utnytter at mennesker skriver feil, og rammer et pakkenavn som ligger tett opp mot et velkjent bibliotek. Dependency confusion utnytter hvordan pakkeløseren selv velger kilde når et internt og et offentlig pakkenavn er identiske, og krever derfor ingen menneskelig skrivefeil for å utløses.
Bør vi blokkere alle postinstall-skript helt, uten unntak?
Nei, ikke i praksis. Mange legitime pakker med native tillegg, som database-drivere og bildebehandlingsbiblioteker, trenger et byggeskript for å kompilere korrekt på operativsystemet som kjører byggejobben. Målet er en kort, gjennomgått unntaksliste, ikke et fullstendig forbud som til slutt blir omgått fordi byggene stopper å fungere.
Hva skjer hvis Semgrep selv flagger et falskt positivt treff på en pakke vi vet er trygg?
Legg pakken i en eksplisitt unntaksliste i konfigurasjonen med en kommentar om hvorfor, i stedet for å deaktivere hele regelen. En regel som deaktiveres helt for å løse ett falskt positivt, slutter samtidig å fange alle fremtidige, reelle funn av samme type.
