hooks-ruby 0.3.1 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cd5b56450a97783c263526f9a1a9e3003f1ed587a0da12bfb7a21b428091d2b
4
- data.tar.gz: 67954e277623e99daafc8b3d86c9bdfa62b871319f4bff759789cd32048e752f
3
+ metadata.gz: 27cbf50d13a78bab2fb359d40a11a8ffb6998712317a2927c95a1d083cdd3be3
4
+ data.tar.gz: b3473918310de8d23aeaf448af5d09b7601f18dce286a18cfcdc0efd78c7548a
5
5
  SHA512:
6
- metadata.gz: 41cbc5a653f452e9c6da4ffd526a1b7ea37532712ae820223ccbdb93809789e949d4370fe5ba18036a0a08b8446280ff44471088703c296f769d8c83b6003cfa
7
- data.tar.gz: 99619a010a1fca90a1bda4f78850c66f705390be9677a1006cfdf72904e9ae0443bd1c0fed02dbbe014515c4df707a84b305460e111eb086fca76af3dec1ad61
6
+ metadata.gz: e8ec9dc8250c234bad0821d62f4876808725d0aa56ac8b62ba03c5789e02b250e11b0c8b8bba581e691033f130d69ef6d57f29ca2d4c29592389457e75107161
7
+ data.tar.gz: 0733db6077bfd15c6723cdad92e443a2c4d262f32ab6c30a989dcee12b07223a7d9bfff4a7fcb50950a1c412cb1c1e1a0031382f3d657d799e89da5a1f0d55dd
data/lib/hooks/app/api.rb CHANGED
@@ -38,8 +38,8 @@ module Hooks
38
38
  content_type :txt, "text/plain"
39
39
  content_type :xml, "application/xml"
40
40
  content_type :any, "*/*"
41
- format :txt
42
- default_format :txt
41
+
42
+ default_format config[:default_format] || :json
43
43
  end
44
44
 
45
45
  api_class.class_eval do
@@ -117,22 +117,21 @@ module Hooks
117
117
  log.info("successfully processed webhook event with handler: #{handler_class_name}")
118
118
  log.debug("processing duration: #{Time.now - start_time}s")
119
119
  status 200
120
- content_type "application/json"
121
- response.to_json
120
+ response
122
121
  rescue Hooks::Plugins::Handlers::Error => e
123
122
  # Handler called error! method - immediately return error response and exit the request
124
123
  log.debug("handler #{handler_class_name} called `error!` method")
125
124
 
126
- error_response = nil
127
-
128
125
  status e.status
129
126
  case e.body
130
127
  when String
128
+ # if error! was called with a string, we assume it's a simple text error
129
+ # example: error!("simple text error", 400) -> should return a plain text response
131
130
  content_type "text/plain"
132
131
  error_response = e.body
133
132
  else
134
- content_type "application/json"
135
- error_response = e.body.to_json
133
+ # Let Grape handle JSON conversion with the default format
134
+ error_response = e.body
136
135
  end
137
136
 
138
137
  return error_response
@@ -163,8 +162,7 @@ module Hooks
163
162
  error_response[:handler] = handler_class_name unless config[:production]
164
163
 
165
164
  status determine_error_code(e)
166
- content_type "application/json"
167
- error_response.to_json
165
+ error_response
168
166
  end
169
167
  end
170
168
  end
@@ -17,6 +17,13 @@ module Hooks
17
17
  class CatchallEndpoint < Grape::API
18
18
  include Hooks::App::Helpers
19
19
 
20
+ # Set up content types and default format to JSON to match main API
21
+ content_type :json, "application/json"
22
+ content_type :txt, "text/plain"
23
+ content_type :xml, "application/xml"
24
+ content_type :any, "*/*"
25
+ default_format :json
26
+
20
27
  def self.mount_path(config)
21
28
  # :nocov:
22
29
  "#{config[:root_path]}/*path"
@@ -81,8 +88,7 @@ module Hooks
81
88
  log.info("successfully processed webhook event with handler: #{handler_class_name}")
82
89
  log.debug("processing duration: #{Time.now - start_time}s")
83
90
  status 200
84
- content_type "application/json"
85
- response.to_json
91
+ response
86
92
  rescue StandardError => e
