Module: ActiveProject::Adapters::GithubProject::Webhooks

Included in:
ActiveProject::Adapters::GithubProjectAdapter
Defined in:
lib/active_project/adapters/github_project/webhooks.rb

Overview

GitHub Project webhook processing for GitHub Projects V2. Handles project item and project events from GitHub webhooks.

Instance Method Summary collapse

Instance Method Details

#parse_webhook(request_body, headers = {}) ⇒ ActiveProject::WebhookEvent?

Parses GitHub webhook payload into standardized WebhookEvent. Supports projects_v2_item and projects_v2 events.

Parameters:

  • request_body (String)

    Raw JSON payload

  • headers (Hash) (defaults to: {})

    Request headers (for X-GitHub-Event)

Returns:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/active_project/adapters/github_project/webhooks.rb', line 35

def parse_webhook(request_body, headers = {})
  payload = JSON.parse(request_body)
  github_event = headers["X-GitHub-Event"] || headers["x-github-event"]

  case github_event
  when "projects_v2_item"
    parse_project_item_event(payload)
  when "projects_v2"
    parse_project_event(payload)
  else
    # Return nil for unsupported events (not an error)
    nil
  end
rescue JSON::ParserError
  # Invalid JSON - return nil
  nil
end

#verify_webhook_signature(request_body, signature_header, webhook_secret: nil) ⇒ Boolean

Verifies GitHub webhook signature using SHA256 HMAC.

Parameters:

  • request_body (String)

    Raw request body

  • signature_header (String)

    Value of X-Hub-Signature-256 header

  • webhook_secret (String) (defaults to: nil)

    GitHub webhook secret

Returns:

  • (Boolean)

    true if signature is valid



17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/active_project/adapters/github_project/webhooks.rb', line 17

def verify_webhook_signature(request_body, signature_header, webhook_secret: nil)
  return false unless webhook_secret && signature_header

  # GitHub sends signature as "sha256=<hash>"
  return false unless signature_header.start_with?("sha256=")

  expected_signature = signature_header[7..-1] # Remove "sha256=" prefix
  computed_signature = OpenSSL::HMAC.hexdigest("SHA256", webhook_secret, request_body)

  # Use secure comparison to prevent timing attacks
  secure_compare(expected_signature, computed_signature)
end