Webhooks

How to use Outgoing Webhooks

Webhooks are based on system events, so each webhook delivery will be for a single event resource. Events are versioned and the schema for the data attribute is static within an API version. Note that the data attribute of an event resource is different from the data JSON keys that wrap API resources.

Using the Outgoing Webhooks documentation

Webhook Documentation

You will find information about the different parts of the request:

  • What triggers the webhook to make a request
  • Header parameters
  • Request body schema
  • Notes on the expected response
  • Payload examples for the event (on the right sidebar)

Getting Started

Setup a new Outgoing Webhook

This will be opened to GA soon, for now you can request access from your Account Manager

To create a new Outgoing Webhook:

  1. Go to:

    https://tracker.bugcrowd.com/YOUR_PROGRAM_CODE/settings/integrations/outgoing_webhooks/new
    
  2. Fill in a name for the webhook
  3. Fill in the consumer URL of the webhook
    • This URL will come from your internal team
  4. Select your desired triggering_event (i.e. ‘submission.updated’)
  5. Click Add & enable webhook button
  6. Ensure your API token uses version 2021-10-28 or newer to receive the event data attribute here

Deliveries page

Within the Outgoing webhooks integration, you can view past deliveries (failed/successful). This page will provide request headers, request bodies, and response data.

Example use case

  1. Submission’s severity is updated to P1
  2. Webhook consumer receives submission.updated webhook request
  3. Webhook consumer looks at change data to see Submission’s severity was updated to P1
  4. Webhook consumer creates an urgent Jira ticket and sends a Slack message for investigation

Validating the signature

GitHub reference

  1. Navigate to:

    https://tracker.bugcrowd.com/PROGRAM_CODE/settings/integrations/outgoing_webhooks
    
  2. Click the Edit button and copy the secret.

  3. Store the secret securely in your application and use it to verify the webhook request.

Ruby example

# Get secret from Outgoing Webhooks integration settings page in Crowdcontrol
secret = '75d5852d-52e1-42e5-9d65-79b73d53e45a'

# Get the raw digest from the request header `X-Bugcrowd-Digest`
raw_digest = 'timestamp=1635961427;sha256=7d85b428ba3e2f7304ffdaa122f34e6e9545e0eb6941a787e3648b9e9c719f7e'

