Findings API
Varje skanner som kan blockera en merge exponerar sina fynd via REST-API:t. När en commit-status säger “merge blocked” är det här du läser de specifika fynden bakom den, och här du avfärdar de som du accepterar.
Det är samma data som /<org>/_admin/code-security i administrationsgränssnittet; gränssnittet är en klient till de här endpointsen.
Sökvägssegmenten matchar inte statusnamnen
Det här är det enda som är värt att lägga på minnet. Commit-statusens kontext och API:ts sökvägssegment skiljer sig åt:
| Skanner | Commit-status som du ser på pull requesten | API-sökvägssegment |
|---|---|---|
| Statisk analys | fremforge/sast | sast |
| Beroenden | fremforge/dep-scan | deps |
| Container-images | fremforge/image-scan | images |
| Licenser | fremforge/license-scan | licenses |
Om du gissar segmentet utifrån statusnamnet får du 501 Not Implemented från API:ts catch-all, vilket ser ut som “dokumenterat men inte levererat än”. Det är det inte: du har fel URL.
Bas-URL
Produktens API levereras under /_app, inte på apex (apex är Forgejos eget API):
https://frem.sh/_app/api/v1/orgs/<org>/code-security/<scanner>/findingsAutentisera med en API-token för organisationen eller en PAT; se API-referensen för tokentyperna.
Alla fynd i en lista
För att läsa alla öppna fynd på en gång, oavsett skanner, använder du den samlade listan. Det är listan under Säkerhet › Fynd › Alla:
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 kommaseparerad lista medcode,secrets,dependencies,images,iac,licencesochmalware. Standard är alla. Fynd i infrastruktur som kod är en egen typ här, även om det är image-skanningen som hittar dem.severity: en kommaseparerad lista medcritical,high,mediumochlow.repo: ett repo (owner/name) eller en image.limit: 1–200, standard 50. För nästa sida skickar du dennext_cursordu fick tillbaka somcursor. Den ärnullpå sista sidan.
Ordningen är konsolens: kända som utnyttjade först, sedan allvarlighetsgrad, sedan de äldsta, så att fyndet som är närmast sitt åtgärdsdatum kommer först. Allvarlighetsgraderna görs jämförbara mellan skannrar: en SAST-error är high, en warning medium och allt annat low; en förbjuden licens är critical, en begränsad high och alla andra low; hemligheter och skadlig kod är alltid critical. counts innehåller antalet öppna fynd per typ för hela organisationen, utan filtren. Kräver findings:read. En okänd typ eller allvarlighetsgrad besvaras med 400.
branch: en skyddad release-branch, till exempelrelease/1.x, för att lista den branchens fynd i stället för standardbranchens. Varje rad har då ocksåbranchochfix_by. En branch som inte har skannats besvaras med404 unknown-branch.GET /orgs/{org}/security/release-brancheslistar de skannade.
En rad innehåller det som listan visar. För det fullständiga fyndet (nåbarhet, triage, beroendesökväg) och för att avfärda det använder du skannerns egen endpoint nedan med radens id.
Lista fynd
Endast öppna fynd, 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
}Frågeparametrar, alla fyra skannrar:
limit: 1–200, standard 50.offset: bläddra medoffset += limittillshas_moreärfalse.include_dismissed: sätt tilltrueför att även se avfärdade fynd.
Svarsfälten skiljer sig per skanner, eftersom fynden gör det: images returnerar cve_id / package / fixed_version, deps lägger till ecosystem / manifest_path / current_version och, för fynd på standardbranchen, via / dependency_path (vilket direkt beroende som drar in paketet, se beroendesökvägar), licenses returnerar license_id / category, och sast returnerar rule_id / path / line. OpenAPI-specifikationen innehåller det exakta schemat för var och en.
Ytterligare två fält kommer från nåbarhet och triage: fynd från deps och images har ett reachability-objekt och ett sla-objekt (tidsgränsen som gäller, med den valfria multiplikatorn tillämpad), och fynd från sast har ett triage-objekt (plats, taint-sökväg, historik över avfärdanden och om fyndet ligger i gruppen med lägre tillförlitlighet). deps och images tar också ?reachability= för att filtrera på bedömning. Alla tre tar ?sort=risk för att lista i triageordning: för deps och images nåbarhetsbedömningen, sedan allvarlighetsgrad, sedan känd som utnyttjad, sedan EPSS; för sast triagesignalerna (gruppen med lägre tillförlitlighet sist, sedan allvarlighetsgrad, sedan en taint-spårning före en okänd träff eller en ren mönsterträff, sedan de äldsta först). Bläddring med offset är stabil i båda ordningarna. Standardordningen är nyaste först, med lika värden sorterade på id.
Fynd från deps och images har också known_exploited: null, eller { "cve_ids": [...], "sources": [...] } när sårbarheten är listad som utnyttjad i praktiken av ENISA:s EU Vulnerability Database (euvd) eller CISA:s KEV-katalog (cisa_kev). Ett beroendefynd matchas via CVE-aliasen för sitt säkerhetsmeddelande, ett image-fynd via sitt CVE-id; båda listorna uppdateras dagligen. Lägg till ?known_exploited=true för att bara lista de fynden, eller ?known_exploited=false för resten; alla andra värden besvaras med 400. Konsolens listor för Beroenden och Container-images har samma filter, Endast kända som utnyttjade, och Säkerhetsfixar öppnar deras fix-pull requests först.
Kräver migrering
Ett deps-fynd med en fixed_version som är null har needs_migration när dess säkerhetsmeddelande bara är åtgärdat i ett annat paket, till exempel en ny modulsökväg eller major-version i Go (github.com/docker/docker till github.com/moby/moby/v2) eller ett paket med nytt namn (npm request till @cypress/request). Annars är det null:
"fixed_version": null,
"needs_migration": { "package": "github.com/moby/moby/v2", "fixed_version": "2.0.0-beta.8" }Ingen fix-pull request erbjuds för ett sådant fynd; se Kräver migrering.
När ett fynd stängs
Tabellerna med fynd tar bara emot nya rader: varje skanning skriver en ny uppsättning rader under en ny skanningskörning, och ingenting tas bort när du åtgärdar det underliggande problemet. Två saker stänger ett fynd.
Ersätts automatiskt. Ett jobb som körs varje timme löser fynd vars skanningskörning inte längre är den senaste för deras mål, och markerar dem dismissed_by: auto-resolve. “Mål” är den naturliga identiteten för varje skanner:
| skanner | målets identitet |
|---|---|
sast | repo + filsökväg |
deps, licenses | repo + manifestsökväg |
images | image-referens |
Den regel som i praktiken gäller är alltså “om den senaste skanningen av målet inte rapporterade det igen, är det borta”, samma semantik som OSV och Trivy använder. Åtgärda det, låt en senare skanning köras, och det försvinner inom en timme. Observera att utlösaren är en nyare skanning av samma mål, inte själva mergen: om ingenting skannar om målet förblir raden öppen på obestämd tid.
Här finns en medveten säkerhetsegenskap: bara den senaste skanningskörningen ersätter. En felkonfigurerad skanning som inte rapporterar något för ett mål rensar inte det målets fynd, så en trasig skanner kan inte tyst tömma din översikt.
Avfärdas uttryckligen. Du avfärdar det, med en motivering. Manuella avfärdanden bevaras av den automatiska lösningen, så din motivering skrivs aldrig över.
Ett avfärdande kan gå ut. Ge det ett expires_at (API:t) eller ett datum i Upphör den (administrationssidorna) så upphör accepterandet av sig självt: ett dagligt jobb öppnar fyndet igen när datumet har passerat, registrerar i granskningsloggen vem som avfärdade det och varför, och skickar, för ett fynd med hög eller kritisk allvarlighetsgrad, samma säkerhetslarm som ett nytt fynd får. Ett datum betyder till och med den dagen, och slutar vid midnatt Europe/Copenhagen. Utan datum går ett avfärdande aldrig ut, vilket är standard och hur alla tidigare avfärdanden fungerar. Utgångsdatumet följer med avfärdandet i senare skanningar, så en omskanning kan inte göra ett tillfälligt accepterande permanent.
Vad ett avfärdande i image-skanningen omfattar. Ett avfärdat fynd för en container-image förblir avfärdat i senare skanningar av samma repo eller image, men bara för samma CVE eller regel i samma fil (fyndets target). Om samma regel dyker upp i en annan fil, även en fil som läggs till senare, är det ett separat fynd som blockerar på egen hand. Att avfärda KSV-0014 i ett manifest accepterar det alltså inte för nästa Deployment som någon lägger till.
I praktiken: åtgärda vid källan och låt nästa skanning ersätta det. Avfärda när du accepterar fyndet, eller när ingen ytterligare skanning av målet väntas, till exempel ett fynd som hör till en branch som sedan dess har mergats och inte längre skannas.
Avfärda ett fynd
reason är obligatoriskt, sparas på fyndet och skrivs till granskningsloggen. Skriv det för den som läser det om sex månader.
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…" }Lägg till expires_at för att göra accepterandet tillfälligt: ett datum ("2026-12-31", accepterat till och med den dagen) eller en ISO 8601-tidsstämpel med tidszon. Det måste ligga i framtiden och inom tre år; allt annat ger 400 invalid-expires-at. Svaret återger den sparade tidpunkten:
{ "dismissed": true, "expires_at": "2026-12-31T23:00:00.000Z", "id": "0f6c…" }En 404 betyder att det inte finns något öppet fynd med det id:t; oftast är det redan avfärdat.
För att stänga många på en gång tar sast, deps och images emot upp till 1000 id:n per anrop:
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 bara avfärdande ett i taget; det finns ingen endpoint för massavfärdande.
Läsa är inte att skicka in
Två sökvägar som ser lika ut gör olika saker:
/orgs/<org>/code-security/images/findings: det som den här sidan beskriver. Avgränsad till organisationen, läsa och avfärda./_app/api/v1/image-scans/findings: den inmatnings-endpoint som skanner-runnern skickar resultat till med POST. Inte för läsning.
Inställningar
Varje skanners blockeringströskel är också ett API-anrop: PUT /orgs/<org>/code-security/<scanner>/settings. Observera att images spärrar på block_action medan sast, deps och licenses spärrar på block_severity; tröskeln för images är posten image_scan i organisationens skanningspolicy (PUT /orgs/<org>/policy, se blockeringströskel); licenses tar dessutom allowlisted_license_ids.
För deps finns ytterligare en, valfri policy: PUT /orgs/<org>/security/dep-scan-unreachable-policy ({"enabled": true, "repos": [...]}, scope policy:write) gör att ett pull request-fynd vars nåbarhetsbedömning är ej nåbar inte blockerar fremforge/dep-scan. Den är avstängd som standard; unknown, skadlig kod och fynd som är kända som utnyttjade blockerar alltid, och en felaktig bedömning “ej nåbar” låter en verklig sårbarhet mergas. Se Nåbarhet: blockera inte på ej nåbara fynd. Ett fynd som den släppte igenom har block_waived: "unreachable" i listan ovan.
Se även
- Acceptera fynd i repot: det andra sättet att acceptera ett fynd, en Trivy-ignore-fil som checkas in tillsammans med koden.
- Image-skanning: hur images skannas och vad som blockerar en merge.
- Licensskanning: policykategorier och tillåtelselistor.
- API-referens: autentisering, token och den fullständiga OpenAPI-specifikationen.