Findings-API
Hver skanner som kan blokkere en merge, gjør funnene sine tilgjengelige over REST-API-et. Når en commit-status sier «merge blocked», er det her du leser de konkrete funnene bak den, og her du avviser dem du godtar.
Dette er de samme dataene som /<org>/_admin/code-security i admingrensesnittet – grensesnittet er en klient av disse endepunktene.
Stisegmentene samsvarer ikke med statusnavnene
Dette er den ene tingen det er verdt å lære seg utenat. Konteksten for commit-statusen og stisegmentet i API-et er forskjellige:
| Skanner | Commit-status du ser på PR-en | Stisegment i API-et |
|---|---|---|
| Statisk analyse | fremforge/sast | sast |
| Avhengigheter | fremforge/dep-scan | deps |
| Containerimager | fremforge/image-scan | images |
| Lisenser | fremforge/license-scan | licenses |
Hvis du gjetter segmentet ut fra statusnavnet, får du 501 Not Implemented fra API-ets oppsamlingsrute, som leses som «dokumentert, men ikke levert ennå». Det er det ikke – du har feil URL.
Basis-URL
Produkt-API-et serveres under /_app, ikke på apex-domenet (apex er Forgejos eget API):
https://frem.sh/_app/api/v1/orgs/<org>/code-security/<scanner>/findingsAutentiser med et API-token for organisasjonen eller et PAT – se API-referansen for tokentypene.
Alle funn i én liste
For å lese alle åpne funn på én gang, uansett skanner, bruker du den samlede listen. Det er listen under Sikkerhet › Funn › Alle:
curl -sS \
-H "Authorization: Bearer ${FREMFORGE_TOKEN}" \
"https://frem.sh/_app/api/v1/orgs/acme/security/findings?severity=critical,high"{
"counts": { "code": 3, "secrets": 0, "dependencies": 41, "images": 12, "iac": 5, "licences": 2, "malware": 0 },
"items": [
{
"type": "dependencies",
"id": "0f6c…",
"severity": "critical",
"title": "GHSA-xxxx-xxxx-xxxx",
"subject": "acme/api",
"detail": "lodash@4.17.20",
"pr_number": null,
"known_exploited": true,
"first_seen_at": "2026-10-02T08:14:03.120Z"
}
],
"next_cursor": "eyJr…"
}type: en kommaliste medcode,secrets,dependencies,images,iac,licencesogmalware. Standard er alle. Funn i infrastruktur som kode er en egen type her, selv om skanningen som finner dem, er image-skanningen.severity: en kommaliste medcritical,high,mediumoglow.repo: ett repository (owner/name) eller image.limit: 1–200, standard 50. For neste side sender dunext_cursorsom du fikk tilbake, somcursor. Den ernullpå siste side.
Rekkefølgen er konsollens: kjent utnyttet først, deretter alvorlighetsgrad, deretter de eldste, slik at funnet som er nærmest fristen for utbedring, kommer først. Alvorlighetsgradene er gjort sammenlignbare på tvers av skannere: en SAST-error er high, en warning er medium og alt annet low; en forbudt lisens er critical, en begrenset high og alle andre low; secrets og skadevare er alltid critical. counts inneholder de åpne funnene per type for hele organisasjonen, uten filtrene. Krever findings:read. En ukjent type eller alvorlighetsgrad gir svaret 400.
branch: en beskyttet utgivelsesbranch, for eksempelrelease/1.x, for å liste den branchens funn i stedet for standardbranchens. Hver rad har da ogsåbranchogfix_by. En branch som ikke er skannet, gir svaret404 unknown-branch.GET /orgs/{org}/security/release-brancheslister de skannede.
En rad inneholder det som listen viser. For hele funnet (nåbarhet, triage, avhengighetssti) og for å avvise det bruker du skannerens eget endepunkt nedenfor med radens id.
List funn
Bare åpne funn, som standard:
curl -sS \
-H "Authorization: Bearer ${FREMFORGE_TOKEN}" \
"https://frem.sh/_app/api/v1/orgs/acme/code-security/images/findings?limit=50"{
"findings": [
{
"id": "0f6c…",
"registry_image": "acme/api@9f2c…",
"cve_id": "CVE-2026-59950",
"package": "mcp",
"severity": "high",
"fixed_version": "1.28.1",
"dismissed_at": null,
"created_at": "2026-07-25T06:33:41.940Z"
}
],
"has_more": false
}Spørreparametere, for alle fire skannerne:
limit– 1–200, standard 50.offset– bla medoffset += limittilhas_moreerfalse.include_dismissed– setttruefor også å se avviste funn.
Feltene i svaret varierer per skanner, fordi funnene gjør det: images returnerer cve_id / package / fixed_version, deps legger til ecosystem / manifest_path / current_version og, for funn på standardbranchen, via / dependency_path (hvilken direkte avhengighet som trekker inn pakken, se avhengighetsstier), licenses returnerer license_id / category, og sast returnerer rule_id / path / line. OpenAPI-spesifikasjonen har det nøyaktige skjemaet for hver av dem.
To felter til kommer fra nåbarhet og triage: funn fra deps og images har et reachability-objekt og et sla-objekt (fristen som gjelder, med multiplikatoren du kan slå på brukt), og funn fra sast har et triage-objekt (plassering, taint-sti, avvisningshistorikk og om funnet er i gruppen med lavere sikkerhet). deps og images tar også ?reachability= for å filtrere på vurdering. Alle tre tar ?sort=risk for å liste i triage-rekkefølge: for deps og images nåbarhetsvurderingen, deretter alvorlighetsgrad, deretter kjent utnyttet, deretter EPSS; for sast triage-signalene (gruppen med lavere sikkerhet sist, deretter alvorlighetsgrad, deretter et taint-spor foran et ukjent treff eller et rent mønstertreff, deretter de eldste først). Bla med offset er stabilt i begge rekkefølger. Standardrekkefølgen er nyeste først, med ID som avgjør ved likhet.
Funn fra deps og images har også known_exploited: null, eller { "cve_ids": [...], "sources": [...] } når sårbarheten er oppført som utnyttet i praksis av ENISAs EU Vulnerability Database (euvd) eller CISAs KEV-katalog (cisa_kev). Et avhengighetsfunn matches gjennom CVE-aliasene i sårbarhetsvarselet sitt, et image-funn på CVE-ID-en; begge listene oppdateres daglig. Legg til ?known_exploited=true for å liste bare disse funnene, eller ?known_exploited=false for resten; alle andre verdier gir svaret 400. Konsollens lister for Dependencies og Container images har det samme filteret, Bare kjent utnyttet, og Sikkerhetsrettelser åpner rettings-pull requestene deres først.
Krever migrering
Et deps-funn med fixed_version lik null har needs_migration når sårbarhetsvarselet bare er rettet i en annen pakke, for eksempel en ny Go-modulsti eller hovedversjon (github.com/docker/docker til github.com/moby/moby/v2) eller en pakke med nytt navn (npm request til @cypress/request). Ellers er den null:
"fixed_version": null,
"needs_migration": { "package": "github.com/moby/moby/v2", "fixed_version": "2.0.0-beta.8" }Ingen rettings-pull request tilbys for et slikt funn; se Krever migrering.
Når et funn lukkes
Funntabellene tar bare imot nye rader: hver skanning skriver et nytt sett med rader under en ny skannekjøring, og ingenting slettes når du retter det underliggende problemet. To ting lukker et funn.
Automatisk erstattet. En jobb som kjører hver time, løser funn der skannekjøringen ikke lenger er den nyeste for målet sitt, og merker dem dismissed_by: auto-resolve. «Mål» er den naturlige identiteten for hver skanner:
| skanner | målidentitet |
|---|---|
sast | repository + filsti |
deps, licenses | repository + manifeststi |
images | image-referanse |
Den reelle regelen er altså «hvis den nyeste skanningen av målet ikke meldte det på nytt, er det borte» – den samme semantikken som OSV og Trivy bruker. Rett det, la en senere skanning kjøre, og det forsvinner innen en time. Merk at utløseren er en nyere skanning av samme mål, ikke selve merge-en: hvis ingenting skanner det målet på nytt, blir raden stående åpen på ubestemt tid.
Her er det en bevisst sikkerhetsegenskap – bare den nyeste skannekjøringen erstatter. En feilkonfigurert skanning som ikke melder noe for et mål, sletter ikke målets funn, så en ødelagt skanner kan ikke stille tømme dashbordet ditt.
Uttrykkelig avvist. Du avviser det, med en begrunnelse. Manuelle avvisninger bevares av den automatiske lukkerunden, så begrunnelsen din overskrives aldri.
En avvisning kan utløpe. Gi den en expires_at (API-et) eller en Expires on-dato (adminsidene), så faller godtakelsen bort av seg selv: en daglig jobb åpner funnet igjen når datoen har passert, registrerer i revisjonsloggen hvem som avviste det og hvorfor, og – for et funn med høy eller kritisk alvorlighetsgrad – sender det samme sikkerhetsvarselet som et nytt funn får. En dato betyr til og med den dagen, med slutt ved midnatt Europe/Copenhagen. Uten en dato utløper en avvisning aldri, som er standard og slik alle tidligere avvisninger oppfører seg. Utløpstiden følger med avvisningen over senere skanninger, så en ny skanning kan ikke gjøre en midlertidig godtakelse permanent.
Hva en avvisning i image-skanningen dekker. Et avvist funn i et containerimage forblir avvist i senere skanninger av det samme repositoryet eller imaget, men bare for den samme CVE-en eller regelen i den samme filen (funnets target). Hvis den samme regelen dukker opp i en annen fil, også en fil som legges til senere, er det et eget funn som blokkerer for seg selv. Å avvise KSV-0014 på ett manifest godtar den altså ikke for den neste Deployment-en noen legger til.
I praksis: rett ved kilden og la neste skanning erstatte funnet. Avvis når du godtar funnet, eller når ingen flere skanninger av det målet er ventet – for eksempel et funn knyttet til en branch som siden er merget og ikke lenger skannes.
Avvise et funn
reason er påkrevd, lagres på funnet og skrives til revisjonsloggen – skriv den for den som leser den om seks måneder.
curl -sS -X POST \
-H "Authorization: Bearer ${FREMFORGE_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"reason":"Fixed at source in acme/api#412; stale PR-scoped row."}' \
"https://frem.sh/_app/api/v1/orgs/acme/code-security/images/findings/0f6c…/dismiss"{ "dismissed": true, "expires_at": null, "id": "0f6c…" }Legg til expires_at for å gjøre godtakelsen midlertidig – en dato ("2026-12-31", godtatt til og med den dagen) eller et ISO 8601-tidsstempel med tidssone. Det må ligge i framtiden og innenfor tre år; alt annet gir 400 invalid-expires-at. Svaret gjentar det lagrede tidspunktet:
{ "dismissed": true, "expires_at": "2026-12-31T23:00:00.000Z", "id": "0f6c…" }En 404 betyr at det ikke finnes noe åpent funn med den ID-en – oftest er det allerede avvist.
For å lukke mange på én gang tar sast, deps og images imot opptil 1000 ID-er per kall:
curl -sS -X POST \
-H "Authorization: Bearer ${FREMFORGE_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"ids":["0f6c…","1a2b…"],"reason":"Accepted: vendored fixture, not shipped."}' \
"https://frem.sh/_app/api/v1/orgs/acme/code-security/deps/findings/bulk-dismiss"licenses har bare avvisning én og én – det finnes ikke noe massendepunkt for den.
Å lese er ikke det samme som å ta inn
To stier som ligner hverandre, gjør forskjellige jobber:
/orgs/<org>/code-security/images/findings– det denne siden beskriver. Avgrenset til organisasjonen, lese og avvise./_app/api/v1/image-scans/findings– inntaks-endepunktet som skannerkjøringen sender resultater til med POST. Ikke for lesing.
Innstillinger
Blokkeringsterskelen til hver skanner er også et API-kall – PUT /orgs/<org>/code-security/<scanner>/settings. Merk at images sperrer på block_action, mens sast, deps og licenses sperrer på block_severity; terskelen for images er image_scan-oppføringen i organisasjonens skannepolicy (PUT /orgs/<org>/policy, se blokkeringsterskel); licenses tar i tillegg allowlisted_license_ids.
For deps finnes det én policy til som du må slå på selv: PUT /orgs/<org>/security/dep-scan-unreachable-policy ({"enabled": true, "repos": [...]}, scope policy:write) hindrer at et funn i en pull request med nåbarhetsvurderingen ikke nåbar blokkerer fremforge/dep-scan. Den er av som standard; unknown, skadevare og funn som er kjent utnyttet, blokkerer alltid, og en feilaktig «ikke nåbar» slipper en reell sårbarhet gjennom til merge. Se Nåbarhet: ikke blokker på funn som ikke er nåbare. Et funn som den slapp gjennom, har block_waived: "unreachable" i listen over.
Se også
- Godta funn i repositoryet – den andre måten å godta et funn på: en Trivy-ignorefil som committes sammen med koden.
- Image-skanning – hvordan imager skannes og hva som blokkerer en merge.
- Lisensskanning – policykategorier og tillatelseslister.
- API-referanse – autentisering, tokener og hele OpenAPI-spesifikasjonen.