Webhooks för säkerhetsvarningar
Säkerhetsaviseringar når personer via e-post. För att också nå ditt incident- eller ärendesystem prenumererar du ett utgående webhook-mål på en eller flera säkerhetshändelser. Varje händelse är en signerad HTTPS-POST, som försöks igen tills din mottagare tar emot den och registreras i leveransloggen.
Två format finns per mål:
- Generiskt (standard): fremforges JSON-händelse som beskrivs nedan.
- ServiceNow: en ärendepost som ServiceNows Table API tar emot som den är. Se ServiceNow.
Händelser
| Händelse | Skickas när | Allvarlighetsgrad |
|---|---|---|
security.finding.created | Ett nytt öppet fynd dyker upp: sårbarhet i ett beroende eller en container, SAST, hemlighet, licenspolicy | critical, eller high om du väljer det |
security.malware.matched | Skadlig kod i en uppladdning (ClamAV), eller ett känt skadligt paket (OSV MAL-*) i en SBOM för en standard-branch eller en image | critical |
security.pull.blocked | En hämtning av en container eller ett paket avvisades av spärren för skadlig kod eller av din policy block_pull för image-skanning | high |
security.policy.violation | En överträdelse av autentiseringspolicyn (tokenlivslängd, hårdvarunyckel, SSH-certifikat, deploy-nyckel), eller en push som avvisats av Push-skydd | high |
security.exception.expired | Ett avfärdande (riskacceptans) med utgångsdatum har löpt ut och fyndet är öppet igen | ingen |
security.exception.requested | Någon begärde ett undantag för ett fynd, eller avfärdade ett medan avfärdanden kräver godkännande (titeln säger då det) | fyndets, där det har en |
security.exception.approved | En godkännare godkände ett undantag för ett fynd; fyndet avfärdas | fyndets, där det har en |
security.exception.rejected | En godkännare avslog ett undantag för ett fynd; fyndet förblir öppet | fyndets, där det har en |
Två händelsenamn till når ett mål med prenumeration utan att man prenumererar på dem:
security.summary: skickas efter en timme då timgränsen höll tillbaka händelser.security.test: skickas av testknappen eller testslutpunkten.
Säkerhetshändelser väljs uttryckligen med namn. Ett mål med tom händelselista tar emot alla händelser från Forgejo, status och underhåll, men inga säkerhetshändelser. Lista de säkerhetshändelser du vill ha uttryckligen.
En prenumeration börjar när du skapar den. Fynd som fanns innan du prenumererade skickas inte i efterhand. Använd Findings API för den nuvarande eftersläpningen.
En händelse per fynd. En ny skanning skickar inte ett fynd igen. Dess correlation_key är densamma för varje händelse som gäller det, inklusive händelsen security.exception.expired när en riskacceptans för det löper ut.
Allvarlighetsgrad. security.finding.created skickas för fynd med critical som standard. Sätt security_min_severity till high för att också få fynd med hög allvarlighetsgrad. SAST-fel räknas som high. Hemligheter räknas som critical, och det gör även förbjudna licenser. Begränsade licenser räknas som high. De övriga händelserna skickas alltid.
Innehåll
{
"event": "security.finding.created",
"event_id": "3f0c2a9e5b1d4c7a8e6f1b2c3d4e5f60",
"occurred_at": "2026-10-04T10:12:44.000Z",
"org": "acme",
"severity": "critical",
"title": "CVE-2026-12345 in lodash 4.17.20",
"repository": "acme/web",
"source": "dep",
"correlation_key": "dep:acme/web:CVE-2026-12345:npm:lodash@4.17.20",
"console_url": "https://frem.sh/acme/_admin/code-security/deps",
"details": {
"finding_id": "8b8d2c7e-0f6a-4a51-9a63-0c1f6f0b2a11",
"vulnerability_id": "CVE-2026-12345",
"ecosystem": "npm",
"package": "lodash",
"version": "4.17.20",
"fixed_version": "4.17.21",
"manifest_path": "package-lock.json",
"cvss_score": 9.8
}
}| Fält | Betydelse |
|---|---|
event | Ett av händelsenamnen ovan |
event_id | Stabilt per händelse. Använd det för att kasta en leverans du redan har behandlat |
occurred_at | När fyndet, avslaget eller överträdelsen inträffade (UTC) |
severity | critical, high eller null |
source | Vad som skapade den: dep, image, sast, secret, license, malware, dependency_malware, push_protection, auth_policy, registry, eller ett skannernamn för ett utgånget undantag |
correlation_key | Fyndets identitet. Samma nyckel används för varje händelse om samma fynd |
console_url | Den konsolsida som visar det |
details | Händelsespecifika fält. Värden för hemligheter tas aldrig med: ett fynd av en hemlighet innehåller bara regel, fil och rad |
Signatur och headers
Varje förfrågan innehåller:
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: fremforge-webhook/1
X-Fremforge-Event: security.finding.created
X-Fremforge-Delivery: ffm-attempt-6d0f…
X-Fremforge-Timestamp: 1791108764
X-Fremforge-Signature: t=1791108764,v1=5d41402abc4b2a76b9719d911017c592…v1 är HMAC-SHA256 i hex, med målets signeringshemlighet som nyckel, över tidsstämpeln, en punkt och det råa innehållet: <t>.<body>. Verifiera den över de byte du tog emot, innan du tolkar dem. Avvisa en tidsstämpel som är mer än några minuter gammal. Efter en rotation av hemligheten innehåller X-Fremforge-Signature-Previous samma signatur gjord med den gamla hemligheten i 7 dagar.
import hmac, hashlib, time
def verify(headers, raw_body: bytes, secret: str) -> None:
parts = dict(p.split('=', 1) for p in headers['X-Fremforge-Signature'].split(','))
ts, sig = parts['t'], parts['v1']
if abs(time.time() - int(ts)) > 300:
raise ValueError('stale')
expected = hmac.new(secret.encode(), ts.encode() + b'.' + raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
raise ValueError('bad signature')Ett mål kan också ha en extra header, för mottagare som autentiserar med en header i stället för med signatur. ServiceNow är en sådan: Authorization: Basic …. Värdet sparas krypterat och visas aldrig igen. Headers som skulle förstöra förfrågan (Host, Content-Type, X-Fremforge-*, proxyheaders och liknande) avvisas.
Leverans
- Nya försök: ett svar som inte är
2xx, en timeout eller ett anslutningsfel försöks igen efter 2 minuter, sedan efter 5 och 15 minuter, 1 timme och 6 timmar. Det blir sex försök totalt under ungefär 7,5 timmar. Efter det sjätte markeras leveransendlqoch finns kvar i loggen. - Mål: en publik HTTPS-URL. Privata adresser, loopback-, link-local- och metadataadresser avvisas. Förfrågningar går ut via fremforges proxy för utgående trafik.
- Hälsa: efter fem misslyckade leveranser i rad får organisationens faktureringskontakter ett mejl som namnger målet.
- Logg: varje försök, med status, HTTP-svar och fel, finns i
GET /orgs/{org}/webhooks/deliveries?destination_id={id}.
Timgräns
Varje mål tar emot högst security_hourly_cap säkerhetshändelser per klocktimme (UTC). Standard är 100 och högsta värde 1000. Händelser över gränsen levereras inte; de visas i leveransloggen som suppressed. Efter timmen talar en händelse security.summary om hur många som hölls tillbaka, per händelsenamn:
{
"event": "security.summary",
"title": "240 security event(s) held back by the hourly cap (2026-10-04T10:00 UTC)",
"details": {
"hour_start": "2026-10-04T10:00:00.000Z",
"suppressed_total": 240,
"suppressed_by_event": { "security.finding.created": 240 }
}
}Gränsen skyddar ditt ärendesystem mot en första skanning av en stor monorepo. Själva fynden finns alla i konsolen och i Findings API.
Ställ in det i konsolen
Öppna Webhooks i din organisations administration (https://frem.sh/<org>/_admin/webhooks). Lägg till ett mål, eller välj snabbstarten ServiceNow-ärenden. Sedan, under Säkerhetshändelser för målet:
- kryssa i de händelser som ska skickas;
- välj Fynd som skickas: Endast kritiska eller Kritiska och höga;
- ange Gräns per timme;
- välj Format: Generisk JSON (signerad) eller ServiceNow-ärende (Table API), med de valfria fälten för ServiceNow-ärenden;
- ange eventuellt en Auth-header.
Skicka test köar en händelse security.test och visar om den levererades och vad mottagaren svarade. Ett mål som redan tar emot alla repo-händelser (en tom händelselista) kan inte begränsas till säkerhetshändelser. Skapa ett separat mål för dem.
Ställ in det med API:t
Scope webhooks:write. Skapa ett mål som prenumererar på kritiska fynd och fynd med hög allvarlighetsgrad samt skadlig kod:
curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/acme/webhooks/destinations" \
-H "Authorization: Bearer $FREMFORGE_TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "SOC intake",
"target_url": "https://soc.example.com/hooks/fremforge",
"events": ["security.finding.created", "security.malware.matched", "security.pull.blocked"],
"security_min_severity": "high",
"security_hourly_cap": 200
}'Svaret innehåller signeringshemligheten secret en gång. Spara den hos din mottagare.
Ändra en inställning senare med PATCH /orgs/{org}/webhooks/destinations/{id}: events, payload_format, security_min_severity, security_hourly_cap, servicenow, auth_header, name, target_url eller active. "auth_header": null tar bort headern.
Skicka en testhändelse:
curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/acme/webhooks/destinations/$ID/test" \
-H "Authorization: Bearer $FREMFORGE_TOKEN"
# {"id":"…","attempt_id":"…","event":"security.test","status":"queued"}
curl -sS "https://frem.sh/_app/api/v1/orgs/acme/webhooks/deliveries?destination_id=$ID&limit=5" \
-H "Authorization: Bearer $FREMFORGE_TOKEN"Testet går samma väg som en riktig händelse: målets format, signatur, auth-header, proxy och nya försök. Slutpunkten svarar 202. Ett pausat mål ("active": false) svarar 409, så återuppta det först.
Relaterat
- ServiceNow: ärenden från säkerhetshändelser.
- Vidarebefordran till SIEM: hela granskningsloggen, för detekteringsregler snarare än ärenden.
- Webhooks: repo-händelser från Forgejo.