Collect HackerOne logs

Supported in:

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

  1. Go to SIEM Settings > Feeds.
  2. Click Add New Feed.
  3. On the next page, click Configure a single feed.
  4. In the Feed name field, enter a name for the feed (for example, HackerOne Vulnerability Reports).
  5. Select Webhook as the Source type.
  6. Select HackerOne as the Log type.
  7. Click Next.
  8. 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.
  9. Click Next.
  10. 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:

  1. On the feed details page, click Generate Secret Key.
  2. A dialog displays the secret key.
  3. Copy and save the secret key securely.

Get the feed endpoint URL

  1. Go to the Details tab of the feed.
  2. In the Endpoint Information section, copy the Feed endpoint URL.
  3. The URL format is:

    https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate
    

    or

    https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate
    
  4. Save this URL for the next steps.

  5. 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

  1. Go to the Google Cloud Console Credentials page.
  2. Select your project (the project associated with your Chronicle instance).
  3. Click Create credentials > API key.
  4. An API key is created and displayed in a dialog.
  5. Click Edit API key to restrict the key.

Restrict the API key

  1. In the API key settings page:
    • Name: Enter a descriptive name (for example, Chronicle HackerOne Webhook API Key).
  2. Under API restrictions:
    1. Select Restrict key.
    2. In the Select APIs list, search for and select Google SecOps API (or Chronicle API).
  3. Click Save.
  4. Copy the API key value from the API key field at the top of the page.
  5. 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

  1. Sign in to HackerOne and navigate to your program.
  2. Go to Engagements, click the kebab menu for the program you want to configure, then click Settings.
  3. Go to Automation > Webhooks.
  4. Click New webhook.
  5. 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
  6. Click Add webhook.

Test the webhook

  1. In the webhook configuration page, select Test request to send an example request to the configured Payload URL.
  2. Verify the response is HTTP 200.
  3. Check Chronicle feed for the test event within 1 to 2 minutes.

Verify webhook is working

Check HackerOne webhook status

  1. Sign in to the HackerOne console.
  2. Go to Engagements, click the kebab menu for your program, then click Settings.
  3. Go to Automation > Webhooks.
  4. Click your webhook to view details.
  5. Under the Recent deliveries section, verify recent deliveries show successful status (HTTP 200).
  6. Click any delivery to view the POST payload request.

Check Chronicle feed status

  1. Go to SIEM Settings > Feeds in Chronicle.
  2. Locate your HackerOne webhook feed.
  3. Check the Status column (should be Active).
  4. Check Events received count (should be incrementing).
  5. Check Last succeeded on timestamp (should be recent).

Verify logs in Chronicle

  1. Go to Search > UDM Search.
  2. Use the following query:

    metadata.vendor_name = "HACKERONE" AND metadata.product_name = "HACKERONE"
    
  3. Adjust time range to last 1 hour.

  4. 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.

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
  • 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.