Skip to content

Commit a1123fa

Browse files
authored
Merge pull request #60 from github/general-improvements
General improvements
2 parents 063a60d + 22eb0c6 commit a1123fa

File tree

5 files changed

+103
-15
lines changed

5 files changed

+103
-15
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
hooks-ruby (0.3.1)
4+
hooks-ruby (0.3.2)
55
dry-schema (~> 1.14, >= 1.14.1)
66
grape (~> 2.3)
77
puma (~> 6.6)

lib/hooks/core/plugin_loader.rb

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,11 @@ def load_custom_auth_plugin(file_path, auth_plugin_dir)
205205
require file_path
206206

207207
# 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
209213
unless auth_plugin_class < Hooks::Plugins::Auth::Base
210214
raise StandardError, "Auth plugin class must inherit from Hooks::Plugins::Auth::Base: #{class_name}"
211215
end
@@ -239,8 +243,13 @@ def load_custom_handler_plugin(file_path, handler_plugin_dir)
239243
# Load the file
240244
require file_path
241245

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
244253
unless handler_class < Hooks::Plugins::Handlers::Base
245254
raise StandardError, "Handler class must inherit from Hooks::Plugins::Handlers::Base: #{class_name}"
246255
end
@@ -274,8 +283,12 @@ def load_custom_lifecycle_plugin(file_path, lifecycle_plugin_dir)
274283
# Load the file
275284
require file_path
276285

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
279292
unless lifecycle_class < Hooks::Plugins::Lifecycle
280293
raise StandardError, "Lifecycle plugin class must inherit from Hooks::Plugins::Lifecycle: #{class_name}"
281294
end
@@ -309,8 +322,12 @@ def load_custom_instrument_plugin(file_path, instruments_plugin_dir)
309322
# Load the file
310323
require file_path
311324

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
314331

315332
# Determine instrument type based on inheritance
316333
if instrument_class < Hooks::Plugins::Instruments::StatsBase

lib/hooks/plugins/auth/shared_secret.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,21 @@ def self.valid?(payload:, headers:, config:)
6868
secret_header = validator_config[:header]
6969

7070
# 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)
7272

73-
if raw_secret.nil? || raw_secret.empty?
73+
if provided_secret.nil? || provided_secret.empty?
7474
log.warn("Auth::SharedSecret validation failed: Missing or empty secret header '#{secret_header}'")
7575
return false
7676
end
7777

7878
# Validate secret format using shared validation
79-
unless valid_header_value?(raw_secret, "Secret")
79+
unless valid_header_value?(provided_secret, "Secret")
8080
log.warn("Auth::SharedSecret validation failed: Invalid secret format")
8181
return false
8282
end
8383

84-
stripped_secret = raw_secret.strip
85-
8684
# 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)
8886
if result
8987
log.debug("Auth::SharedSecret validation successful for header '#{secret_header}'")
9088
else

lib/hooks/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
module Hooks
55
# Current version of the Hooks webhook framework
66
# @return [String] The version string following semantic versioning
7-
VERSION = "0.3.1".freeze
7+
VERSION = "0.3.2".freeze
88
end

spec/unit/lib/hooks/core/plugin_loader_spec.rb

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,28 @@ def self.valid?(payload:, headers:, config:)
240240
end
241241
end
242242

