class SlackSmartBot
def run_repl(dest, user, session_name, env_vars, prerun, rules_file)
from = user.name
if has_access?(__method__, user)
save_stats(__method__)
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
code = prerun.join("\n")
if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
(@repls[session_name][:creator_name] != user.name or @repls[session_name][:creator_team_id] != user.team_id) and
!is_admin?(user)
respond "The REPL with session name: #{session_name} is private", dest
elsif !prerun.empty? and (code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
code.match?(/=?\s*(require|load)(\(|\s)/i))
respond "Sorry I cannot run this due security reasons", dest
else
if @repls.key?(session_name) @repls[session_name][:accessed] = Time.now.to_s
if @repls[session_name].creator_name == user.name and @repls[session_name].creator_team_id == user.team_id
@repls[session_name][:runs_by_creator] += 1
else
@repls[session_name][:runs_by_others] += 1
end
update_repls()
end
content = env_vars.join("\n")
content += "\nrequire 'nice_http'\n"
unless rules_file.empty? begin
eval(File.new(config.path + rules_file).read) if File.exist?(config.path + rules_file)
end
end
if File.exist?("#{project_folder}/.smart-bot-repl") and
((@repls.key?(session_name) and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean) or !@repls.key?(session_name))
content += File.read("#{project_folder}/.smart-bot-repl")
content += "\n"
end
unless prerun.empty?
content += prerun.join("\n")
content += "\n"
end
content += File.read("#{config.path}/repl/#{@channel_id}/#{session_name}.run").gsub(/^(quit|exit|bye)$/i, "") Dir.mkdir("#{project_folder}/tmp") unless Dir.exist?("#{project_folder}/tmp")
Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
if Thread.current[:on_thread]
content = "Thread.new do
while true do
puts ''
sleep 3
end
end
#{content}
"
end
random = "5:LN&".gen
File.write("#{project_folder}/tmp/repl/#{session_name}_#{user.name}_#{random}.rb", content, mode: "w+")
process_to_run = "ruby ./tmp/repl/#{session_name}_#{user.name}_#{random}.rb"
process_to_run = ("cd #{project_folder} && #{process_to_run}") if defined?(project_folder)
respond "Running REPL #{session_name} (id: #{random})"
@run_repls[random] = { team_id: user.team_id, user: user.name, name: session_name, pid: '' }
react :running
require "pty"
timeout = 60 * 60 * 4
started = Time.now
results = []
begin
PTY.spawn(process_to_run) do |stdout, stdin, pid|
last_result = -1
last_time = Time.now
@run_repls[random].pid = pid
begin
stdout.each do |line|
if (Time.now - started) > timeout
respond "run REPL session finished. Max time reached: #{session_name} (id: #{random})", dest
pids = `pgrep -P #{pid}`.split("\n").map(&:to_i) pids.each do |pd|
begin
Process.kill("KILL", pd)
rescue
end
end
break
else
results << line
if Thread.current[:on_thread]
if (Time.now - last_time) > 2
if (results.size - last_result) < 60 and results[(last_result + 1)..-1].join.size < 3500
output = ""
results[(last_result + 1)..-1].each do |li|
if li.match?(/^\s*{.+}\s*$/) or li.match?(/^\s*\[.+\]\s*$/)
output += "```\n#{li}```\n"
else
output += li
end
end
respond output
else
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: results[(last_result + 1)..-1].join)
end
last_result = results.size - 1
last_time = Time.now
end
end
end
end
rescue Errno::EIO
@logger.warn "run_repl PTY Errno:EIO error"
end
if results.empty?
respond "*#{session_name}* (id: #{random}): Nothing returned."
else
if last_result != (results.size - 1)
if (results.size - last_result) < 60 and results[(last_result + 1)..-1].join.size < 3500
output = ""
results[(last_result + 1)..-1].each do |li|
if li.match?(/^\s*{.+}\s*$/) or li.match?(/^\s*\[.+\]\s*$/)
output += "```\n#{li}```\n"
else
output += li
end
end
if Thread.current[:on_thread]
respond output
else
respond "*#{session_name}* (id: #{random}):\n#{output}"
end
else
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: results[(last_result + 1)..-1].join)
end
end
end
end
rescue PTY::ChildExited
@logger.warn "run_repl PTY The child process exited!"
end
@run_repls.delete(random) if @run_repls.key?(random)
unreact :running
end
else
respond "The REPL with session name: #{session_name} doesn't exist on this Smart Bot Channel", dest
end
end
end
end