87
93
  err_msg = "Error processing webhook event with handler: #{handler_class_name} - #{e.message} " \
88
94
  "- request_id: #{request_id} - path: #{full_path} - method: #{http_method} - " \
@@ -102,8 +108,7 @@ module Hooks
102
108
  error_response[:handler] = handler_class_name unless config[:production]
103
109
 
104
110
  status determine_error_code(e)
105
- content_type "application/json"
106
- error_response.to_json
111
+ error_response
107
112
  end
108
113
  end
109
114
  end
@@ -6,14 +6,20 @@ require_relative "../../version"
6
6
  module Hooks
7
7
  module App
8
8
  class HealthEndpoint < Grape::API
9
+ # Set up content types and default format to JSON
10
+ content_type :json, "application/json"
11
+ content_type :txt, "text/plain"
12
+ content_type :xml, "application/xml"
13
+ content_type :any, "*/*"
14
+ default_format :json
15
+
9
16
  get do
10
- content_type "application/json"
11
17
  {
12
18
  status: "healthy",
13
19
  timestamp: Time.now.utc.iso8601,
14
20
  version: Hooks::VERSION,
15
21
  uptime_seconds: (Time.now - Hooks::App::API.server_start_time).to_i
16
- }.to_json
22
+ }
17
23
  end
18
24
  end
19
25
  end
@@ -6,12 +6,18 @@ require_relative "../../version"
6
6
  module Hooks
7
7
  module App
8
8
  class VersionEndpoint < Grape::API
9
+ # Set up content types and default format to JSON
10
+ content_type :json, "application/json"
11
+ content_type :txt, "text/plain"
12
+ content_type :xml, "application/xml"
13
+ content_type :any, "*/*"
14
+ default_format :json
15
+
9
16
  get do
10
- content_type "application/json"
11
17
  {
12
18
  version: Hooks::VERSION,
13
19
  timestamp: Time.now.utc.iso8601
14
- }.to_json
20
+ }
15
21
  end
16
22
  end
17
23
  end
@@ -20,7 +20,8 @@ module Hooks
20
20
  production: true,
21
21
  endpoints_dir: "./config/endpoints",
22
22
  use_catchall_route: false,
23
- normalize_headers: true
23
+ normalize_headers: true,
24
+ default_format: :json
24
25
  }.freeze
25
26
 
26
27
  SILENCE_CONFIG_LOADER_MESSAGES = ENV.fetch(
@@ -141,6 +142,7 @@ module Hooks
141
142
  "HOOKS_ENDPOINTS_DIR" => :endpoints_dir,
142
143
  "HOOKS_USE_CATCHALL_ROUTE" => :use_catchall_route,
143
144
  "HOOKS_NORMALIZE_HEADERS" => :normalize_headers,
145
+ "HOOKS_DEFAULT_FORMAT" => :default_format,
144
146
  "HOOKS_SOME_STRING_VAR" => :some_string_var # Added for test
145
147
  }
146
148
 
@@ -155,6 +157,9 @@ module Hooks
155
157
  when :use_catchall_route, :normalize_headers
156
158
  # Convert string to boolean
157
159
  env_config[config_key] = %w[true 1 yes on].include?(value.downcase)
160
+ when :default_format
161
+ # Convert string to symbol
162
+ env_config[config_key] = value.to_sym
158
163
  else
159
164
  env_config[config_key] = value
160
165
  end
@@ -27,6 +27,7 @@ module Hooks
27
27
  optional(:endpoints_dir).filled(:string)
28
28
  optional(:use_catchall_route).filled(:bool)
29
29
  optional(:normalize_headers).filled(:bool)
30
+ optional(:default_format).filled(:symbol, included_in?: %i[json txt xml any])
30
31
 
31
32
  optional(:ip_filtering).hash do
32
33
  optional(:ip_header).filled(:string)
@@ -205,7 +205,11 @@ module Hooks
205
205
  require file_path
206
206
 
207
207
  # Get the class and validate it
