Gå til hovedinnhold

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

HendelseSendes nårAlvorlighetsgrad
security.finding.createdEt nytt åpent funn dukker opp: sårbarhet i en avhengighet eller en container, SAST, secret, lisenspolicycritical, eller high hvis du velger det
security.malware.matchedSkadevare i en opplasting (ClamAV), eller en kjent ondsinnet pakke (OSV MAL-*) i SBOM-en for en standardbranch eller et imagecritical
security.pull.blockedEn pull av en container eller en pakke ble nektet av skadevaresperren eller av image-skanningspolicyen din block_pullhigh
security.policy.violationEt brudd på autentiseringspolicyen (tokenets levetid, maskinvarenøkkel, SSH-sertifikat, deploy-nøkkel), eller en push som push-beskyttelsen avvistehigh
security.exception.expiredEn avvisning (risikoaksept) med utløpsdato har løpt ut, og funnet er åpent igjeningen
security.exception.requestedNoen 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.approvedEn godkjenner godkjente et unntak for et funn; funnet er avvistfunnets, der det har en
security.exception.rejectedEn godkjenner avslo et unntak for et funn; funnet forblir åpentfunnets, 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
  }
}
FeltBetydning
eventEt av hendelsesnavnene ovenfor
event_idStabil per hendelse. Bruk den til å forkaste en levering du allerede har behandlet
occurred_atNår funnet, nektingen eller bruddet skjedde (UTC)
severitycritical, high eller null
sourceHva som produserte den: dep, image, sast, secret, license, malware, dependency_malware, push_protection, auth_policy, registry, eller et skannernavn for et utløp
correlation_keyFunnets identitet. Den samme nøkkelen brukes for alle hendelser om det samme funnet
console_urlKonsollsiden som viser det
detailsHendelsesspesifikke 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 leveringen dlq og 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