Varselwebhooks for sikkerhet
Sikkerhetsvarsler når folk på e-post. For også å nå hendelses- eller ticket-systemet ditt abonnerer du et utgående webhook-mål på én eller flere sikkerhetshendelser. Hver hendelse er én signert HTTPS-POST, som prøves på nytt til mottakeren din godtar den, og som registreres i leveringsloggen.
To formater for innholdet er tilgjengelige per mål:
- Generic (standard): fremforges JSON-hendelse som er beskrevet nedenfor.
- ServiceNow: en hendelsespost som ServiceNows Table API godtar som den er. Se ServiceNow.
Hendelser
| Hendelse | Sendes når | Alvorlighetsgrad |
|---|---|---|
security.finding.created | Et nytt åpent funn dukker opp: sårbarhet i en avhengighet eller en container, SAST, secret, lisenspolicy | critical, eller high hvis du velger det |
security.malware.matched | Skadevare i en opplasting (ClamAV), eller en kjent ondsinnet pakke (OSV MAL-*) i SBOM-en for en standardbranch eller et image | critical |
security.pull.blocked | En pull av en container eller en pakke ble nektet av skadevaresperren eller av image-skanningspolicyen din block_pull | high |
security.policy.violation | Et brudd på autentiseringspolicyen (tokenets levetid, maskinvarenøkkel, SSH-sertifikat, deploy-nøkkel), eller en push som push-beskyttelsen avviste | high |
security.exception.expired | En avvisning (risikoaksept) med utløpsdato har løpt ut, og funnet er åpent igjen | ingen |
security.exception.requested | Noen ba om et unntak for et funn, eller avviste et mens avvisninger krever godkjenning (tittelen sier det da) | funnets, der det har en |
security.exception.approved | En godkjenner godkjente et unntak for et funn; funnet er avvist | funnets, der det har en |
security.exception.rejected | En godkjenner avslo et unntak for et funn; funnet forblir åpent | funnets, der det har en |
To hendelsesnavn til når et abonnert mål uten at det abonneres på dem:
security.summary: sendes etter en time der timegrensen holdt tilbake hendelser.security.test: sendes av testknappen eller testendepunktet.
Sikkerhetshendelser må velges ved navn. Et mål med en tom hendelsesliste mottar alle hendelser fra Forgejo, status og vedlikehold, men ingen sikkerhetshendelser. List opp sikkerhetshendelsene du vil ha, eksplisitt.
Et abonnement starter når du oppretter det. Funn som fantes før du abonnerte, spilles ikke av på nytt. Bruk Findings-API-et for det nåværende etterslepet.
Én hendelse per funn. En ny skanning sender ikke et funn på nytt. Dets correlation_key er den samme for alle hendelser om det, inkludert hendelsen security.exception.expired når en risikoaksept for det løper ut.
Alvorlighetsgrad. security.finding.created sendes som standard for funn med critical. Sett security_min_severity til high for også å motta høye funn. SAST-feil teller som high. Secrets teller som critical, og det samme gjør forbudte lisenser. Begrensede lisenser teller som high. De andre hendelsene sendes alltid.
Innhold
{
"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
}
}| Felt | Betydning |
|---|---|
event | Et av hendelsesnavnene ovenfor |
event_id | Stabil per hendelse. Bruk den til å forkaste en levering du allerede har behandlet |
occurred_at | Når funnet, nektingen eller bruddet skjedde (UTC) |
severity | critical, high eller null |
source | Hva som produserte den: dep, image, sast, secret, license, malware, dependency_malware, push_protection, auth_policy, registry, eller et skannernavn for et utløp |
correlation_key | Funnets identitet. Den samme nøkkelen brukes for alle hendelser om det samme funnet |
console_url | Konsollsiden som viser det |
details | Hendelsesspesifikke felt. Verdier av secrets tas aldri med: et secret-funn har bare regelen, filen og linjen |
Signatur og headere
Hver forespørsel har:
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 er den heksadesimale HMAC-SHA256-en, med målets signeringssecret som nøkkel, over tidsstempelet, et punktum og det rå innholdet: <t>.<body>. Verifiser den over bytene du mottok, før du parser dem. Avvis et tidsstempel som er mer enn noen få minutter gammelt. Etter en rotasjon av secreten har X-Fremforge-Signature-Previous den samme signaturen laget med den gamle secreten i 7 dager.
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')Et mål kan også ha én ekstra header, for mottakere som autentiserer med en header i stedet for med en signatur. ServiceNow er en av dem: Authorization: Basic …. Verdien lagres kryptert og vises aldri igjen. Headere som ville ødelagt forespørselen (Host, Content-Type, X-Fremforge-*, proxyheadere og lignende), avvises.
Levering
- Nye forsøk: et svar som ikke er
2xx, et tidsavbrudd eller en tilkoblingsfeil prøves på nytt etter 2 minutter, deretter etter 5 og 15 minutter, 1 time og 6 timer. Det gir seks forsøk i alt over omtrent 7,5 timer. Etter det sjette merkes leveringendlqog blir stående i loggen. - Mål: en offentlig HTTPS-URL. Private adresser, loopback, link-local og metadataadresser avvises. Forespørslene går ut gjennom fremforges utgående proxy.
- Helse: etter fem mislykkede leveringer på rad får organisasjonens faktureringskontakter en e-post som navngir målet.
- Logg: hvert forsøk, med status, HTTP-svar og feil, finnes i
GET /orgs/{org}/webhooks/deliveries?destination_id={id}.
Timegrense
Hvert mål godtar høyst security_hourly_cap sikkerhetshendelser per klokketime (UTC). Standard er 100 og maksimum 1000. Hendelser over grensen leveres ikke; de vises i leveringsloggen som suppressed. Etter timen sier én security.summary-hendelse hvor mange som ble holdt tilbake, per hendelsesnavn:
{
"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 }
}
}Grensen beskytter ticket-systemet ditt mot den første skanningen av et stort monorepo. Selve funnene ligger alle i konsollen og i Findings-API-et.
Sett det opp i konsollen
Åpne Webhooks i organisasjonsadministrasjonen (https://frem.sh/<org>/_admin/webhooks). Legg til et mål, eller velg hurtigstarten ServiceNow incidents. Deretter, under Security events på målet:
- kryss av for hendelsene som skal sendes;
- velg Funn som skal sendes: Critical only eller Critical and high;
- sett Timegrense;
- velg Body format: Generic JSON (signed) eller ServiceNow incident (Table API), med de valgfrie feltene for ServiceNow-hendelser;
- sett eventuelt en Autentiseringsheader.
Send test setter en security.test-hendelse i kø og viser om den ble levert og hva mottakeren svarte. Et mål som allerede mottar alle repositoryhendelser (en tom hendelsesliste), kan ikke snevres inn til sikkerhetshendelser. Opprett et eget mål for dem.
Sett det opp med API-et
Scope webhooks:write. Opprett et mål som abonnerer på kritiske og høye funn og skadevare:
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 inneholder signeringssecreten secret én gang. Lagre den hos mottakeren din.
Endre en innstilling senere 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 fjerner headeren.
Send en testhendelse:
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"Testen går gjennom den samme veien som en ekte hendelse: målets format, signatur, autentiseringsheader, proxy og nye forsøk. Endepunktet svarer 202. Et pauset mål ("active": false) svarer 409, så gjenoppta det først.
Relatert
- ServiceNow: hendelser fra sikkerhetshendelser.
- SIEM-videresending: hele revisjonsloggen, for deteksjonsregler i stedet for tickets.
- Webhooks: repositoryhendelser fra Forgejo.