# # Copyright (c) 2006-2021 Hal Brodigan (postmodern.mod3 at gmail.com) # # This file is part of ronin. # # Ronin is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ronin is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ronin. If not, see . # require 'ronin/config' require 'set' require 'shellwords' module Ronin module UI module Console # # Allows for executing shell commands prefixed by a `!`. # # @since 1.5.0 # # @api private # module Shell # Names and statuses of executables. EXECUTABLES = Hash.new do |hash,key| hash[key] = Config::BIN_DIRS.any? do |dir| path = File.join(dir,key) (File.file?(path) && File.executable?(path)) end end # Regexp to recognize `!commands`. PATTERN = /^![a-zA-Z][a-zA-Z0-9\._-]*/ # Blacklist of known commands that conflict with Ruby keywords. BLACKLIST = Set[ '[', 'ap', 'begin', 'case', 'class', 'def', 'fail', 'false', 'for', 'if', 'lambda', 'load', 'loop', 'module', 'p', 'pp', 'print', 'proc', 'puts', 'raise', 'require', 'true', 'undef', 'unless', 'until', 'warn', 'while' ] # # Dynamically execute shell commands, instead of Ruby. # # @param [String] input # The input from the console. # def loop_eval(input) if (@buffer.nil? && input =~ PATTERN) command = input[1..-1] name, arguments = parse_command(command) unless BLACKLIST.include?(name) if Shell.singleton_class.method_defined?(name) arguments ||= [] return Shell.send(name,*arguments) elsif executable?(name) return Shell.exec(name,*arguments) end end end super(input) end # # Default command which executes a command in the shell. # # @param [Array] arguments # The arguments of the command. # # @return [Boolean] # Specifies whether the command exited successfully. # # @since 1.5.0 # def self.exec(*arguments) system(Shellwords.shelljoin(arguments)) end # # Equivalent of the `cd` command, using `Dir.chdir`. # # @param [Array] arguments # The arguments of the command. # # @return [Boolean] # Specifies whether the directory change was successful. # def self.cd(*arguments) old_pwd = Dir.pwd new_cwd = if arguments.empty? Config::HOME elsif arguments.first == '-' unless ENV['OLDPWD'] print_warning 'cd: OLDPWD not set' return false end ENV['OLDPWD'] else arguments.first end Dir.chdir(new_cwd) ENV['OLDPWD'] = old_pwd return true end # # Equivalent of the `export` or `set` commands. # # @param [Array] arguments # The arguments of the command. # # @return [true] # def self.export(*arguments) arguments.each do |pair| name, value = pair.split('=',2) ENV[name] = value end end protected # # Parses a Console command. # # @param [String] command # The Console command to parse. # # @return [String, Array] # The command name and additional arguments. # # @since 1.5.0 # def parse_command(command) # evaluate embedded Ruby expressions command = command.gsub(/\#\{[^\}]*\}/) do |expression| eval(expression[2..-2],Ripl.config[:binding]).to_s.dump end arguments = Shellwords.shellsplit(command) name = arguments.shift return name, arguments end # # Determines if an executable exists on the system. # # @param [String] name # The program name or path. # # @return [Boolean] # Specifies whether the executable exists. # def executable?(name) (File.file?(name) && File.executable?(name)) || EXECUTABLES[name] end end end end end Ripl::Shell.send :include, Ronin::UI::Console::Shell