ServiceNow
fremforge can open a ServiceNow incident for each security event: a new critical or high finding, a malware match, a blocked pull, a policy violation, or a lapsed risk acceptance. The destination posts straight to the Table API (POST /api/now/table/incident), so ServiceNow needs only an integration user, and no scripted REST API or MID server.
1. ServiceNow side: an integration user
- Create a user for fremforge, for example
fremforge.integration. Tick Web service access only. - Give it a role that may create incidents through the Table API, typically
itil, or a narrower custom role withcreateonincident. - Choose how fremforge authenticates:
- Basic: the user’s name and password, sent as
Authorization: Basic <base64 of user:password>. - OAuth bearer: an OAuth token for that user, sent as
Authorization: Bearer <token>. A ServiceNow OAuth access token expires, so an expiring token has to be replaced on the destination before it runs out. Basic is the simpler choice for a machine user.
- Basic: the user’s name and password, sent as
- Optional: note the
sys_idof the assignment group that should receive the incidents, and of the caller to record.
ServiceNow must accept requests from fremforge’s egress address. If your instance restricts source IPs, ask support for the current address.
2. fremforge side: a destination in ServiceNow format
With the API (scope webhooks:write):
AUTH="Basic $(printf '%s' 'fremforge.integration:<password>' | base64)"
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 @- <<EOF
{
"name": "ServiceNow incidents",
"target_url": "https://acme.service-now.com/api/now/table/incident",
"events": ["security.finding.created", "security.malware.matched",
"security.pull.blocked", "security.policy.violation",
"security.exception.expired"],
"payload_format": "servicenow",
"security_min_severity": "critical",
"security_hourly_cap": 50,
"servicenow": { "assignment_group": "<sys_id>", "category": "security" },
"auth_header": { "name": "Authorization", "value": "$AUTH" }
}
EOFThe auth_header value is stored encrypted and never shown again. The answer says "auth_header_set": true. To replace the password, send a PATCH with a new auth_header.
Then send a test incident and read its result:
curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/acme/webhooks/destinations/$ID/test" \
-H "Authorization: Bearer $FREMFORGE_TOKEN"
curl -sS "https://frem.sh/_app/api/v1/orgs/acme/webhooks/deliveries?destination_id=$ID&limit=1" \
-H "Authorization: Bearer $FREMFORGE_TOKEN"A 201 in response_status means ServiceNow created the incident. A 401 means the credential is wrong. A 403 means the user lacks the role to create incidents.
What the incident contains
| Incident field | Value |
|---|---|
short_description | [fremforge] and the event title, for example [fremforge] CVE-2026-12345 in lodash 4.17.20 (at most 160 characters) |
description | Event name, organisation, repository, severity, time, correlation key, a link to the fremforge console, and the event details as JSON |
urgency, impact | 1 for critical, 2 for high, 3 otherwise |
correlation_id | fremforge: followed by the finding’s correlation key. A key too long for the field is replaced by a stable SHA-256 of it |
correlation_display | fremforge |
assignment_group, caller_id, category, subcategory | Only when set on the destination (servicenow object) |
ServiceNow computes the incident’s priority from urgency and impact as usual.
Every event creates a new incident. The Table API does not update an existing record. Events about the same finding carry the same correlation_id. One example is a finding followed later by the lapse of a risk acceptance on it. Use that value in a ServiceNow business rule or list filter to group them. The hourly cap limits how many incidents one destination can open per hour. Past the cap, one summary incident follows the hour.
The request is also signed (X-Fremforge-Signature). The Table API ignores the signature; the auth header authenticates the request.
Alternative: your own logic in a Scripted REST API
To update an existing incident instead of creating one, or to map fields differently, keep the destination’s format Generic and point it at a Scripted REST API resource. The script receives the generic body. Look up an open incident by correlation_id and update it, or insert a new one. To verify the signature in the script, compute HMAC-SHA256 over <X-Fremforge-Timestamp>.<raw body> with the destination’s signing secret and compare it with the v1 part of X-Fremforge-Signature. Protect the resource with the same integration user and the auth_header.
Related
- Security alert webhooks: events, body, signature, retries and the cap.
- SIEM forwarding: the full audit log to a SIEM.