# Get the raw body from the request - can use "copy to clipboard" from the deliveries page
raw_body = "{\"data\":{\"id\":\"036a5dc5-b6bd-4791-883b-21e18127cb6c\",\"type\":\"event\",\"attributes\":{\"created_at\":\"2021-11-03T17:21:06.038Z\",\"key\":\"submission.updated\",\"data\":{\"changes\":{\"state\":{\"to\":\"unresolved\",\"from\":\"triaged\"}},\"duplicate_ids\":[]}},\"relationships\":{\"resource\":{\"data\":{\"id\":\"060a0a4b-a846-41dc-9ef8-5bbecba8d810\",\"type\":\"submission\"},\"links\":{\"related\":{\"href\":\"/submissions/060a0a4b-a846-41dc-9ef8-5bbecba8d810\"}}},\"actor\":{\"data\":{\"id\":\"12277f0b-dbcc-48fd-830a-f7602ab5737e\",\"type\":\"identity\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}}},\"links\":{\"self\":\"(NOT IMPLEMENTED YET)\"}},\"included\":[{\"id\":\"12277f0b-dbcc-48fd-830a-f7602ab5737e\",\"type\":\"identity\",\"attributes\":{\"name\":\"admin\",\"email\":\"joker@bugcrowd.com\",\"staff\":true},\"links\":{\"self\":\"(NOT IMPLEMENTED YET)\"}},{\"id\":\"060a0a4b-a846-41dc-9ef8-5bbecba8d810\",\"type\":\"submission\",\"attributes\":{\"bug_url\":\"http://torphysteuber.co/annett\",\"custom_fields\":{},\"description\":\"#### Here's a header for a description \\n\\u003cSCRIPT SRC=//ha.ckers.org/.j\\u003e\",\"duplicate\":false,\"extra_info\":\"\",\"http_request\":\"GET / HTTP/1.1\\nHost: bugcrowd.com\\nProxy-Connection: keep-alive\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36\\nAccept-Encoding: gzip,deflate,sdch\\nAccept-Language: en-US,en;q=0.8,fr;q=0.6,mt;q=0.4\",\"last_transitioned_to_informational_at\":null,\"last_transitioned_to_not_applicable_at\":null,\"last_transitioned_to_not_reproducible_at\":null,\"last_transitioned_to_out_of_scope_at\":null,\"last_transitioned_to_resolved_at\":null,\"last_transitioned_to_triaged_at\":\"2021-11-03T17:18:51.675Z\",\"last_transitioned_to_unresolved_at\":\"2021-11-03T17:21:05.849Z\",\"remediation_advice\":\"1. If possible consider disabling external access.\\n2. Never use default credentials as it is trivial for an attacker to gain access by providing known or easy to guess credentials.\\n3. Always change any kind of default credentials as the first step of setting up any kind of environment.\\n4. Passwords should meet or exceed proper password strength requirements.\",\"severity\":1,\"source\":\"platform\",\"state\":\"unresolved\",\"submitted_at\":\"2021-07-21T05:12:28.874Z\",\"title\":\"Use the virtual JBOD alarm, then you can copy the primary hard drive!\",\"vrt_id\":\"server_security_misconfiguration.using_default_credentials\",\"vrt_version\":\"1.10.1\",\"vulnerability_references\":\"* [https://www.owasp.org/index.php/Testing_for_default_credentials_(OTG-AUTHN-002)](https://www.owasp.org/index.php/Testing_for_default_credentials_(OTG-AUTHN-002))\\n* [https://www.owasp.org/index.php/Configuration#Default_passwords](https://www.owasp.org/index.php/Configuration#Default_passwords)\\n* [https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md)\\n* [https://www.owasp.org/index.php/Top_10-2017_A6-Security_Misconfiguration](https://www.owasp.org/index.php/Top_10-2017_A6-Security_Misconfiguration)\\n* [http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration](http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration)\\n* [https://cwe.mitre.org/data/definitions/255](https://cwe.mitre.org/data/definitions/255)\\n* [https://cwe.mitre.org/data/definitions/521](https://cwe.mitre.org/data/definitions/521)\\n* [https://cwe.mitre.org/data/definitions/16](https://cwe.mitre.org/data/definitions/16)\"},\"relationships\":{\"activities\":{\"data\":[{\"id\":\"e61bc905-7758-487d-a7b2-e8ecfcafbadb\",\"type\":\"activity\"},{\"id\":\"42c7f6bc-70ac-4d5f-85a4-57e9fdf62678\",\"type\":\"activity\"},{\"id\":\"0da8db26-cdfa-4e4e-b7c1-1b5cee1c2119\",\"type\":\"activity\"},{\"id\":\"537db75f-19aa-4357-9355-cf09994eb2ec\",\"type\":\"activity\"}],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":4,\"total_hits\":4}}}},\"assignee\":{\"data\":{\"id\":\"adfa1452-02ab-4fad-ba08-a1ba5228bfa6\",\"type\":\"identity\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}},\"claim_ticket\":{\"data\":null,\"links\":{\"related\":{\"href\":null}}},\"comments\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"duplicate_of\":{\"data\":null,\"links\":{\"related\":{\"href\":null}}},\"duplicates\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"external_issues\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"file_attachments\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"monetary_rewards\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"program\":{\"data\":{\"id\":\"2322d2f6-60b7-4aa9-98bc-6bc56d69d61e\",\"type\":\"program\"},\"links\":{\"related\":{\"href\":\"/programs/2322d2f6-60b7-4aa9-98bc-6bc56d69d61e\"}}},\"researcher\":{\"data\":{\"id\":\"a275330b-117f-405c-a69b-4a35e55326b1\",\"type\":\"identity\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}},\"target\":{\"data\":{\"id\":\"ebf18692-a85b-4012-adbd-36ee916e269c\",\"type\":\"target\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}}},\"links\":{\"self\":\"/submissions/060a0a4b-a846-41dc-9ef8-5bbecba8d810\"}}]}"

