Collect HackerOne logs
This document explains how to configure HackerOne to push logs to Google Security Operations using webhooks.
HackerOne is a vulnerability coordination and bug bounty platform that connects organizations with security researchers to identify and remediate security vulnerabilities. The platform provides bug bounty programs, vulnerability disclosure programs, pentesting, and continuous security testing across the software development lifecycle.
Before you begin
Ensure that you have the following prerequisites:
- A Google SecOps instance
- HackerOne program with Professional or Enterprise tier (webhooks are only available for these tiers)
- Administrative access to your HackerOne program settings
Create webhook feed in Google SecOps
Create the feed
- Go to SIEM Settings > Feeds.
- Click Add New Feed.
- On the next page, click Configure a single feed.
- In the Feed name field, enter a name for the feed (for example,
HackerOne Vulnerability Reports). - Select Webhook as the Source type.
- Select HackerOne as the Log type.
- Click Next.
- Specify values for the following input parameters:
- Split delimiter (optional): Leave empty. Each webhook request contains a single JSON event.
- Asset namespace: The asset namespace.
- Ingestion labels: The label to be applied to the events from this feed.
- Click Next.
- Review your new feed configuration in the Finalize screen, and then click Submit.
Generate and save secret key
After creating the feed, you must generate a secret key for authentication:
- On the feed details page, click Generate Secret Key.
- A dialog displays the secret key.
- Copy and save the secret key securely.
Get the feed endpoint URL
- Go to the Details tab of the feed.
- In the Endpoint Information section, copy the Feed endpoint URL.
The URL format is:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateor
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateSave this URL for the next steps.
Click Done.
Create Google Cloud API key
Chronicle requires an API key for authentication. Create a restricted API key in the Google Cloud Console.
Create the API key
- Go to the Google Cloud Console Credentials page.
- Select your project (the project associated with your Chronicle instance).
- Click Create credentials > API key.
- An API key is created and displayed in a dialog.
- Click Edit API key to restrict the key.
Restrict the API key
- In the API key settings page:
- Name: Enter a descriptive name (for example,
Chronicle HackerOne Webhook API Key).
- Name: Enter a descriptive name (for example,
- Under API restrictions:
- Select Restrict key.
- In the Select APIs list, search for and select Google SecOps API (or Chronicle API).
- Click Save.
- Copy the API key value from the API key field at the top of the page.
Save the API key securely.
Configure HackerOne webhook
Construct the webhook URL
Combine the Chronicle endpoint URL and API key:
<ENDPOINT_URL>?key=<API_KEY>Example:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
Create webhook in HackerOne
- Sign in to HackerOne and navigate to your program.
- Go to Engagements, click the kebab menu for the program you want to configure, then click Settings.
- Go to Automation > Webhooks.
- Click New webhook.
- Provide the following configuration details:
- Payload URL: Paste the complete endpoint URL with API key from above.
- Secret: Paste the secret key from Chronicle feed creation.
- Select which events you'd like to trigger the webhook: Choose one of the following:
- Send me everything: All events will trigger the webhook.
- Let me specify individual events: Select specific events you want to send to Chronicle. Recommended events for security monitoring:
- report_created: When a hacker submits a new vulnerability report
- report_triaged: When a report is triaged
- report_resolved: When a report is resolved
- report_bounty_awarded: When a bounty is awarded
- report_swag_awarded: When swag is awarded
- program_hacker_joined: When a hacker joins the program
- program_hacker_left: When a hacker leaves the program
- Click Add webhook.
Test the webhook
- In the webhook configuration page, select Test request to send an example request to the configured Payload URL.
- Verify the response is HTTP 200.
- Check Chronicle feed for the test event within 1 to 2 minutes.
Verify webhook is working
Check HackerOne webhook status
- Sign in to the HackerOne console.
- Go to Engagements, click the kebab menu for your program, then click Settings.
- Go to Automation > Webhooks.
- Click your webhook to view details.
- Under the Recent deliveries section, verify recent deliveries show successful status (HTTP 200).
- Click any delivery to view the POST payload request.
Check Chronicle feed status
- Go to SIEM Settings > Feeds in Chronicle.
- Locate your HackerOne webhook feed.
- Check the Status column (should be Active).
- Check Events received count (should be incrementing).
- Check Last succeeded on timestamp (should be recent).
Verify logs in Chronicle
- Go to Search > UDM Search.
Use the following query:
metadata.vendor_name = "HACKERONE" AND metadata.product_name = "HACKERONE"Adjust time range to last 1 hour.
Verify events appear in results.
Authentication methods reference
Chronicle webhook feeds support multiple authentication methods. HackerOne webhooks use a combination of query parameters and signature validation.
Method 1: Query parameters with signature validation (Recommended for HackerOne)
HackerOne sends webhooks to the configured Payload URL. Authentication is handled through:
- API key in URL: The Chronicle API key is appended as a query parameter to the Payload URL.
Secret signature validation: HackerOne generates an X-H1-Signature header containing an HMAC SHA256 hexdigest of the request body signed with the configured secret.
URL format:
<ENDPOINT_URL>?key=<API_KEY>Request format:
POST <ENDPOINT_URL>?key=<API_KEY> HTTP/1.1 Content-Type: application/json X-H1-Signature: sha256=<HMAC_HEXDIGEST> X-H1-Event: <EVENT_TYPE> X-H1-Delivery: <DELIVERY_ID> { "data": { "activity": {...}, "report": {...} } }
Advantages:
- Dual authentication: API key for Chronicle access and signature for payload validation
- HackerOne provides built-in signature generation
- Secure payload integrity verification
Signature validation
HackerOne includes the following headers with each webhook request:
- X-H1-Signature: HMAC SHA256 hexdigest of the request body (format:
sha256=<hexdigest>) - X-H1-Event: The event type that triggered the webhook
- X-H1-Delivery: Unique identifier for the delivery
- X-H1-Signature: HMAC SHA256 hexdigest of the request body (format:
To validate the signature on your receiving endpoint:
import hmac import hashlib def validate_request(request_body, secret, signature): _, digest = signature.split('=') generated_digest = hmac.new( secret.encode(), request_body.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(digest, generated_digest)
Webhook event types
HackerOne supports the following webhook event types:
| Event Type | Description |
|---|---|
| report_created | Triggered when a hacker submits a new vulnerability report |
| report_triaged | Triggered when a report is triaged |
| report_resolved | Triggered when a report is resolved |
| report_bounty_awarded | Triggered when a bounty is awarded for a report |
| report_swag_awarded | Triggered when swag is awarded for a report |
| report_became_public | Triggered when a report becomes public |
| program_hacker_joined | Triggered when a hacker joins the program |
| program_hacker_left | Triggered when a hacker leaves the program |
UDM mapping table
| Log Field | UDM Mapping | Logic |
|---|---|---|
| attributes.cleared, attributes.rules_of_engagement_signed, attributes.identity_verified, attributes.background_checked, attributes.citizenship_verified, attributes.residency_verified, type, attributes.title, attributes.main_state, attributes.state, relationships.reporter.data.type, relationships.reporter.data.attributes.reputation, relationships.reporter.data.attributes.signal, relationships.reporter.data.attributes.impact, relationships.reporter.data.attributes.disabled, relationships.reporter.data.attributes.profile_picture.62x62, relationships.reporter.data.attributes.profile_picture.82x82, relationships.reporter.data.attributes.profile_picture.110x110, relationships.reporter.data.attributes.profile_picture.260x260, relationships.reporter.data.attributes.hackerone_triager, relationships.program.data.id, relationships.program.data.type, relationships.program.data.attributes.handle, relationships.severity.data.type, relationships.severity.data.attributes.rating, relationships.severity.data.attributes.author_type, relationships.severity.data.attributes.calculation_method, relationships.weakness.data.id, relationships.weakness.data.type, relationships.weakness.data.attributes.name, relationships.weakness.data.attributes.description, relationships.weakness.data.attributes.external_id, relationships.structured_scope.data.id, relationships.structured_scope.data.type, relationships.structured_scope.data.attributes.asset_type, relationships.structured_scope.data.attributes.eligible_for_bounty, relationships.structured_scope.data.attributes.eligible_for_submission, relationships.structured_scope.data.attributes.instruction, relationships.structured_scope.data.attributes.max_severity, relationships.structured_scope.data.attributes.confidentiality_requirement, relationships.structured_scope.data.attributes.integrity_requirement, relationships.structured_scope.data.attributes.availability_requirement, relationships.inboxes.data.id, relationships.inboxes.data.type, relationships.inboxes.data.attributes.name, relationships.inboxes.data.attributes.type | additional.fields | Merged as key-value labels |
| timestamp | metadata.event_timestamp | Parsed using date filter with format yyyy-MM-dd'T'HH:mm:ss.SSSZ |
| metadata.event_type | Set to "STATUS_UPDATE" if has_principal true, "USER_UNCATEGORIZED" if has_principal_user_user true, else "GENERIC_EVENT" | |
| id | metadata.product_log_id | Value copied directly |
| relationships.structured_scope.data.attributes.asset_identifier | principal.asset.asset_id | Prefixed with "ASSET:" |
| attributes.email_alias | principal.user.email_addresses | Merged |
| relationships.reporter.data.id | principal.user.employee_id | Value copied directly |
| relationships.reporter.data.attributes.name | principal.user.first_name | Value copied directly |
| attributes.username, relationships.reporter.data.attributes.username | principal.user.user_display_name | Value from relationships.reporter.data.attributes.username if not empty, else attributes.username |
| relationships.severity.data.attributes.user_id | principal.user.userid | Value copied directly |
| relationships.severity.data.id | security_result.rule_id | Value copied directly |
| relationships.severity.data.attributes.max_severity | security_result.severity | Converted to uppercase |
| attributes.vulnerability_information | security_result.summary | Value copied directly |
Need more help? Get answers from Community members and Google SecOps professionals.