243+
it "raises error when auth plugin class is not found after loading" do
244+
temp_auth_dir = File.join(temp_dir, "auth_missing_class")
245+
FileUtils.mkdir_p(temp_auth_dir)
246+
247+
# Create plugin file that doesn't define the expected class
248+
missing_file = File.join(temp_auth_dir, "missing_auth.rb")
249+
File.write(missing_file, <<~RUBY)
250+
# This file doesn't define MissingAuth class
251+
module Hooks
252+
module Plugins
253+
module Auth
254+
# Nothing here
255+
end
256+
end
257+
end
258+
RUBY
259+
260+
expect {
261+
described_class.send(:load_custom_auth_plugin, missing_file, temp_auth_dir)
262+
}.to raise_error(StandardError, /Auth plugin class not found in Hooks::Plugins::Auth namespace: MissingAuth/)
263+
end
264+
243265
describe "handler plugin loading failures" do
244266
it "raises error when handler plugin file fails to load" do
245267
temp_handler_dir = File.join(temp_dir, "handler_failures")
@@ -298,6 +320,23 @@ def call(payload:, headers:, env:, config:)
298320
described_class.send(:load_custom_handler_plugin, wrong_file, temp_handler_dir)
299321
}.to raise_error(StandardError, /Handler class must inherit from Hooks::Plugins::Handlers::Base/)
300322
end
323+
324+
it "raises error when handler plugin class is not found after loading" do
325+
temp_handler_dir = File.join(temp_dir, "handler_missing_class")
326+
FileUtils.mkdir_p(temp_handler_dir)
327+
328+
# Create plugin file that doesn't define the expected class
329+
missing_file = File.join(temp_handler_dir, "missing_handler.rb")
330+
File.write(missing_file, <<~RUBY)
331+
# This file doesn't define MissingHandler class
332+
class SomeOtherClass
333+
end
334+
RUBY
335+
336+
expect {
337+
described_class.send(:load_custom_handler_plugin, missing_file, temp_handler_dir)
338+
}.to raise_error(StandardError, /Handler class not found: MissingHandler/)
339+
end
301340
end
302341

303342
describe "lifecycle plugin loading failures" do
@@ -358,6 +397,23 @@ def on_request(env)
358397
described_class.send(:load_custom_lifecycle_plugin, wrong_file, temp_lifecycle_dir)
359398
}.to raise_error(StandardError, /Lifecycle plugin class must inherit from Hooks::Plugins::Lifecycle/)
360399
end
400+
401+
it "raises error when lifecycle plugin class is not found after loading" do
402+
temp_lifecycle_dir = File.join(temp_dir, "lifecycle_missing_class")
403+
FileUtils.mkdir_p(temp_lifecycle_dir)
404+
405+
# Create plugin file that doesn't define the expected class
406+
missing_file = File.join(temp_lifecycle_dir, "missing_lifecycle.rb")
407+
File.write(missing_file, <<~RUBY)
408+
# This file doesn't define MissingLifecycle class
409+
class SomeOtherClass
410+
end
411+
RUBY
412+
413+
expect {
414+
described_class.send(:load_custom_lifecycle_plugin, missing_file, temp_lifecycle_dir)
415+
}.to raise_error(StandardError, /Lifecycle plugin class not found: MissingLifecycle/)
416+
end
361417
end
362418

363419
describe "instrument plugin loading failures" do
@@ -418,6 +474,23 @@ def record(metric_name, value, tags = {})
418474
described_class.send(:load_custom_instrument_plugin, wrong_file, temp_instrument_dir)
419475
}.to raise_error(StandardError, /Instrument plugin class must inherit from StatsBase or FailbotBase/)
420476
end
477+
478+
it "raises error when instrument plugin class is not found after loading" do
479+
temp_instrument_dir = File.join(temp_dir, "instrument_missing_class")
480+
FileUtils.mkdir_p(temp_instrument_dir)
481+
482+
# Create plugin file that doesn't define the expected class
483+
missing_file = File.join(temp_instrument_dir, "missing_instrument.rb")
484+
File.write(missing_file, <<~RUBY)
485+
# This file doesn't define MissingInstrument class
486+
class SomeOtherClass
487+
end
488+
RUBY
489+
490+
expect {
491+
described_class.send(:load_custom_instrument_plugin, missing_file, temp_instrument_dir)
492+
}.to raise_error(StandardError, /Instrument plugin class not found: MissingInstrument/)
493+
end
421494
end
422495
end
423496
end

0 commit comments

Comments
 (0)