# Separate the timestamp
at_time = raw_digest.split(';').first.tr('timestamp=', '')

# Separate the digest string
digest = raw_digest.split(';').last

# Generate a new signature
signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, (raw_body + at_time))

# Compare the new signature with the digest from the request
Rack::Utils.secure_compare(signature, digest)

# _To avoid timing attacks, always use `secure_compare` (which performs comparisons in constant time) instead of an equality operator like `=`._

Python example

import hmac
import json

secret = '75d5852d-52e1-42e5-9d65-79b73d53e45a'
raw_digest = 'timestamp=1635961427;sha256=7d85b428ba3e2f7304ffdaa122f34e6e9545e0eb6941a787e3648b9e9c719f7e'

# To get the raw body into the correct format from a non-compacted server, you need to specify compact formatting when dumping it to JSON:
# raw_body = json.dumps(payload, separators=(',', ':'))
# Alternatively can copy from the deliveries page
raw_body = "{\"data\":{\"id\":\"036a5dc5-b6bd-4791-883b-21e18127cb6c\",\"type\":\"event\",\"attributes\":{\"created_at\":\"2021-11-03T17:21:06.038Z\",\"key\":\"submission.updated\",\"data\":{\"changes\":{\"state\":{\"to\":\"unresolved\",\"from\":\"triaged\"}},\"duplicate_ids\":[]}},\"relationships\":{\"resource\":{\"data\":{\"id\":\"060a0a4b-a846-41dc-9ef8-5bbecba8d810\",\"type\":\"submission\"},\"links\":{\"related\":{\"href\":\"/submissions/060a0a4b-a846-41dc-9ef8-5bbecba8d810\"}}},\"actor\":{\"data\":{\"id\":\"12277f0b-dbcc-48fd-830a-f7602ab5737e\",\"type\":\"identity\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}}},\"links\":{\"self\":\"(NOT IMPLEMENTED YET)\"}},\"included\":[{\"id\":\"12277f0b-dbcc-48fd-830a-f7602ab5737e\",\"type\":\"identity\",\"attributes\":{\"name\":\"admin\",\"email\":\"joker@bugcrowd.com\",\"staff\":true},\"links\":{\"self\":\"(NOT IMPLEMENTED YET)\"}},{\"id\":\"060a0a4b-a846-41dc-9ef8-5bbecba8d810\",\"type\":\"submission\",\"attributes\":{\"bug_url\":\"http://torphysteuber.co/annett\",\"custom_fields\":{},\"description\":\"#### Here's a header for a description \\n\\u003cSCRIPT SRC=//ha.ckers.org/.j\\u003e\",\"duplicate\":false,\"extra_info\":\"\",\"http_request\":\"GET / HTTP/1.1\\nHost: bugcrowd.com\\nProxy-Connection: keep-alive\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36\\nAccept-Encoding: gzip,deflate,sdch\\nAccept-Language: en-US,en;q=0.8,fr;q=0.6,mt;q=0.4\",\"last_transitioned_to_informational_at\":null,\"last_transitioned_to_not_applicable_at\":null,\"last_transitioned_to_not_reproducible_at\":null,\"last_transitioned_to_out_of_scope_at\":null,\"last_transitioned_to_resolved_at\":null,\"last_transitioned_to_triaged_at\":\"2021-11-03T17:18:51.675Z\",\"last_transitioned_to_unresolved_at\":\"2021-11-03T17:21:05.849Z\",\"remediation_advice\":\"1. If possible consider disabling external access.\\n2. Never use default credentials as it is trivial for an attacker to gain access by providing known or easy to guess credentials.\\n3. Always change any kind of default credentials as the first step of setting up any kind of environment.\\n4. Passwords should meet or exceed proper password strength requirements.\",\"severity\":1,\"source\":\"platform\",\"state\":\"unresolved\",\"submitted_at\":\"2021-07-21T05:12:28.874Z\",\"title\":\"Use the virtual JBOD alarm, then you can copy the primary hard drive!\",\"vrt_id\":\"server_security_misconfiguration.using_default_credentials\",\"vrt_version\":\"1.10.1\",\"vulnerability_references\":\"* [https://www.owasp.org/index.php/Testing_for_default_credentials_(OTG-AUTHN-002)](https://www.owasp.org/index.php/Testing_for_default_credentials_(OTG-AUTHN-002))\\n* [https://www.owasp.org/index.php/Configuration#Default_passwords](https://www.owasp.org/index.php/Configuration#Default_passwords)\\n* [https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md)\\n* [https://www.owasp.org/index.php/Top_10-2017_A6-Security_Misconfiguration](https://www.owasp.org/index.php/Top_10-2017_A6-Security_Misconfiguration)\\n* [http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration](http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration)\\n* [https://cwe.mitre.org/data/definitions/255](https://cwe.mitre.org/data/definitions/255)\\n* [https://cwe.mitre.org/data/definitions/521](https://cwe.mitre.org/data/definitions/521)\\n* [https://cwe.mitre.org/data/definitions/16](https://cwe.mitre.org/data/definitions/16)\"},\"relationships\":{\"activities\":{\"data\":[{\"id\":\"e61bc905-7758-487d-a7b2-e8ecfcafbadb\",\"type\":\"activity\"},{\"id\":\"42c7f6bc-70ac-4d5f-85a4-57e9fdf62678\",\"type\":\"activity\"},{\"id\":\"0da8db26-cdfa-4e4e-b7c1-1b5cee1c2119\",\"type\":\"activity\"},{\"id\":\"537db75f-19aa-4357-9355-cf09994eb2ec\",\"type\":\"activity\"}],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":4,\"total_hits\":4}}}},\"assignee\":{\"data\":{\"id\":\"adfa1452-02ab-4fad-ba08-a1ba5228bfa6\",\"type\":\"identity\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}},\"claim_ticket\":{\"data\":null,\"links\":{\"related\":{\"href\":null}}},\"comments\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"duplicate_of\":{\"data\":null,\"links\":{\"related\":{\"href\":null}}},\"duplicates\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"external_issues\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"file_attachments\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"monetary_rewards\":{\"data\":[],\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\",\"meta\":{\"count\":0,\"total_hits\":0}}}},\"program\":{\"data\":{\"id\":\"2322d2f6-60b7-4aa9-98bc-6bc56d69d61e\",\"type\":\"program\"},\"links\":{\"related\":{\"href\":\"/programs/2322d2f6-60b7-4aa9-98bc-6bc56d69d61e\"}}},\"researcher\":{\"data\":{\"id\":\"a275330b-117f-405c-a69b-4a35e55326b1\",\"type\":\"identity\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}},\"target\":{\"data\":{\"id\":\"ebf18692-a85b-4012-adbd-36ee916e269c\",\"type\":\"target\"},\"links\":{\"related\":{\"href\":\"(NOT IMPLEMENTED YET)\"}}}},\"links\":{\"self\":\"/submissions/060a0a4b-a846-41dc-9ef8-5bbecba8d810\"}}]}"

digest = raw_digest.split(';')[1]
at_time = raw_digest.split(';')[0].split('=')[1]
signature = 'sha256=' + hmac.new(secret.encode('utf8'), (raw_body + at_time).encode('utf8'), digestmod='SHA256').digest().hex()
hmac.compare_digest(digest, signature)