hooks-ruby 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +295 -0
- data/bin/bundle +109 -0
- data/bin/erb +27 -0
- data/bin/hooks +27 -0
- data/bin/htmldiff +27 -0
- data/bin/irb +27 -0
- data/bin/ldiff +27 -0
- data/bin/puma +27 -0
- data/bin/pumactl +27 -0
- data/bin/racc +27 -0
- data/bin/rdoc +27 -0
- data/bin/ri +27 -0
- data/bin/rspec +27 -0
- data/bin/rubocop +27 -0
- data/bin/ruby-parse +27 -0
- data/bin/ruby-rewrite +27 -0
- data/bin/rubygems-await +27 -0
- data/bin/sigstore-cli +27 -0
- data/bin/thor +27 -0
- data/config.ru +6 -0
- data/hooks.gemspec +36 -0
- data/lib/hooks/app/api.rb +112 -0
- data/lib/hooks/app/auth/auth.rb +48 -0
- data/lib/hooks/app/endpoints/catchall.rb +84 -0
- data/lib/hooks/app/endpoints/health.rb +20 -0
- data/lib/hooks/app/endpoints/version.rb +18 -0
- data/lib/hooks/app/helpers.rb +95 -0
- data/lib/hooks/core/builder.rb +97 -0
- data/lib/hooks/core/config_loader.rb +152 -0
- data/lib/hooks/core/config_validator.rb +131 -0
- data/lib/hooks/core/log.rb +9 -0
- data/lib/hooks/core/logger_factory.rb +99 -0
- data/lib/hooks/core/plugin_loader.rb +250 -0
- data/lib/hooks/plugins/auth/base.rb +62 -0
- data/lib/hooks/plugins/auth/hmac.rb +313 -0
- data/lib/hooks/plugins/auth/shared_secret.rb +115 -0
- data/lib/hooks/plugins/handlers/base.rb +35 -0
- data/lib/hooks/plugins/handlers/default.rb +21 -0
- data/lib/hooks/plugins/lifecycle.rb +33 -0
- data/lib/hooks/security.rb +17 -0
- data/lib/hooks/utils/normalize.rb +83 -0
- data/lib/hooks/version.rb +5 -0
- data/lib/hooks.rb +29 -0
- metadata +189 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module Hooks
|
6
|
+
module Plugins
|
7
|
+
module Auth
|
8
|
+
# Generic shared secret validator for webhooks
|
9
|
+
#
|
10
|
+
# This validator provides simple shared secret authentication for webhook requests.
|
11
|
+
# It compares a secret value sent in a configurable HTTP header against the expected
|
12
|
+
# secret value. This is a common (though less secure than HMAC) authentication pattern
|
13
|
+
# used by various webhook providers.
|
14
|
+
#
|
15
|
+
# @example Basic configuration
|
16
|
+
# auth:
|
17
|
+
# type: shared_secret
|
18
|
+
# secret_env_key: WEBHOOK_SECRET
|
19
|
+
# header: Authorization
|
20
|
+
#
|
21
|
+
# @example Custom header configuration
|
22
|
+
# auth:
|
23
|
+
# type: shared_secret
|
24
|
+
# secret_env_key: SOME_OTHER_WEBHOOK_SECRET
|
25
|
+
# header: X-API-Key
|
26
|
+
#
|
27
|
+
# @note This validator performs direct string comparison of the shared secret.
|
28
|
+
# While simpler than HMAC, it provides less security since the secret is
|
29
|
+
# transmitted directly in the request header.
|
30
|
+
class SharedSecret < Base
|
31
|
+
# Default configuration values for shared secret validation
|
32
|
+
#
|
33
|
+
# @return [Hash<Symbol, String>] Default configuration settings
|
34
|
+
DEFAULT_CONFIG = {
|
35
|
+
header: "Authorization"
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
# Validate shared secret from webhook requests
|
39
|
+
#
|
40
|
+
# Performs secure comparison of the shared secret value from the configured
|
41
|
+
# header against the expected secret. Uses secure comparison to prevent
|
42
|
+
# timing attacks.
|
43
|
+
#
|
44
|
+
# @param payload [String] Raw request body (unused but required by interface)
|
45
|
+
# @param headers [Hash<String, String>] HTTP headers from the request
|
46
|
+
# @param config [Hash] Endpoint configuration containing validator settings
|
47
|
+
# @option config [Hash] :auth Validator-specific configuration
|
48
|
+
# @option config [String] :header ('Authorization') Header containing the secret
|
49
|
+
# @return [Boolean] true if secret is valid, false otherwise
|
50
|
+
# @raise [StandardError] Rescued internally, returns false on any error
|
51
|
+
# @note This method is designed to be safe and will never raise exceptions
|
52
|
+
# @note Uses Rack::Utils.secure_compare to prevent timing attacks
|
53
|
+
# @example Basic validation
|
54
|
+
# SharedSecret.valid?(
|
55
|
+
# payload: request_body,
|
56
|
+
# headers: request.headers,
|
57
|
+
# config: { auth: { header: 'Authorization' } }
|
58
|
+
# )
|
59
|
+
def self.valid?(payload:, headers:, config:)
|
60
|
+
secret = fetch_secret(config)
|
61
|
+
|
62
|
+
validator_config = build_config(config)
|
63
|
+
|
64
|
+
# Security: Check raw headers BEFORE normalization to detect tampering
|
65
|
+
return false unless headers.respond_to?(:each)
|
66
|
+
|
67
|
+
secret_header = validator_config[:header]
|
68
|
+
|
69
|
+
# Find the secret header with case-insensitive matching but preserve original value
|
70
|
+
raw_secret = nil
|
71
|
+
headers.each do |key, value|
|
72
|
+
if key.to_s.downcase == secret_header.downcase
|
73
|
+
raw_secret = value.to_s
|
74
|
+
break
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
return false if raw_secret.nil? || raw_secret.empty?
|
79
|
+
|
80
|
+
stripped_secret = raw_secret.strip
|
81
|
+
|
82
|
+
# Security: Reject secrets with leading/trailing whitespace
|
83
|
+
return false if raw_secret != stripped_secret
|
84
|
+
|
85
|
+
# Security: Reject secrets containing null bytes or other control characters
|
86
|
+
return false if raw_secret.match?(/[\u0000-\u001f\u007f-\u009f]/)
|
87
|
+
|
88
|
+
# Use secure comparison to prevent timing attacks
|
89
|
+
Rack::Utils.secure_compare(secret, stripped_secret)
|
90
|
+
rescue StandardError => _e
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# Build final configuration by merging defaults with provided config
|
97
|
+
#
|
98
|
+
# Combines default configuration values with user-provided settings,
|
99
|
+
# ensuring all required configuration keys are present with sensible defaults.
|
100
|
+
#
|
101
|
+
# @param config [Hash] Raw endpoint configuration
|
102
|
+
# @return [Hash<Symbol, Object>] Merged configuration with defaults applied
|
103
|
+
# @note Missing configuration values are filled with DEFAULT_CONFIG values
|
104
|
+
# @api private
|
105
|
+
def self.build_config(config)
|
106
|
+
validator_config = config.dig(:auth) || {}
|
107
|
+
|
108
|
+
DEFAULT_CONFIG.merge({
|
109
|
+
header: validator_config[:header] || DEFAULT_CONFIG[:header]
|
110
|
+
})
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hooks
|
4
|
+
module Plugins
|
5
|
+
module Handlers
|
6
|
+
# Base class for all webhook handlers
|
7
|
+
#
|
8
|
+
# All custom handlers must inherit from this class and implement the #call method
|
9
|
+
class Base
|
10
|
+
# Process a webhook request
|
11
|
+
#
|
12
|
+
# @param payload [Hash, String] Parsed request body (JSON Hash) or raw string
|
13
|
+
# @param headers [Hash<String, String>] HTTP headers
|
14
|
+
# @param config [Hash] Merged endpoint configuration including opts section
|
15
|
+
# @return [Hash, String, nil] Response body (will be auto-converted to JSON)
|
16
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
17
|
+
def call(payload:, headers:, config:)
|
18
|
+
raise NotImplementedError, "Handler must implement #call method"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Short logger accessor for all subclasses
|
22
|
+
# @return [Hooks::Log] Logger instance
|
23
|
+
#
|
24
|
+
# Provides a convenient way for handlers to log messages without needing
|
25
|
+
# to reference the full Hooks::Log namespace.
|
26
|
+
#
|
27
|
+
# @example Logging an error in an inherited class
|
28
|
+
# log.error("oh no an error occured")
|
29
|
+
def log
|
30
|
+
Hooks::Log.instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Default handler when no custom handler is found
|
4
|
+
# This handler simply acknowledges receipt of the webhook and shows a few of the built-in features
|
5
|
+
class DefaultHandler < Hooks::Plugins::Handlers::Base
|
6
|
+
def call(payload:, headers:, config:)
|
7
|
+
|
8
|
+
log.info("🔔 Default handler invoked for webhook 🔔")
|
9
|
+
|
10
|
+
# do some basic processing
|
11
|
+
if payload
|
12
|
+
log.debug("received payload: #{payload.inspect}")
|
13
|
+
end
|
14
|
+
|
15
|
+
{
|
16
|
+
message: "webhook processed successfully",
|
17
|
+
handler: "DefaultHandler",
|
18
|
+
timestamp: Time.now.iso8601
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hooks
|
4
|
+
module Plugins
|
5
|
+
# Base class for global lifecycle plugins
|
6
|
+
#
|
7
|
+
# Plugins can hook into request/response/error lifecycle events
|
8
|
+
class Lifecycle
|
9
|
+
# Called before handler execution
|
10
|
+
#
|
11
|
+
# @param env [Hash] Rack environment
|
12
|
+
def on_request(env)
|
13
|
+
# Override in subclass for pre-processing logic
|
14
|
+
end
|
15
|
+
|
16
|
+
# Called after successful handler execution
|
17
|
+
#
|
18
|
+
# @param env [Hash] Rack environment
|
19
|
+
# @param response [Hash] Handler response
|
20
|
+
def on_response(env, response)
|
21
|
+
# Override in subclass for post-processing logic
|
22
|
+
end
|
23
|
+
|
24
|
+
# Called when any error occurs during request processing
|
25
|
+
#
|
26
|
+
# @param exception [Exception] The raised exception
|
27
|
+
# @param env [Hash] Rack environment
|
28
|
+
def on_error(exception, env)
|
29
|
+
# Override in subclass for error handling logic
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hooks
|
4
|
+
module Security
|
5
|
+
# List of dangerous class names that should not be loaded as handlers
|
6
|
+
# for security reasons. These classes provide system access that could
|
7
|
+
# be exploited if loaded dynamically.
|
8
|
+
#
|
9
|
+
# @return [Array<String>] Array of dangerous class names
|
10
|
+
DANGEROUS_CLASSES = %w[
|
11
|
+
File Dir Kernel Object Class Module Proc Method
|
12
|
+
IO Socket TCPSocket UDPSocket BasicSocket
|
13
|
+
Process Thread Fiber Mutex ConditionVariable
|
14
|
+
Marshal YAML JSON Pathname
|
15
|
+
].freeze
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hooks
|
4
|
+
module Utils
|
5
|
+
# Utility class for normalizing HTTP headers
|
6
|
+
#
|
7
|
+
# Provides a robust method to consistently format HTTP headers
|
8
|
+
# across the application, handling various edge cases and formats.
|
9
|
+
class Normalize
|
10
|
+
# Normalize a hash of HTTP headers
|
11
|
+
#
|
12
|
+
# @param headers [Hash, #each] Headers hash or hash-like object
|
13
|
+
# @return [Hash] Normalized headers hash with downcased keys and trimmed values
|
14
|
+
#
|
15
|
+
# @example Hash of headers normalization
|
16
|
+
# headers = { "Content-Type" => " application/json ", "X-GitHub-Event" => "push" }
|
17
|
+
# normalized = Normalize.headers(headers)
|
18
|
+
# # => { "content-type" => "application/json", "x-github-event" => "push" }
|
19
|
+
#
|
20
|
+
# @example Handle various input types
|
21
|
+
# Normalize.headers(nil) # => nil
|
22
|
+
# Normalize.headers({}) # => {}
|
23
|
+
# Normalize.headers({ "KEY" => ["a", "b"] }) # => { "key" => "a" }
|
24
|
+
# Normalize.headers({ "Key" => 123 }) # => { "key" => "123" }
|
25
|
+
def self.headers(headers)
|
26
|
+
# Handle nil input
|
27
|
+
return nil if headers.nil?
|
28
|
+
|
29
|
+
# Fast path for non-enumerable inputs (numbers, etc.)
|
30
|
+
return {} unless headers.respond_to?(:each)
|
31
|
+
|
32
|
+
normalized = {}
|
33
|
+
|
34
|
+
headers.each do |key, value|
|
35
|
+
# Skip nil keys or values entirely
|
36
|
+
next if key.nil? || value.nil?
|
37
|
+
|
38
|
+
# Convert key to string, downcase, and strip in one operation
|
39
|
+
normalized_key = key.to_s.downcase.strip
|
40
|
+
next if normalized_key.empty?
|
41
|
+
|
42
|
+
# Handle different value types efficiently
|
43
|
+
normalized_value = case value
|
44
|
+
when String
|
45
|
+
value.strip
|
46
|
+
when Array
|
47
|
+
# Take first non-empty element for multi-value headers
|
48
|
+
first_valid = value.find { |v| v && !v.to_s.strip.empty? }
|
49
|
+
first_valid ? first_valid.to_s.strip : nil
|
50
|
+
else
|
51
|
+
value.to_s.strip
|
52
|
+
end
|
53
|
+
|
54
|
+
# Only add if we have a non-empty value
|
55
|
+
normalized[normalized_key] = normalized_value if normalized_value && !normalized_value.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
normalized
|
59
|
+
end
|
60
|
+
|
61
|
+
# Normalize a single HTTP header name
|
62
|
+
#
|
63
|
+
# @param header [String] Header name to normalize
|
64
|
+
# @return [String, nil] Normalized header name (downcased and trimmed), or nil if input is nil
|
65
|
+
#
|
66
|
+
# @example Single header normalization
|
67
|
+
# Normalize.header(" Content-Type ") # => "content-type"
|
68
|
+
# Normalize.header("X-GitHub-Event") # => "x-github-event"
|
69
|
+
# Normalize.header("") # => ""
|
70
|
+
# Normalize.header(nil) # => nil
|
71
|
+
#
|
72
|
+
# @raise [ArgumentError] If input is not a String or nil
|
73
|
+
def self.header(header)
|
74
|
+
return nil if header.nil?
|
75
|
+
if header.is_a?(String)
|
76
|
+
header.downcase.strip
|
77
|
+
else
|
78
|
+
raise ArgumentError, "Expected a String for header normalization"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/hooks.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "hooks/version"
|
4
|
+
require_relative "hooks/core/builder"
|
5
|
+
|
6
|
+
# Load all plugins (auth plugins, handler plugins, lifecycle hooks, etc.)
|
7
|
+
Dir[File.join(__dir__, "hooks/plugins/**/*.rb")].sort.each do |file|
|
8
|
+
require file
|
9
|
+
end
|
10
|
+
|
11
|
+
# Load all utils
|
12
|
+
Dir[File.join(__dir__, "hooks/utils/**/*.rb")].sort.each do |file|
|
13
|
+
require file
|
14
|
+
end
|
15
|
+
|
16
|
+
# Main module for the Hooks webhook server framework
|
17
|
+
module Hooks
|
18
|
+
# Build a Rack-compatible webhook server application
|
19
|
+
#
|
20
|
+
# @param config [String, Hash] Path to config file or config hash
|
21
|
+
# @param log [Logger] Custom logger instance (optional)
|
22
|
+
# @return [Object] Rack-compatible application
|
23
|
+
def self.build(config: nil, log: nil)
|
24
|
+
Core::Builder.new(
|
25
|
+
config:,
|
26
|
+
log:,
|
27
|
+
).build
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hooks-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- github
|
8
|
+
- GrantBirki
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redacting-logger
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: retryable
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 3.0.5
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.0'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.0.5
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: dry-schema
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.14'
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 1.14.1
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '1.14'
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 1.14.1
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: grape
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '2.3'
|
74
|
+
type: :runtime
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '2.3'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: grape-swagger
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '2.1'
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 2.1.2
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2.1'
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 2.1.2
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: puma
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - "~>"
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '6.6'
|
108
|
+
type: :runtime
|
109
|
+
prerelease: false
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - "~>"
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '6.6'
|
115
|
+
description: 'A Pluggable Webhook Server Framework written in Ruby
|
116
|
+
|
117
|
+
'
|
118
|
+
executables:
|
119
|
+
- hooks
|
120
|
+
extensions: []
|
121
|
+
extra_rdoc_files: []
|
122
|
+
files:
|
123
|
+
- LICENSE
|
124
|
+
- README.md
|
125
|
+
- bin/bundle
|
126
|
+
- bin/erb
|
127
|
+
- bin/hooks
|
128
|
+
- bin/htmldiff
|
129
|
+
- bin/irb
|
130
|
+
- bin/ldiff
|
131
|
+
- bin/puma
|
132
|
+
- bin/pumactl
|
133
|
+
- bin/racc
|
134
|
+
- bin/rdoc
|
135
|
+
- bin/ri
|
136
|
+
- bin/rspec
|
137
|
+
- bin/rubocop
|
138
|
+
- bin/ruby-parse
|
139
|
+
- bin/ruby-rewrite
|
140
|
+
- bin/rubygems-await
|
141
|
+
- bin/sigstore-cli
|
142
|
+
- bin/thor
|
143
|
+
- config.ru
|
144
|
+
- hooks.gemspec
|
145
|
+
- lib/hooks.rb
|
146
|
+
- lib/hooks/app/api.rb
|
147
|
+
- lib/hooks/app/auth/auth.rb
|
148
|
+
- lib/hooks/app/endpoints/catchall.rb
|
149
|
+
- lib/hooks/app/endpoints/health.rb
|
150
|
+
- lib/hooks/app/endpoints/version.rb
|
151
|
+
- lib/hooks/app/helpers.rb
|
152
|
+
- lib/hooks/core/builder.rb
|
153
|
+
- lib/hooks/core/config_loader.rb
|
154
|
+
- lib/hooks/core/config_validator.rb
|
155
|
+
- lib/hooks/core/log.rb
|
156
|
+
- lib/hooks/core/logger_factory.rb
|
157
|
+
- lib/hooks/core/plugin_loader.rb
|
158
|
+
- lib/hooks/plugins/auth/base.rb
|
159
|
+
- lib/hooks/plugins/auth/hmac.rb
|
160
|
+
- lib/hooks/plugins/auth/shared_secret.rb
|
161
|
+
- lib/hooks/plugins/handlers/base.rb
|
162
|
+
- lib/hooks/plugins/handlers/default.rb
|
163
|
+
- lib/hooks/plugins/lifecycle.rb
|
164
|
+
- lib/hooks/security.rb
|
165
|
+
- lib/hooks/utils/normalize.rb
|
166
|
+
- lib/hooks/version.rb
|
167
|
+
homepage: https://github.com/github/hooks
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata:
|
171
|
+
bug_tracker_uri: https://github.com/github/hooks/issues
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: 3.2.2
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubygems_version: 3.6.7
|
187
|
+
specification_version: 4
|
188
|
+
summary: A Pluggable Webhook Server Framework written in Ruby
|
189
|
+
test_files: []
|