@@ -172,4 +172,252 @@ def call(payload:, headers:, config:)
172172 )
173173 end
174174 end
175+
176+ describe "failure scenarios" do
177+ describe "auth plugin loading failures" do
178+ it "raises error when auth plugin file fails to load" do
179+ temp_auth_dir = File . join ( temp_dir , "auth_failures" )
180+ FileUtils . mkdir_p ( temp_auth_dir )
181+
182+ # Create a malformed Ruby file
183+ malformed_file = File . join ( temp_auth_dir , "broken_auth.rb" )
184+ File . write ( malformed_file , "class BrokenAuth\n # Missing end statement" )
185+
186+ expect {
187+ described_class . load_all_plugins ( { auth_plugin_dir : temp_auth_dir } )
188+ } . to raise_error ( StandardError , /Failed to load auth plugin from.*broken_auth\. rb/ )
189+ end
190+
191+ it "raises error for auth plugin path traversal attempt" do
192+ temp_auth_dir = File . join ( temp_dir , "auth_secure" )
193+ FileUtils . mkdir_p ( temp_auth_dir )
194+
195+ # Create a plugin file outside the auth directory
196+ outside_file = File . join ( temp_dir , "outside_auth.rb" )
197+ File . write ( outside_file , "# Outside file" )
198+
199+ expect {
200+ described_class . send ( :load_custom_auth_plugin , outside_file , temp_auth_dir )
201+ } . to raise_error ( SecurityError , /Auth plugin path outside of auth plugin directory/ )
202+ end
203+
204+ it "raises error for invalid auth plugin class name" do
205+ temp_auth_dir = File . join ( temp_dir , "auth_invalid" )
206+ FileUtils . mkdir_p ( temp_auth_dir )
207+
208+ # Create plugin with invalid class name
209+ invalid_file = File . join ( temp_auth_dir , "file.rb" )
210+ File . write ( invalid_file , "# File with dangerous class name" )
211+
212+ expect {
213+ described_class . send ( :load_custom_auth_plugin , invalid_file , temp_auth_dir )
214+ } . to raise_error ( StandardError , /Invalid auth plugin class name: File/ )
215+ end
216+
217+ it "raises error when auth plugin doesn't inherit from correct base class" do
218+ temp_auth_dir = File . join ( temp_dir , "auth_inheritance" )
219+ FileUtils . mkdir_p ( temp_auth_dir )
220+
221+ # Create plugin with wrong inheritance
222+ wrong_file = File . join ( temp_auth_dir , "wrong_auth.rb" )
223+ File . write ( wrong_file , <<~RUBY )
224+ module Hooks
225+ module Plugins
226+ module Auth
227+ class WrongAuth
228+ def self.valid?(payload:, headers:, config:)
229+ true
230+ end
231+ end
232+ end
233+ end
234+ end
235+ RUBY
236+
237+ expect {
238+ described_class . send ( :load_custom_auth_plugin , wrong_file , temp_auth_dir )
239+ } . to raise_error ( StandardError , /Auth plugin class must inherit from Hooks::Plugins::Auth::Base/ )
240+ end
241+ end
242+
243+ describe "handler plugin loading failures" do
244+ it "raises error when handler plugin file fails to load" do
245+ temp_handler_dir = File . join ( temp_dir , "handler_failures" )
246+ FileUtils . mkdir_p ( temp_handler_dir )
247+
248+ # Create a malformed Ruby file
249+ malformed_file = File . join ( temp_handler_dir , "broken_handler.rb" )
250+ File . write ( malformed_file , "class BrokenHandler\n # Missing end statement" )
251+
252+ expect {
253+ described_class . load_all_plugins ( { handler_plugin_dir : temp_handler_dir } )
254+ } . to raise_error ( StandardError , /Failed to load handler plugin from.*broken_handler\. rb/ )
255+ end
256+
257+ it "raises error for handler plugin path traversal attempt" do
258+ temp_handler_dir = File . join ( temp_dir , "handler_secure" )
259+ FileUtils . mkdir_p ( temp_handler_dir )
260+
261+ # Create a plugin file outside the handler directory
262+ outside_file = File . join ( temp_dir , "outside_handler.rb" )
263+ File . write ( outside_file , "# Outside file" )
264+
265+ expect {
266+ described_class . send ( :load_custom_handler_plugin , outside_file , temp_handler_dir )
267+ } . to raise_error ( SecurityError , /Handler plugin path outside of handler plugin directory/ )
268+ end
269+
270+ it "raises error for invalid handler plugin class name" do
271+ temp_handler_dir = File . join ( temp_dir , "handler_invalid" )
272+ FileUtils . mkdir_p ( temp_handler_dir )
273+
274+ # Create plugin with invalid class name
275+ invalid_file = File . join ( temp_handler_dir , "file.rb" )
276+ File . write ( invalid_file , "# File with dangerous class name" )
277+
278+ expect {
279+ described_class . send ( :load_custom_handler_plugin , invalid_file , temp_handler_dir )
280+ } . to raise_error ( StandardError , /Invalid handler class name: File/ )
281+ end
282+
283+ it "raises error when handler plugin doesn't inherit from correct base class" do
284+ temp_handler_dir = File . join ( temp_dir , "handler_inheritance" )
285+ FileUtils . mkdir_p ( temp_handler_dir )
286+
287+ # Create plugin with wrong inheritance
288+ wrong_file = File . join ( temp_handler_dir , "wrong_handler.rb" )
289+ File . write ( wrong_file , <<~RUBY )
290+ class WrongHandler
291+ def call(payload:, headers:, config:)
292+ { message: "wrong handler" }
293+ end
294+ end
295+ RUBY
296+
297+ expect {
298+ described_class . send ( :load_custom_handler_plugin , wrong_file , temp_handler_dir )
299+ } . to raise_error ( StandardError , /Handler class must inherit from Hooks::Plugins::Handlers::Base/ )
300+ end
301+ end
302+
303+ describe "lifecycle plugin loading failures" do
304+ it "raises error when lifecycle plugin file fails to load" do
305+ temp_lifecycle_dir = File . join ( temp_dir , "lifecycle_failures" )
306+ FileUtils . mkdir_p ( temp_lifecycle_dir )
307+
308+ # Create a malformed Ruby file
309+ malformed_file = File . join ( temp_lifecycle_dir , "broken_lifecycle.rb" )
310+ File . write ( malformed_file , "class BrokenLifecycle\n # Missing end statement" )
311+
312+ expect {
313+ described_class . load_all_plugins ( { lifecycle_plugin_dir : temp_lifecycle_dir } )
314+ } . to raise_error ( StandardError , /Failed to load lifecycle plugin from.*broken_lifecycle\. rb/ )
315+ end
316+
317+ it "raises error for lifecycle plugin path traversal attempt" do
318+ temp_lifecycle_dir = File . join ( temp_dir , "lifecycle_secure" )
319+ FileUtils . mkdir_p ( temp_lifecycle_dir )
320+
321+ # Create a plugin file outside the lifecycle directory
322+ outside_file = File . join ( temp_dir , "outside_lifecycle.rb" )
323+ File . write ( outside_file , "# Outside file" )
324+
325+ expect {
326+ described_class . send ( :load_custom_lifecycle_plugin , outside_file , temp_lifecycle_dir )
327+ } . to raise_error ( SecurityError , /Lifecycle plugin path outside of lifecycle plugin directory/ )
328+ end
329+
330+ it "raises error for invalid lifecycle plugin class name" do
331+ temp_lifecycle_dir = File . join ( temp_dir , "lifecycle_invalid" )
332+ FileUtils . mkdir_p ( temp_lifecycle_dir )
333+
334+ # Create plugin with invalid class name
335+ invalid_file = File . join ( temp_lifecycle_dir , "file.rb" )
336+ File . write ( invalid_file , "# File with dangerous class name" )
337+
338+ expect {
339+ described_class . send ( :load_custom_lifecycle_plugin , invalid_file , temp_lifecycle_dir )
340+ } . to raise_error ( StandardError , /Invalid lifecycle plugin class name: File/ )
341+ end
342+
343+ it "raises error when lifecycle plugin doesn't inherit from correct base class" do
344+ temp_lifecycle_dir = File . join ( temp_dir , "lifecycle_inheritance" )
345+ FileUtils . mkdir_p ( temp_lifecycle_dir )
346+
347+ # Create plugin with wrong inheritance
348+ wrong_file = File . join ( temp_lifecycle_dir , "wrong_lifecycle.rb" )
349+ File . write ( wrong_file , <<~RUBY )
350+ class WrongLifecycle
351+ def on_request(env)
352+ # Wrong base class
353+ end
354+ end
355+ RUBY
356+
357+ expect {
358+ described_class . send ( :load_custom_lifecycle_plugin , wrong_file , temp_lifecycle_dir )
359+ } . to raise_error ( StandardError , /Lifecycle plugin class must inherit from Hooks::Plugins::Lifecycle/ )
360+ end
361+ end
362+
363+ describe "instrument plugin loading failures" do
364+ it "raises error when instrument plugin file fails to load" do
365+ temp_instrument_dir = File . join ( temp_dir , "instrument_failures" )
366+ FileUtils . mkdir_p ( temp_instrument_dir )
367+
368+ # Create a malformed Ruby file
369+ malformed_file = File . join ( temp_instrument_dir , "broken_instrument.rb" )
370+ File . write ( malformed_file , "class BrokenInstrument\n # Missing end statement" )
371+
372+ expect {
373+ described_class . load_all_plugins ( { instruments_plugin_dir : temp_instrument_dir } )
374+ } . to raise_error ( StandardError , /Failed to load instrument plugin from.*broken_instrument\. rb/ )
375+ end
376+
377+ it "raises error for instrument plugin path traversal attempt" do
378+ temp_instrument_dir = File . join ( temp_dir , "instrument_secure" )
379+ FileUtils . mkdir_p ( temp_instrument_dir )
380+
381+ # Create a plugin file outside the instrument directory
382+ outside_file = File . join ( temp_dir , "outside_instrument.rb" )
383+ File . write ( outside_file , "# Outside file" )
384+
385+ expect {
386+ described_class . send ( :load_custom_instrument_plugin , outside_file , temp_instrument_dir )
387+ } . to raise_error ( SecurityError , /Instrument plugin path outside of instruments plugin directory/ )
388+ end
389+
390+ it "raises error for invalid instrument plugin class name" do
391+ temp_instrument_dir = File . join ( temp_dir , "instrument_invalid" )
392+ FileUtils . mkdir_p ( temp_instrument_dir )
393+
394+ # Create plugin with invalid class name
395+ invalid_file = File . join ( temp_instrument_dir , "file.rb" )
396+ File . write ( invalid_file , "# File with dangerous class name" )
397+
398+ expect {
399+ described_class . send ( :load_custom_instrument_plugin , invalid_file , temp_instrument_dir )
400+ } . to raise_error ( StandardError , /Invalid instrument plugin class name: File/ )
401+ end
402+
403+ it "raises error when instrument plugin doesn't inherit from correct base class" do
404+ temp_instrument_dir = File . join ( temp_dir , "instrument_inheritance" )
405+ FileUtils . mkdir_p ( temp_instrument_dir )
406+
407+ # Create plugin with wrong inheritance
408+ wrong_file = File . join ( temp_instrument_dir , "wrong_instrument.rb" )
409+ File . write ( wrong_file , <<~RUBY )
410+ class WrongInstrument
411+ def record(metric_name, value, tags = {})
412+ # Wrong base class
413+ end
414+ end
415+ RUBY
416+
417+ expect {
418+ described_class . send ( :load_custom_instrument_plugin , wrong_file , temp_instrument_dir )
419+ } . to raise_error ( StandardError , /Instrument plugin class must inherit from StatsBase or FailbotBase/ )
420+ end
421+ end
422+ end
175423end
0 commit comments