Remove wpstools #793
This commit is contained in:
15
README.md
15
README.md
@@ -311,21 +311,6 @@ Debug output...
|
|||||||
|
|
||||||
```ruby wpscan.rb --url www.example.com --debug-output 2>debug.log```
|
```ruby wpscan.rb --url www.example.com --debug-output 2>debug.log```
|
||||||
|
|
||||||
#### WPSTOOLS ARGUMENTS
|
|
||||||
|
|
||||||
-v, --verbose Verbose output
|
|
||||||
--check-vuln-ref-urls, --cvru Check all the vulnerabilities reference urls for 404
|
|
||||||
--check-local-vulnerable-files, --clvf LOCAL_DIRECTORY Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells
|
|
||||||
-s, --stats Show WpScan Database statistics.
|
|
||||||
--spellcheck, --sc Check all files for common spelling mistakes.
|
|
||||||
|
|
||||||
|
|
||||||
#### WPSTOOLS EXAMPLES
|
|
||||||
|
|
||||||
Locally scan a wordpress installation for vulnerable files or shells:
|
|
||||||
|
|
||||||
```ruby wpstools.rb --check-local-vulnerable-files /var/www/wordpress/```
|
|
||||||
|
|
||||||
#### PROJECT HOME
|
#### PROJECT HOME
|
||||||
|
|
||||||
[http://www.wpscan.org](http://www.wpscan.org)
|
[http://www.wpscan.org](http://www.wpscan.org)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ DATA_DIR = File.join(ROOT_DIR, 'data')
|
|||||||
CONF_DIR = File.join(ROOT_DIR, 'conf')
|
CONF_DIR = File.join(ROOT_DIR, 'conf')
|
||||||
CACHE_DIR = File.join(ROOT_DIR, 'cache')
|
CACHE_DIR = File.join(ROOT_DIR, 'cache')
|
||||||
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan')
|
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan')
|
||||||
WPSTOOLS_LIB_DIR = File.join(LIB_DIR, 'wpstools')
|
|
||||||
UPDATER_LIB_DIR = File.join(LIB_DIR, 'updater')
|
UPDATER_LIB_DIR = File.join(LIB_DIR, 'updater')
|
||||||
COMMON_LIB_DIR = File.join(LIB_DIR, 'common')
|
COMMON_LIB_DIR = File.join(LIB_DIR, 'common')
|
||||||
MODELS_LIB_DIR = File.join(COMMON_LIB_DIR, 'models')
|
MODELS_LIB_DIR = File.join(COMMON_LIB_DIR, 'models')
|
||||||
@@ -17,7 +16,6 @@ LOG_FILE = File.join(ROOT_DIR, 'log.txt')
|
|||||||
# Plugins directories
|
# Plugins directories
|
||||||
COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
|
COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
|
||||||
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
|
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
|
||||||
WPSTOOLS_PLUGINS_DIR = File.join(WPSTOOLS_LIB_DIR, 'plugins')
|
|
||||||
|
|
||||||
# Data files
|
# Data files
|
||||||
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.txt')
|
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.txt')
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
class CheckerPlugin < Plugin
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super(author: 'WPScanTeam - @erwanlr')
|
|
||||||
|
|
||||||
register_options(
|
|
||||||
['--check-vuln-ref-urls', '--cvru', 'Check all the vulnerabilities reference urls for 404'],
|
|
||||||
['--check-local-vulnerable-files LOCAL_DIRECTORY', '--clvf', 'Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(options = {})
|
|
||||||
if options[:check_vuln_ref_urls]
|
|
||||||
check_vuln_ref_urls
|
|
||||||
end
|
|
||||||
|
|
||||||
if options[:check_local_vulnerable_files]
|
|
||||||
check_local_vulnerable_files(options[:check_local_vulnerable_files])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_vuln_ref_urls
|
|
||||||
vuln_ref_files = [PLUGINS_VULNS_FILE, THEMES_VULNS_FILE, WP_VULNS_FILE]
|
|
||||||
error_codes = [404, 500, 403]
|
|
||||||
not_found_regexp = %r{No Results Found|error 404|ID Invalid or Not Found}i
|
|
||||||
|
|
||||||
puts '[+] Checking vulnerabilities reference urls'
|
|
||||||
|
|
||||||
vuln_ref_files.each do |vuln_ref_file|
|
|
||||||
json = json(vuln_ref_file)
|
|
||||||
|
|
||||||
urls = []
|
|
||||||
json.each do |asset|
|
|
||||||
asset[asset.keys.inject]['vulnerabilities'].each do |url|
|
|
||||||
unless url['url'].nil?
|
|
||||||
url['url'].each do |url|
|
|
||||||
urls << url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
urls.uniq!
|
|
||||||
|
|
||||||
puts "[!] No URLs found in #{vuln_ref_file}!" if urls.empty?
|
|
||||||
|
|
||||||
dead_urls = []
|
|
||||||
queue_count = 0
|
|
||||||
request_count = 0
|
|
||||||
browser = Browser.instance
|
|
||||||
hydra = browser.hydra
|
|
||||||
number_of_urls = urls.size
|
|
||||||
|
|
||||||
urls.each do |url|
|
|
||||||
request = browser.forge_request(url, { cache_ttl: 0, followlocation: true })
|
|
||||||
request_count += 1
|
|
||||||
|
|
||||||
request.on_complete do |response|
|
|
||||||
print "\r [+] Checking #{vuln_ref_file} #{number_of_urls} total ... #{(request_count * 100) / number_of_urls}% complete."
|
|
||||||
|
|
||||||
if error_codes.include?(response.code) or not_found_regexp.match(response.body)
|
|
||||||
dead_urls << url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hydra.queue(request)
|
|
||||||
queue_count += 1
|
|
||||||
|
|
||||||
if queue_count == browser.max_threads
|
|
||||||
hydra.run
|
|
||||||
queue_count = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hydra.run
|
|
||||||
puts
|
|
||||||
unless dead_urls.empty?
|
|
||||||
dead_urls.each { |url| puts " Not Found #{url}" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_local_vulnerable_files(dir_to_scan)
|
|
||||||
if Dir.exist?(dir_to_scan)
|
|
||||||
xml_file = LOCAL_FILES_FILE
|
|
||||||
local_hashes = {}
|
|
||||||
file_extension_to_scan = '*.{js,php,swf,html,htm}'
|
|
||||||
|
|
||||||
print '[+] Generating local hashes ... '
|
|
||||||
|
|
||||||
Dir[File.join(dir_to_scan, '**', file_extension_to_scan)]
|
|
||||||
.select { |f| File.file?(f) }
|
|
||||||
.each do |filename|
|
|
||||||
sha1sum = Digest::SHA1.file(filename).hexdigest
|
|
||||||
|
|
||||||
if local_hashes.key?(sha1sum)
|
|
||||||
local_hashes[sha1sum] << filename
|
|
||||||
else
|
|
||||||
local_hashes[sha1sum] = [filename]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
puts 'done.'
|
|
||||||
|
|
||||||
puts '[+] Checking for vulnerable files ...'
|
|
||||||
|
|
||||||
xml = xml(xml_file)
|
|
||||||
|
|
||||||
xml.xpath('//hash').each do |node|
|
|
||||||
sha1sum = node.attribute('sha1').text
|
|
||||||
|
|
||||||
if local_hashes.has_key?(sha1sum)
|
|
||||||
local_filenames = local_hashes[sha1sum]
|
|
||||||
vuln_title = node.search('title').text
|
|
||||||
vuln_filename = node.search('file').text
|
|
||||||
vuln_refrence = node.search('reference').text
|
|
||||||
|
|
||||||
puts " #{vuln_filename} found :"
|
|
||||||
puts ' | Location(s):'
|
|
||||||
local_filenames.each do |file|
|
|
||||||
puts " | - #{file}"
|
|
||||||
end
|
|
||||||
puts ' |'
|
|
||||||
puts " | Title: #{vuln_title}"
|
|
||||||
puts " | Refrence: #{vuln_refrence}" if !vuln_refrence.empty?
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
puts 'done.'
|
|
||||||
|
|
||||||
else
|
|
||||||
puts "The supplied directory '#{dir_to_scan}' does not exist"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
class CheckerSpelling < Plugin
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super(author: 'WPScanTeam - @ethicalhack3r')
|
|
||||||
|
|
||||||
register_options(['--spellcheck', '--sc', 'Check all files for common spelling mistakes.'])
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(options = {})
|
|
||||||
spellcheck if options[:spellcheck]
|
|
||||||
end
|
|
||||||
|
|
||||||
def spellcheck
|
|
||||||
mistakes = 0
|
|
||||||
|
|
||||||
puts '[+] Checking for spelling mistakes'
|
|
||||||
puts
|
|
||||||
|
|
||||||
files.each do |file_name|
|
|
||||||
if File.exists?(file_name)
|
|
||||||
file = File.open(file_name, 'r')
|
|
||||||
|
|
||||||
misspellings.each_key do |misspelling|
|
|
||||||
begin
|
|
||||||
file.read.scan(/#{misspelling}/).each do |match|
|
|
||||||
mistakes += 1
|
|
||||||
puts "[MISSPELLING] File: #{file_name} Bad: #{match} Good: #{misspellings[misspelling]}"
|
|
||||||
end
|
|
||||||
rescue => e
|
|
||||||
puts "Error in #{file_name} #{e}"
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
file.close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
puts
|
|
||||||
puts "[+] Found #{mistakes} spelling mistakes"
|
|
||||||
|
|
||||||
mistakes
|
|
||||||
end
|
|
||||||
|
|
||||||
def misspellings
|
|
||||||
{
|
|
||||||
/databse/i => 'database',
|
|
||||||
/whith/i => 'with',
|
|
||||||
/wich/i => 'which',
|
|
||||||
/verions/i => 'versions',
|
|
||||||
/vulnerabilitiy/i => 'vulnerability',
|
|
||||||
/unkown/i => 'unknown',
|
|
||||||
/recieved/i => 'received',
|
|
||||||
/acheive/i => 'achieve',
|
|
||||||
/wierd/i => 'weird',
|
|
||||||
/untill/i => 'until',
|
|
||||||
/alot/i => 'a lot',
|
|
||||||
/randomstorm/ => 'RandomStorm',
|
|
||||||
/wpscan/ => 'WPScan',
|
|
||||||
/Wordpress/ => 'WordPress'
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def files
|
|
||||||
files = Dir['**/*'].reject {|fn| File.directory?(fn) }
|
|
||||||
|
|
||||||
ignore.each do |ignore|
|
|
||||||
files.delete_if { |data| data.match(ignore) }
|
|
||||||
end
|
|
||||||
|
|
||||||
files
|
|
||||||
end
|
|
||||||
|
|
||||||
def ignore
|
|
||||||
ignore = []
|
|
||||||
|
|
||||||
ignore << File.basename(__FILE__)
|
|
||||||
ignore << 'spec/cache/'
|
|
||||||
ignore << 'spec/spec_session/'
|
|
||||||
ignore << 'cache/'
|
|
||||||
ignore << 'coverage/'
|
|
||||||
ignore << 'wordlist-iso-8859-1'
|
|
||||||
ignore << 'log.txt'
|
|
||||||
ignore << 'debug.log'
|
|
||||||
ignore << 'wordlist.txt'
|
|
||||||
|
|
||||||
ignore
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
class StatsPlugin < Plugin
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super(author: 'WPScanTeam - Christian Mehlmauer')
|
|
||||||
|
|
||||||
register_options(
|
|
||||||
['--stats', '-s', 'Show WpScan Database statistics.']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(options = {})
|
|
||||||
if options[:stats]
|
|
||||||
date_wp = File.mtime(WP_VULNS_FILE)
|
|
||||||
date_plugins = File.mtime(PLUGINS_VULNS_FILE)
|
|
||||||
date_themes = File.mtime(THEMES_VULNS_FILE)
|
|
||||||
date_plugins_full = File.mtime(PLUGINS_FULL_FILE)
|
|
||||||
date_themes_full = File.mtime(THEMES_FULL_FILE)
|
|
||||||
|
|
||||||
puts "WPScan Database Statistics:"
|
|
||||||
puts "---------------------------"
|
|
||||||
puts
|
|
||||||
puts "[#] Total vulnerable versions: #{vuln_core_count}"
|
|
||||||
puts "[#] Total vulnerable plugins: #{vuln_plugin_count}"
|
|
||||||
puts "[#] Total vulnerable themes: #{vuln_theme_count}"
|
|
||||||
puts
|
|
||||||
puts "[#] Total version vulnerabilities: #{version_vulns_count}"
|
|
||||||
puts "[#] Total fixed vulnerabilities: #{fix_version_count}"
|
|
||||||
puts
|
|
||||||
puts "[#] Total plugin vulnerabilities: #{plugin_vulns_count}"
|
|
||||||
puts "[#] Total fixed vulnerabilities: #{fix_plugin_count}"
|
|
||||||
puts
|
|
||||||
puts "[#] Total theme vulnerabilities: #{theme_vulns_count}"
|
|
||||||
puts "[#] Total fixed vulnerabilities: #{fix_theme_count}"
|
|
||||||
puts
|
|
||||||
puts "[#] Total plugins to enumerate: #{total_plugins}"
|
|
||||||
puts "[#] Total themes to enumerate: #{total_themes}"
|
|
||||||
puts
|
|
||||||
puts "[+] WordPress DB modified: #{date_wp.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
puts "[+] Plugins DB modified: #{date_plugins.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
puts "[+] Themes DB modified: #{date_themes.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
puts "[+] Enumeration plugins: #{date_plugins_full.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
puts "[+] Enumeration themes: #{date_themes_full.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
puts
|
|
||||||
puts "[+] Report generated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def vuln_core_count(file=WP_VULNS_FILE)
|
|
||||||
json(file).size
|
|
||||||
end
|
|
||||||
|
|
||||||
def vuln_plugin_count(file=PLUGINS_VULNS_FILE)
|
|
||||||
json(file).size
|
|
||||||
end
|
|
||||||
|
|
||||||
def vuln_theme_count(file=THEMES_VULNS_FILE)
|
|
||||||
json(file).size
|
|
||||||
end
|
|
||||||
|
|
||||||
def version_vulns_count(file=WP_VULNS_FILE)
|
|
||||||
asset_vulns_count(json(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_version_count(file=WP_VULNS_FILE)
|
|
||||||
asset_fixed_in_count(json(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
def plugin_vulns_count(file=PLUGINS_VULNS_FILE)
|
|
||||||
asset_vulns_count(json(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_plugin_count(file=PLUGINS_VULNS_FILE)
|
|
||||||
asset_fixed_in_count(json(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
def theme_vulns_count(file=THEMES_VULNS_FILE)
|
|
||||||
asset_vulns_count(json(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_theme_count(file=THEMES_VULNS_FILE)
|
|
||||||
asset_fixed_in_count(json(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_plugins(file=PLUGINS_FULL_FILE)
|
|
||||||
lines_in_file(file)
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_themes(file=THEMES_FULL_FILE)
|
|
||||||
lines_in_file(file)
|
|
||||||
end
|
|
||||||
|
|
||||||
def lines_in_file(file)
|
|
||||||
IO.readlines(file).size
|
|
||||||
end
|
|
||||||
|
|
||||||
def asset_vulns_count(json)
|
|
||||||
json.map { |asset| asset[asset.keys.inject]['vulnerabilities'].size }.inject(:+)
|
|
||||||
end
|
|
||||||
|
|
||||||
def asset_fixed_in_count(json)
|
|
||||||
json.map { |asset| asset[asset.keys.inject]['vulnerabilities'].map {|a| a['fixed_in'].nil? ? 0 : 1 }.inject(:+) }.inject(:+)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
require File.expand_path(File.dirname(__FILE__) + '/../common/common_helper')
|
|
||||||
|
|
||||||
require_files_from_directory(WPSTOOLS_LIB_DIR)
|
|
||||||
require_files_from_directory(WPSTOOLS_PLUGINS_DIR, '**/*.rb')
|
|
||||||
|
|
||||||
def usage
|
|
||||||
script_name = $0
|
|
||||||
puts
|
|
||||||
puts '-h for further help.'
|
|
||||||
puts
|
|
||||||
puts 'Examples:'
|
|
||||||
puts
|
|
||||||
puts 'Locally scan a wordpress installation for vulnerable files or shells'
|
|
||||||
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
|
|
||||||
puts
|
|
||||||
puts 'See README for further information.'
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
require File.expand_path(File.dirname(__FILE__) + '/../../wpstools_helper')
|
|
||||||
|
|
||||||
describe 'StatsPlugin' do
|
|
||||||
subject(:stats) { StatsPlugin.new }
|
|
||||||
let(:plugins_vulns) { MODELS_FIXTURES + '/wp_plugin/vulnerable/plugins_vulns.json' }
|
|
||||||
let(:themes_vulns) { MODELS_FIXTURES + '/wp_theme/vulnerable/themes_vulns.json' }
|
|
||||||
let(:plugins_file) { COLLECTIONS_FIXTURES + '/wp_plugins/detectable/targets.txt' }
|
|
||||||
let(:themes_file) { COLLECTIONS_FIXTURES + '/wp_themes/detectable/targets.txt'}
|
|
||||||
|
|
||||||
describe '#vuln_plugin_count' do
|
|
||||||
it 'returns the correct number' do
|
|
||||||
expect(stats.vuln_plugin_count(plugins_vulns)).to eq 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#vuln_theme_count' do
|
|
||||||
it 'returns the correct number' do
|
|
||||||
expect(stats.vuln_theme_count(themes_vulns)).to eq 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#plugin_vulns_count' do
|
|
||||||
it 'returns the correct number' do
|
|
||||||
expect(stats.plugin_vulns_count(plugins_vulns)).to eq 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#theme_vulns_count' do
|
|
||||||
it 'returns the correct number' do
|
|
||||||
expect(stats.theme_vulns_count(themes_vulns)).to eq 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#total_plugins' do
|
|
||||||
it 'returns the correct numer' do
|
|
||||||
expect(stats.total_plugins(plugins_file)).to eq 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#total_themes' do
|
|
||||||
it 'returns the correct numer' do
|
|
||||||
expect(stats.total_themes(themes_file)).to eq 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
require WPSTOOLS_LIB_DIR + '/wpstools_helper'
|
|
||||||
44
wpstools.rb
44
wpstools.rb
@@ -1,44 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
$: << '.'
|
|
||||||
require File.dirname(__FILE__) + '/lib/wpstools/wpstools_helper'
|
|
||||||
|
|
||||||
begin
|
|
||||||
# delete old logfile, check if it is a symlink first.
|
|
||||||
File.delete(LOG_FILE) if File.exist?(LOG_FILE) and !File.symlink?(LOG_FILE)
|
|
||||||
|
|
||||||
banner()
|
|
||||||
|
|
||||||
option_parser = CustomOptionParser.new('Usage: ./wpstools.rb [options]', 60)
|
|
||||||
option_parser.separator ''
|
|
||||||
option_parser.add(['-v', '--verbose', 'Verbose output'])
|
|
||||||
|
|
||||||
plugins = Plugins.new(option_parser)
|
|
||||||
plugins.register(
|
|
||||||
CheckerPlugin.new,
|
|
||||||
StatsPlugin.new,
|
|
||||||
CheckerSpelling.new
|
|
||||||
)
|
|
||||||
|
|
||||||
options = option_parser.results
|
|
||||||
|
|
||||||
if options.empty?
|
|
||||||
raise "No option supplied\n\n#{option_parser}"
|
|
||||||
end
|
|
||||||
|
|
||||||
plugins.each do |plugin|
|
|
||||||
plugin.run(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
exit(0)
|
|
||||||
rescue => e
|
|
||||||
puts "[ERROR] #{e.message}"
|
|
||||||
|
|
||||||
unless e.backtrace[0] =~ /main/
|
|
||||||
puts 'Trace :'
|
|
||||||
puts e.backtrace.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user