Module: ActiveProject::Adapters::GithubRepo::Webhooks

Included in:
ActiveProject::Adapters::GithubRepoAdapter
Defined in:
lib/active_project/adapters/github_repo/webhooks.rb

Instance Method Summary collapse

Instance Method Details

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

Parses an incoming webhook payload into a standardized WebhookEvent struct

Parameters:

  • request_body (String)

    The raw request body

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

    Hash of request headers

Returns:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/active_project/adapters/github_repo/webhooks.rb', line 52

def parse_webhook(request_body, headers = {})
  data = JSON.parse(request_body)
  event_type = headers["X-GitHub-Event"]

  case event_type
  when "issues"
    parse_issue_event(data)
  when "issue_comment"
    parse_comment_event(data)
  when "pull_request"
    parse_pull_request_event(data)
  else
    nil # Unsupported event type
  end
rescue JSON::ParserError
  nil # Return nil for invalid JSON
end

#secure_compare(a, b) ⇒ Boolean

Constant-time comparison to prevent timing attacks

Parameters:

  • a (String)

    First string to compare

  • b (String)

    Second string to compare

Returns:

  • (Boolean)

    True if strings are equal



39
40
41
42
43
44
45
46
# File 'lib/active_project/adapters/github_repo/webhooks.rb', line 39

def secure_compare(a, b)
  return false if a.bytesize != b.bytesize
  l = a.unpack("C*")

  res = 0
  b.each_byte { |byte| res |= byte ^ l.shift }
  res == 0
end

#verify_webhook_signature(request_body, signature_header) ⇒ Boolean

Validates incoming webhook signature using X-Hub-Signature-256 header

Parameters:

  • request_body (String)

    The raw request body

  • signature_header (String)

    The value of the X-Hub-Signature-256 header

Returns:

  • (Boolean)

    True if signature is valid or verification is not needed



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/active_project/adapters/github_repo/webhooks.rb', line 11

def verify_webhook_signature(request_body, signature_header)
  webhook_secret = @config.options[:webhook_secret]

  # No webhook secret configured = no verification needed
  return true if webhook_secret.nil? || webhook_secret.empty?

  # Signature header is required when a secret is configured
  return false unless signature_header

  # GitHub uses 'sha256=' prefix for their signatures
  algorithm, signature = signature_header.split("=", 2)
  return false unless algorithm == "sha256" && signature

  # Calculate expected signature
  expected_signature = OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest.new("sha256"),
    webhook_secret,
    request_body
  )

  # Perform a secure comparison
  secure_compare(signature, expected_signature)
end