Class: Aspera::Faspex4PostProcServlet
- Inherits:
-
WEBrick::HTTPServlet::AbstractServlet
- Object
- WEBrick::HTTPServlet::AbstractServlet
- Aspera::Faspex4PostProcServlet
- Defined in:
- lib/aspera/faspex_postproc.rb
Overview
Start a Faspex-4 style post-processing script using Faspex-5 webhook
Constant Summary collapse
- ALLOWED_PARAMETERS =
%i[root script_folder fail_on_error timeout_seconds].freeze
Instance Method Summary collapse
-
#do_POST(request, response) ⇒ Object
:reek:UncommunicativeMethodName.
-
#initialize(server, parameters) ⇒ Faspex4PostProcServlet
constructor
A new instance of Faspex4PostProcServlet.
Constructor Details
#initialize(server, parameters) ⇒ Faspex4PostProcServlet
Returns a new instance of Faspex4PostProcServlet.
15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/aspera/faspex_postproc.rb', line 15 def initialize(server, parameters) Aspera.assert_type(parameters, Hash) @parameters = parameters.symbolize_keys Log.dump(:post_proc_parameters, @parameters) not_allowed = @parameters.keys - ALLOWED_PARAMETERS Aspera.assert(not_allowed.empty?) { "unsupported parameters: #{not_allowed.join(', ')}" } @parameters[:script_folder] ||= '.' @parameters[:fail_on_error] ||= false @parameters[:timeout_seconds] ||= 60 super(server) Log.log.debug { 'Faspex4PostProcServlet initialized' } end |
Instance Method Details
#do_POST(request, response) ⇒ Object
:reek:UncommunicativeMethodName
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/aspera/faspex_postproc.rb', line 29 def do_POST(request, response) Log.log.debug { "request=#{request.path}" } Log.dump(:query, request.query) begin # Only accept requests on the root if !request.path.start_with?(@parameters[:root]) response.status = 400 response['Content-Type'] = Mime::JSON response.body = {status: 'error', message: 'Request outside domain'}.to_json return end if request.body.nil? response.status = 400 response['Content-Type'] = Mime::JSON response.body = {status: 'error', message: 'Empty request'}.to_json return end # build script path by removing domain and adding script folder script_file = request.path[@parameters[:root].size..] Log.log.debug { "script file=#{script_file}" } script_path = File.join(@parameters[:script_folder], script_file) # Resolve the real path and ensure it stays within script_folder (prevents path traversal) resolved_script_folder = File.realpath(@parameters[:script_folder]) resolved_script_path = File.realpath(script_path) Aspera.assert(resolved_script_path.start_with?("#{resolved_script_folder}/")) { 'Script path traversal attempt detected' } script_path = resolved_script_path Log.log.debug { "script=#{script_path}" } webhook_parameters = JSON.parse(request.body) Log.dump(:webhook_parameters, webhook_parameters) process_status = nil if request.query.key?('lambda') # Code can throw exception, source code must return a lambda Environment.secure_eval(File.read(script_path), __FILE__, __LINE__).call(webhook_parameters) else # env expects only strings environment = webhook_parameters.each_with_object({}) { |(k, v), h| h[k] = v.to_s } post_proc_pid = Environment.secure_execute(script_path, mode: :background, env: environment) Timeout.timeout(@parameters[:timeout_seconds]) do # "wait" for process to avoid zombie Process.wait(post_proc_pid) post_proc_pid = nil end process_status = $CHILD_STATUS Aspera.assert(process_status.success? || !@parameters[:fail_on_error]) { "script #{script_path} failed with code #{process_status.exitstatus}" } end response.status = 200 response.content_type = Mime::JSON response.body = JSON.generate({status: 'success', script: script_path, exit_code: process_status&.exitstatus}) Log.log.debug { 'Script executed successfully' } rescue => e Log.log.error("Script failed: #{e.class}:#{e.}") if !post_proc_pid.nil? Process.kill('SIGKILL', post_proc_pid) Process.wait(post_proc_pid) Log.log.error("Killed process: #{post_proc_pid}") end response.status = 500 response['Content-Type'] = Mime::JSON response.body = {status: 'error', script: script_path, message: e.}.to_json end end |