208
- auth_plugin_class = Object.const_get("Hooks::Plugins::Auth::#{class_name}")
208
+ auth_plugin_class = begin
209
+ Hooks::Plugins::Auth.const_get(class_name, false) # false = don't inherit from ancestors
210
+ rescue NameError
211
+ raise StandardError, "Auth plugin class not found in Hooks::Plugins::Auth namespace: #{class_name}"
212
+ end
209
213
  unless auth_plugin_class < Hooks::Plugins::Auth::Base
210
214
  raise StandardError, "Auth plugin class must inherit from Hooks::Plugins::Auth::Base: #{class_name}"
211
215
  end
@@ -239,8 +243,13 @@ module Hooks
239
243
  # Load the file
240
244
  require file_path
241
245
 
242
- # Get the class and validate it
243
- handler_class = Object.const_get(class_name)
246
+ # Get the class and validate it - use safe constant lookup
247
+ handler_class = begin
248
+ # Check if the constant exists in the global namespace for handlers
249
+ Object.const_get(class_name, false) # false = don't inherit from ancestors
250
+ rescue NameError
251
+ raise StandardError, "Handler class not found: #{class_name}"
252
+ end
244
253
  unless handler_class < Hooks::Plugins::Handlers::Base
245
254
  raise StandardError, "Handler class must inherit from Hooks::Plugins::Handlers::Base: #{class_name}"
246
255
  end
@@ -274,8 +283,12 @@ module Hooks
274
283
  # Load the file
275
284
  require file_path
276
285
 
277
- # Get the class and validate it
278
- lifecycle_class = Object.const_get(class_name)
286
+ # Get the class and validate it - use safe constant lookup
287
+ lifecycle_class = begin
288
+ Object.const_get(class_name, false) # false = don't inherit from ancestors
289
+ rescue NameError
290
+ raise StandardError, "Lifecycle plugin class not found: #{class_name}"
291
+ end
279
292
  unless lifecycle_class < Hooks::Plugins::Lifecycle
280
293
  raise StandardError, "Lifecycle plugin class must inherit from Hooks::Plugins::Lifecycle: #{class_name}"
281
294
  end
@@ -309,8 +322,12 @@ module Hooks
309
322
  # Load the file
310
323
  require file_path
311
324
 
312
- # Get the class and validate it
313
- instrument_class = Object.const_get(class_name)
325
+ # Get the class and validate it - use safe constant lookup
326
+ instrument_class = begin
327
+ Object.const_get(class_name, false) # false = don't inherit from ancestors
328
+ rescue NameError
329
+ raise StandardError, "Instrument plugin class not found: #{class_name}"
330
+ end
314
331
 
315
332
  # Determine instrument type based on inheritance
316
333
  if instrument_class < Hooks::Plugins::Instruments::StatsBase
@@ -68,23 +68,21 @@ module Hooks
68
68
  secret_header = validator_config[:header]
69
69
 
70
70
  # Find the secret header with case-insensitive matching
71
- raw_secret = find_header_value(headers, secret_header)
71
+ provided_secret = find_header_value(headers, secret_header)
72
72
 
73
- if raw_secret.nil? || raw_secret.empty?
73
+ if provided_secret.nil? || provided_secret.empty?
74
74
  log.warn("Auth::SharedSecret validation failed: Missing or empty secret header '#{secret_header}'")
75
75
  return false
76
76
  end
77
77
 
78
78
  # Validate secret format using shared validation
79
- unless valid_header_value?(raw_secret, "Secret")
79
+ unless valid_header_value?(provided_secret, "Secret")
80
80
  log.warn("Auth::SharedSecret validation failed: Invalid secret format")
81
81
  return false
82
82
  end
83
83
 
84
- stripped_secret = raw_secret.strip
85
-
86
84
  # Use secure comparison to prevent timing attacks
87
- result = Rack::Utils.secure_compare(secret, stripped_secret)
85
+ result = Rack::Utils.secure_compare(secret, provided_secret)
88
86
  if result
89
87
  log.debug("Auth::SharedSecret validation successful for header '#{secret_header}'")
90
88
  else
data/lib/hooks/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  module Hooks
5
5
  # Current version of the Hooks webhook framework
6
6
  # @return [String] The version string following semantic versioning
7
- VERSION = "0.3.1".freeze
7
+ VERSION = "0.4.0".freeze
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hooks-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - github