Custom webhook
Custom webhooks allow you to receive GitGuardian notifications on any server that accepts incoming json-encoded HTTP "POST" requests.
We use HMAC with sha256 as a hash function to sign the payload of our requests. The key used is a string concatenation of the timestamp and the signature token. This allows you to check that requests are coming from GitGuardian and that the payload was not altered during transport. See below how to implement the verification procedure. You can set the signature token in your settings.
The “Timestamp” field in the header counters replay attacks. If your current timestamp differs from our sending “Timestamp” by more than a few seconds, it is safer to drop the request.
A custom header can also be added to the requests from your settings to specify, for example, the environment or service.
Events
GitGuardian Internal Monitoring allows you to subscribe to the following groups of events
Incidents
Name | Description |
---|---|
Incident triggered | A new incident has been detected |
Incident resolved | This incident has been resolved |
Incident ignored | This incident has been ignored |
Incident reopened | This incident has been reopened |
Incident regression | A new regression has been found for this incident |
Incident assigned | This incident has been assigned to a user |
Incident reassigned | This incident has been reassigned to a different user |
Incident unassigned | This incident has been unassigned |
Incident Severity changed | The severity has been updated for this incident |
Incident Validity changed | The validity has been updated for this incident |
Incident access granted | A user has been granted access to this incident |
Incident access revoked | Access to this incident has been revoked for a user |
Incident shared publicly | A user has generated a public sharing link for this incident |
Incident unshared publicly | A user has deactivated the public sharing link for this incident |
Occurrences
Name | Description |
---|---|
New occurrence | A new occurrence has been detected for this incident |
Incidents notes
Name | Description |
---|---|
Incident note created | A new note has been created for this incident |
How to create a custom webhook endpoint
- Navigate to Settings > Workspace > Integrations > Custom webhook
For personal workspace
Create a new custom webhook with the name of your webhook and the URL where you want to receive GitGuardian notifications. GitGuardian generates a default signature token for you to verify the authenticity of the webhook. The signature token can be edited, make sure to store it in a safe place as you won't be able to access it again after creating the webhook.
Select the events you would like to subscribe to and receive.
Configure the endpoint on your side to verify the incoming request and handle GitGuardian alerts.
For business workspace
- Create a new custom webhook at team level.
- If you intend to activate the custom webhook for ALL incidents within the workspace, you should create it within the 'All-incidents team'.
- If you intend to activate the custom webhook for incidents within a particular team, you should create it within that team.
This can be done directly from the integration page:
or from the team page:
3. Select the events you would like to subscribe to and receive.
4. Configure the endpoint on your side to verify the incoming request and handle GitGuardian alerts.
How to test the webhook
You can send a test message from the GitGuardian workspace in order to check that your webhook is operational. Here is a sample payload of the test message you will receive:
{
"author": {
"info": "sample@sample.sample",
"name": "Sample Sample"
},
"origin": "GitGuardian",
"date": "2042-10-10 04:00:00 PM",
"type": "Welcome Message Token",
"policy": "Secrets detection",
"gitguardian_link": "http://dashboard.gitguardian.com/workspace/34123/incidents/8213",
"break_url": "github.com/sample_user/sample_repo/compare#ae32df",
"severity": "unknown",
"validity": "invalid",
"matches": [
{
"type": "client_id",
"match": "--censored--",
"index_start": 74,
"index_end": 86,
"pre_line_start": 2,
"pre_line_end": 2,
"post_line_start": 3,
"post_line_end": 3
},
{
"type": "client_secret",
"match": "--censored--",
"index_start": 30,
"index_end": 44,
"pre_line_start": 1,
"pre_line_end": 1,
"post_line_start": 2,
"post_line_end": 2
}
]
}
Note: your response message will contain the matched secret in plain text and will not show as
--censored--
as shown above.
How to verify the payload signature
We use HMAC with SHA256 as a hash function to sign the payload of our requests. The key used is a string concatenation of the timestamp and the signature token. This allows you to check that requests are coming from GitGuardian and that the payload was not altered during transport.
See below how to implement the verification procedure. You can set the signature token in your settings.
The Timestamp
field in the header counters replay attacks. If your current timestamp differs from our sending Timestamp
by more than a few seconds, it is safer to drop the request.
The Gitguardian-Signature
replaces the old header X-GitGuardian-Signature
, which is still available but deprecated as we switch to the new.
Python3 example
Here is a python3 example of how to verify the signature of our notifications:
- signature is from our request's headers
- timestamp is from our request's headers
- signature_token is the token that you have specified in the settings
- payload is our request body, decoded from “utf-8” (i.e. string representing our json)
import hmac
import hashlib
def verify_signature(signature: str, timestamp: str, signature_token: str, payload: str) -> bool:
if not signature.startswith("sha256="):
return False
signature = signature.split("sha256=")[-1]
hmac_digest = hmac.new(key=bytes(timestamp + signature_token, "utf-8"),
msg=bytes(payload, "utf-8"),
digestmod=hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, hmac_digest)
Here is a very basic unit test that lets you check that your implementation is correct:
assert verify_signature_gitguardian(
signature="sha256=172fe3d694b734aa53dc892fd3b8d62163fc240064de570ba006900bb54a0fc2",
timestamp="0",
signature_token="foo",
payload="bar"
)
AWS Lambda example
When using AWS lambda, a HTTP API Gateway must be used as trigger.
Below are two examples, respectively in Javascript and Python, on how to validate the payload received from the dashboard.
Javascript
const { createHmac } = require('crypto')
function verifySignature(signature, timestamp, signatureToken, payload) {
var signatureHeader = signature.substring(0, 7)
if (signatureHeader !== 'sha256=') {
return false
}
var signatureActual = signature.split('=')[1]
var hmac = createHmac(
'sha256',
Buffer.from(timestamp + signatureToken, 'utf8')
)
hmac.update(payload)
var result = hmac.digest('hex')
if (result === signatureActual) {
return true
} else {
return false
}
}
exports.handler = async (event) => {
const payload_signature = event.headers['gitguardian-signature']
const timestamp = event.headers['timestamp']
const webhook_token = '<INSERT SIGNATURE TOKEN HERE>'
const payload = event.body
let statusCode
if (verifySignature(payload_signature, timestamp, webhook_token, payload)) {
console.log('OK')
statusCode = 200
} else {
statusCode = 400
throw new Error()
}
const response = {
statusCode,
}
return response
}
Python
import hmac
import hashlib
import json
def verify_signature(signature: str, timestamp: str, signature_token: str, payload: str) -> bool:
if not signature.startswith("sha256="):
return False
signature = signature.split("sha256=")[-1]
hmac_digest = hmac.new(key=bytes(timestamp + signature_token, "utf-8"),
msg=bytes(payload, "utf-8"),
digestmod=hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, hmac_digest)
def lambda_handler(event, context):
payload_dump = event["body"]
timestamp = event["headers"]["timestamp"]
payload_signature = event["headers"]["gitguardian-signature"]
webhook_token = "<INSERT SIGNATURE TOKEN HERE>"
if verify_signature(payload_signature, timestamp, webhook_token, payload_dump):
return {'statusCode': 200, 'body': json.dumps('Success') }
return {'statusCode': 400, 'body': json.dumps('Failed to verify') }
Payload structure
The payloads are described by the OpenAPI specification. Below are a few examples for the different events you can subscribe to.
Incident events payloads
New incident
{
"source": "GitGuardian",
"timestamp": "2022-06-20T07:45:32.930965Z",
"action": "incident_triggered",
"message": "A new incident has been detected.",
"target_user": "GitGuardian",
"incident": {
"id": 31542,
"date": "2022-06-20T07:45:31.666462Z",
"detector": {
"name": "generic_password",
"display_name": "Generic Password",
"nature": "generic",
"family": "Other",
"detector_group_name": "generic_password",
"detector_group_display_name": "Generic Password"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 0,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "unknown",
"validity": "no_checker",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Assign event
{
"source": "GitGuardian",
"timestamp": "2022-06-17T12:18:41.917977Z",
"action": "incident_assigned",
"message": "This incident has been assigned to a user.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31450,
"date": "2022-06-15T09:16:42.378417Z",
"detector": {
"name": "generic_password",
"display_name": "Generic Password",
"nature": "generic",
"family": "Other",
"detector_group_name": "generic_password",
"detector_group_display_name": "Generic Password"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "assigned",
"regression": false,
"assignee_email": "bruce.wayne@gitguardian.com",
"severity": "medium",
"validity": "no_checker",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Re-assign event
{
"source": "GitGuardian",
"timestamp": "2022-06-17T12:18:41.917977Z",
"action": "incident_reassigned",
"message": "This incident has been reassigned to a different user.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31450,
"date": "2022-06-15T09:16:42.378417Z",
"detector": {
"name": "generic_password",
"display_name": "Generic Password",
"nature": "generic",
"family": "Other",
"detector_group_name": "generic_password",
"detector_group_display_name": "Generic Password"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "assigned",
"regression": false,
"assignee_email": "bruce.wayne@gitguardian.com",
"severity": "medium",
"validity": "no_checker",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Severity change
{
"source": "GitGuardian",
"timestamp": "2022-06-17T12:18:22.220508Z",
"action": "incident_severity_changed",
"message": "The severity has been updated for this incident.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31450,
"date": "2022-06-15T09:16:42.378417Z",
"detector": {
"name": "generic_password",
"display_name": "Generic Password",
"nature": "generic",
"family": "Other",
"detector_group_name": "generic_password",
"detector_group_display_name": "Generic Password"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "medium",
"validity": "no_checker",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Validity change
{
"source": "GitGuardian",
"timestamp": "2022-06-17T12:18:22.220508Z",
"action": "incident_validity_changed",
"message": "The validity has been updated for this incident.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31450,
"date": "2022-06-15T09:16:42.378417Z",
"detector": {
"name": "generic_password",
"display_name": "Generic Password",
"nature": "generic",
"family": "Other",
"detector_group_name": "generic_password",
"detector_group_display_name": "Generic Password"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "medium",
"validity": "invalid",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Incident resolved
{
"source": "GitGuardian",
"timestamp": "2022-06-22T09:00:16.143457Z",
"action": "incident_resolved",
"message": "This incident has been resolved.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31605,
"date": "2022-06-16T08:23:40Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": true,
"occurrence_count": 1,
"status": "resolved",
"regression": false,
"assignee_email": null,
"severity": "high",
"validity": "not_checked",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": "2022-06-22T09:00:16.038050Z",
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Incident ignored
{
"source": "GitGuardian",
"timestamp": "2022-06-22T09:02:57.377837Z",
"action": "incident_ignored",
"message": "This incident has been ignored.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31605,
"date": "2022-06-16T08:23:40Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "ignored",
"regression": false,
"assignee_email": null,
"severity": "high",
"validity": "not_checked",
"ignored_at": "2022-06-22T09:02:57.292217Z",
"ignore_reason": "low_risk",
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Incident reopened
{
"source": "GitGuardian",
"timestamp": "2022-06-22T09:03:10.775369Z",
"action": "incident_reopened",
"message": "This incident has been reopened.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31605,
"date": "2022-06-16T08:23:40Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "ignored",
"regression": false,
"assignee_email": null,
"severity": "high",
"validity": "not_checked",
"ignored_at": "2022-06-22T09:02:57.292217Z",
"ignore_reason": "low_risk",
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Incident regression
{
"source": "GitGuardian",
"timestamp": "2022-06-28T09:10:19.966461Z",
"action": "incident_regression",
"message": "A new regression was found for this incident.",
"target_user": "GitGuardian",
"incident": {
"id": 1234,
"date": "2022-06-28T09:10:18.613418Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": true,
"occurrence_count": 4,
"status": "triggered",
"regression": true,
"assignee_email": null,
"severity": "high",
"validity": "invalid",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Incident access granted
{
"source": "GitGuardian",
"timestamp": "2022-06-28T09:15:55.682589Z",
"action": "incident_access_granted",
"message": "A user has been granted access to this incident.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 3831600,
"date": "2022-06-28T00:03:46.110078Z",
"detector": {
"name": "stripe",
"display_name": "Stripe Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "stripe_keys",
"detector_group_display_name": "Stripe Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 2,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "unknown",
"validity": "invalid",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/8/incidents/xxx",
"share_url": "https://dashboard.gitguardian.com/share/incidents/xxx"
},
"member": {
"id": 3252,
"name": "John Smith",
"email": "john.smith@example.org",
"role": "owner"
}
}
member
provides details about the member who have been granted access during this event. If multiple members were granted access, there will be one event for each access granted.
Incident access revoked
{
"source": "GitGuardian",
"timestamp": "2022-06-28T09:17:01.353280Z",
"action": "incident_access_revoked",
"message": "Access to this incident has been revoked for a user.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 3831600,
"date": "2022-06-28T00:03:46.110078Z",
"detector": {
"name": "stripe",
"display_name": "Stripe Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "stripe_keys",
"detector_group_display_name": "Stripe Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 2,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "unknown",
"validity": "invalid",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/8/incidents/xxx",
"share_url": "https://dashboard.gitguardian.com/share/incidents/xxx"
},
"member": {
"id": 3252,
"name": "John Smith",
"email": "john.smith@example.org",
"role": "owner"
}
}
member
provides details about the member who have been revoked access during this event. If multiple members were granted access, there will be one event for each access granted.
Incident shared publicly
{
"source": "GitGuardian",
"timestamp": "2022-06-28T08:48:49.290758Z",
"action": "incident_shared_publicly",
"message": "A user has generated a public sharing link for this incident.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 3827964,
"date": "2022-06-23T14:34:11Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "unknown",
"validity": "invalid",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xx",
"share_url": "https://dashboard.gitguardian.com/share/incidents/xxx"
}
}
Incident publicly unshared
{
"source": "GitGuardian",
"timestamp": "2022-06-28T08:49:56.806741Z",
"action": "incident_unshared_publicly",
"message": "A user has deactivated the public sharing link for this incident.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 3827964,
"date": "2022-06-23T14:34:11Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 2,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "unknown",
"validity": "invalid",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
}
}
Occurrences events payload
New occurrence
{
"source": "GitGuardian",
"timestamp": "2022-06-23T09:10:24.594597Z",
"action": "new_occurrence",
"message": "A new occurrence has been detected for this incident.",
"target_user": "GitGuardian",
"incident": {
"id": 31605,
"date": "2022-06-16T08:23:40Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 5,
"status": "assigned",
"regression": false,
"assignee_email": "bruce.wayne@gitguardian.com",
"severity": "high",
"validity": "not_checked",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
},
"occurrence": {
"id": 1234,
"incident_id": 1243,
"kind": "RLTM",
"sha": "xxx",
"author_name": "GitHub",
"author_info": "noreply@github.com",
"date": "2022-06-23T09:10:23.529812Z",
"presence": "visible",
"url": "https://github.com/user/repo/commit/123#diff-xxx",
"matches": [],
"filepath": "TestJS.js",
"source": {
"id": 710,
"url": "https://github.com/path-to-repository",
"type": "github",
"full_name": "name of repository",
"health": "at_risk",
"open_incidents_count": 5,
"closed_incidents_count": 0,
"visibility": "private",
"last_scan": {
"status": "finished",
"date": "2022-11-18T17:07:59.079520Z"
},
"external_id": "github_id"
}
}
}
Notes events payloads
New incident note
{
"source": "GitGuardian",
"timestamp": "2022-06-22T09:11:02.733441Z",
"action": "incident_note_created",
"message": "A new note has been created for this incident.",
"target_user": "John Doe john.doe@gitguardian.com",
"incident": {
"id": 31605,
"date": "2022-06-16T08:23:40Z",
"detector": {
"name": "aws_iam",
"display_name": "AWS Keys",
"nature": "specific",
"family": "Api",
"detector_group_name": "aws_iam",
"detector_group_display_name": "AWS Keys"
},
"secret_hash": "xxx",
"secret_revoked": false,
"occurrence_count": 1,
"status": "triggered",
"regression": false,
"assignee_email": null,
"severity": "high",
"validity": "not_checked",
"ignored_at": null,
"ignore_reason": null,
"resolved_at": null,
"gitguardian_url": "https://dashboard.gitguardian.com/workspace/1/incidents/xxx",
"share_url": null
},
"incident_note": {
"id": 46389,
"api_token": null,
"created_at": "2022-06-22T09:11:02.683727Z",
"updated_at": null,
"comment": "This is not a test"
}
}
Custom webhook v1 (deprecated)
Custom webhooks v1 have been deprecated on July 11, 2023 and have been fully replaced by the event-based custom webhooks.
How to integrate
Navigate to Settings > Workspace > Integrations > Custom webhook
Create a new custom webhook with the name of your webhook and the URL where you want to receive GitGuardian notifications.
We propose a default signature token which you can edit in order to verify that the post request comes from GitGuardian. Make sure to store the signature token in a safe place as you won't be able to access it again. Lastly, choose when you want to receive notifications.
Configure the endpoint on your side to verify the request and handle GitGuardian alerts (see details below)
Payloads
The payload is described by the following openAPI structure:
scan_result:
type: object
title: Scan Result
description: Result of a policy evaluation.
required:
- author
- origin
- date
- type
- policy
- gitguardian_link
- break_url
- severity
- matches
properties:
author:
type: object
title: Author
properties:
info:
type: string
description: author contact.
name:
type: string
description: author name.
origin:
type: string
description: origin of message.
date:
type: string
description: date of detection.
type:
type: string
description: incident type.
policy:
type: string
description: policy responsible for detection.
gitguardian_link:
type: string
description: url of incident on the dashboard.
break_url:
type: string
description: url of found incident.
severity:
type: string
description: incident severity.
enum: [critical, high, medium, low, info, unknown]
validity:
type: string
description: incident validity.
enum: [cannot_check, valid, invalid, unknown]
matches:
type: array
description: list of incidents matches.
minItems: 0
items:
$ref: '#/components/schemas/match'
match:
type: object
required:
- type
- match
properties:
type:
type: string
description: Type of match.
match:
type: string
description: Matched string of failure. If it's a secret it won't correspond to the real match.
pre_line_start:
type: integer
description: start line number of match (index origin = 1) in previous patch.
pre_line_end:
type: integer
description: end line number of match (index origin = 1) in previous patch.
post_line_start:
type: integer
description: start line number of match (index origin = 1) in post patch.
post_line_end:
type: integer
description: end line number of match (index origin = 1) in post patch.
index_start:
type: integer
description: start index of match in file as an array (index origin = 0).
index_end:
type: integer
description: end index of match in file as an array (index origin = 0).
You may send a test message in order to check that everything works, here is a sample payload of the test message you will receive:
{
"author": {
"info": "sample@sample.sample",
"name": "Sample Sample"
},
"origin": "GitGuardian",
"date": "2042-10-10 04:00:00 PM",
"type": "Welcome Message Token",
"policy": "Secrets detection",
"gitguardian_link": "http://dashboard.gitguardian.com/workspace/34123/incidents/8213",
"break_url": "github.com/sample_user/sample_repo/compare#ae32df",
"severity": "unknown",
"validity": "invalid",
"matches": [
{
"type": "client_id",
"match": "--censored--",
"index_start": 74,
"index_end": 86,
"pre_line_start": 2,
"pre_line_end": 2,
"post_line_start": 3,
"post_line_end": 3
},
{
"type": "client_secret",
"match": "--censored--",
"index_start": 30,
"index_end": 44,
"pre_line_start": 1,
"pre_line_end": 1,
"post_line_start": 2,
"post_line_end": 2
}
]
}
Verifying the payload signature
Here is a python3 example of how to verify the signature of our notifications:
- signature is from our request's headers
- timestamp is from our request's headers
- signature_token is the token that you have specified in the settings
- payload is our request body, decoded from “utf-8” (i.e. string representing our json)
import hmac
import hashlib
def verify_signature(signature: str, timestamp: str, signature_token: str, payload: str) -> bool:
if not signature.startswith("sha256="):
return False
signature = signature.split("sha256=")[-1]
hmac_digest = hmac.new(key=bytes(timestamp + signature_token, "utf-8"),
msg=bytes(payload, "utf-8"),
digestmod=hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, hmac_digest)
Here is a very basic unit test that lets you check that your implementation is correct:
assert verify_signature_gitguardian(
signature="sha256=172fe3d694b734aa53dc892fd3b8d62163fc240064de570ba006900bb54a0fc2",
timestamp="0",
signature_token="foo",
payload="bar"
)
Using AWS Lambda
When using AWS lambda, a HTTP API Gateway must be used as trigger.
Below are two examples, respectively in Javascript and Python, on how to validate the payload received from the dashboard.
Javascript
const { createHmac } = require('crypto')
function verifySignature(signature, timestamp, signatureToken, payload) {
var signatureHeader = signature.substring(0, 7)
if (signatureHeader !== 'sha256=') {
return false
}
var signatureActual = signature.split('=')[1]
var hmac = createHmac(
'sha256',
Buffer.from(timestamp + signatureToken, 'utf8')
)
hmac.update(payload)
var result = hmac.digest('hex')
if (result === signatureActual) {
return true
} else {
return false
}
}
exports.handler = async (event) => {
const payload_signature = event.headers['x-gitguardian-signature']
const timestamp = event.headers['timestamp']
const webhook_token = '<INSERT SIGNATURE TOKEN HERE>'
const payload = event.body
let statusCode
if (verifySignature(payload_signature, timestamp, webhook_token, payload)) {
console.log('OK')
statusCode = 200
} else {
statusCode = 400
throw new Error()
}
const response = {
statusCode,
}
return response
}
Python
import hmac
import hashlib
import json
def verify_signature(signature: str, timestamp: str, signature_token: str, payload: str) -> bool:
if not signature.startswith("sha256="):
return False
signature = signature.split("sha256=")[-1]
hmac_digest = hmac.new(key=bytes(timestamp + signature_token, "utf-8"),
msg=bytes(payload, "utf-8"),
digestmod=hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, hmac_digest)
def lambda_handler(event, context):
payload_dump = event["body"]
timestamp = event["headers"]["timestamp"]
payload_signature = event["headers"]["x-gitguardian-signature"]
webhook_token = "<INSERT SIGNATURE TOKEN HERE>"
if verify_signature(payload_signature, timestamp, webhook_token, payload_dump):
return {'statusCode': 200, 'body': json.dumps('Success') }
return {'statusCode': 400, 'body': json.dumps('Failed to verify') }