From 3370465d84234e6ff76ee92306f961e6b4d189c0 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 12 May 2014 15:03:51 -0500 Subject: [PATCH] Use railties to load Metasploit::Credential correctly MSP-9606 In order to support Metasploit::Credential correctly, metasploit-framework needs to support Metasploit::Concern, which does all its magic using a Rails::Engine initializer, so the easiest path is to make metasploit-framework be able to use Rails::Engines. To make Rails::Engine use Rails::Engine, make a dummy Rails::Application subclass so that all the initializers will be run when anything requires msfenv. --- .gitignore | 3 + Gemfile | 16 +++- Gemfile.lock | 70 +++++++++++++- Rakefile | 83 +---------------- config/application.rb | 33 +++++++ config/boot.rb | 33 +++++++ config/environment.rb | 5 + db/migrate/.git-keep | 0 db/schema.rb | 93 ++++++++++++++++++- lib/metasploit/framework.rb | 24 ----- lib/metasploit/framework/database.rb | 2 +- lib/msf/core/db_manager.rb | 93 +++++++++---------- lib/msfenv.rb | 13 +-- lib/tasks/database.rake | 73 --------------- lib/tasks/databases.rake | 7 ++ lib/tasks/rails.rake | 21 ----- script/rails | 6 ++ .../abstract_adapter/connection_pool_spec.rb | 7 +- spec/lib/msf/core/modules/loader/base_spec.rb | 12 +++ spec/lib/msf/db_manager/export_spec.rb | 2 +- spec/lib/msf/db_manager_spec.rb | 28 ++---- spec/spec_helper.rb | 59 +++++------- .../shared/contexts/database_cleaner.rb | 37 -------- .../support/shared/contexts/msf/db_manager.rb | 10 +- .../examples/msf/db_manager/import_msf_xml.rb | 14 --- .../examples/msf/db_manager/migration.rb | 2 +- .../examples/msf/module_manager/cache.rb | 19 +--- 27 files changed, 366 insertions(+), 399 deletions(-) create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/environment.rb create mode 100644 db/migrate/.git-keep delete mode 100644 lib/tasks/database.rake create mode 100644 lib/tasks/databases.rake delete mode 100644 lib/tasks/rails.rake create mode 100644 script/rails delete mode 100644 spec/support/shared/contexts/database_cleaner.rb diff --git a/.gitignore b/.gitignore index c232e2ba43..dc18acb40a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,9 @@ tags *.opensdf *.user +# Rails log directory +/log + # ignore release/debug folders for exploits external/source/exploits/**/Debug external/source/exploits/**/Release diff --git a/Gemfile b/Gemfile index 1063c7741c..c08214f542 100755 --- a/Gemfile +++ b/Gemfile @@ -12,16 +12,21 @@ gem 'msgpack' gem 'nokogiri' # Needed by db.rb and Msf::Exploit::Capture gem 'packetfu', '1.1.9' +# Run initializers for metasploit-concern, metasploit-credential, metasploit_data_models Rails::Engines +gem 'railties' # Needed by JSObfu gem 'rkelly-remix', '0.0.6' # Needed by anemone crawler gem 'robots' + group :db do # Needed for Msf::DbManager gem 'activerecord', '>= 3.0.0', '< 4.0.0' + # Metasploit::Creential database models + gem 'metasploit-credential', git: 'github-metasploit-credential:rapid7/metasploit-credential.git', tag: 'v0.1.2-metasploit-credential' # Database models shared between framework and Pro. - gem 'metasploit_data_models', '~> 0.17.0' + gem 'metasploit_data_models', '~> 0.17.1' # Needed for module caching in Mdm::ModuleDetails gem 'pg', '>= 0.11' end @@ -38,10 +43,17 @@ group :development, :test do # Version 4.1.0 or newer is needed to support generate calls without the # 'FactoryGirl.' in factory definitions syntax. gem 'factory_girl', '>= 4.1.0' + # automatically include factories from spec/factories + gem 'factory_girl_rails' # Make rspec output shorter and more useful gem 'fivemat', '1.2.1' # running documentation generation tasks and rspec tasks gem 'rake', '>= 10.0.0' + # testing framework + gem 'rspec', '>= 2.12' + # Define `rake spec`. Must be in development AND test so that its available by default as a rake test when the + # environment is development + gem 'rspec-rails' end group :pcap do @@ -55,8 +67,6 @@ group :test do # transactional fixtures because multiple connections are in use so # transactions won't work. gem 'database_cleaner' - # testing framework - gem 'rspec', '>= 2.12' gem 'shoulda-matchers' # code coverage for tests # any version newer than 0.5.4 gives an Encoding error when trying to read the source files. diff --git a/Gemfile.lock b/Gemfile.lock index 24c9e87b7f..b5d1b7db47 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,26 @@ +GIT + remote: github-metasploit-credential:rapid7/metasploit-credential.git + revision: 2f8384cd5f7d0124e276a6e4b7fa8193dd96f56c + tag: v0.1.2-metasploit-credential + specs: + metasploit-credential (0.1.2.pre.metasploit.pre.credential) + metasploit-concern (~> 0.0.4) + metasploit_data_models (~> 0.17.0) + rubyntlm + GEM remote: https://rubygems.org/ specs: + actionpack (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) + builder (~> 3.0.0) + erubis (~> 2.7.0) + journey (~> 1.0.4) + rack (~> 1.4.5) + rack-cache (~> 1.2) + rack-test (~> 0.6.1) + sprockets (~> 2.2.1) activemodel (3.2.17) activesupport (= 3.2.17) builder (~> 3.0.0) @@ -17,13 +37,21 @@ GEM builder (3.0.4) database_cleaner (1.2.0) diff-lcs (1.2.5) + erubis (2.7.0) factory_girl (4.4.0) activesupport (>= 3.0.0) + factory_girl_rails (4.4.1) + factory_girl (~> 4.4.0) + railties (>= 3.0.0) fivemat (1.2.1) + hike (1.2.3) i18n (0.6.9) + journey (1.0.4) json (1.8.1) - metasploit_data_models (0.17.0) - activerecord (>= 3.2.13) + metasploit-concern (0.0.4) + activesupport (~> 3.0, >= 3.0.0) + metasploit_data_models (0.17.1) + activerecord (>= 3.2.13, < 4.0.0) activesupport pg mini_portile (0.5.3) @@ -35,7 +63,23 @@ GEM packetfu (1.1.9) pcaprub (0.11.3) pg (0.17.1) + rack (1.4.5) + rack-cache (1.2) + rack (>= 0.4) + rack-ssl (1.3.4) + rack + rack-test (0.6.2) + rack (>= 1.0) + railties (3.2.17) + actionpack (= 3.2.17) + activesupport (= 3.2.17) + rack-ssl (~> 1.3.2) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (>= 0.14.6, < 2.0) rake (10.3.1) + rdoc (3.12.2) + json (~> 1.4) redcarpet (3.1.1) rkelly-remix (0.0.6) robots (0.10.1) @@ -47,12 +91,28 @@ GEM rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) rspec-mocks (2.14.6) + rspec-rails (2.14.2) + actionpack (>= 3.0) + activemodel (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rubyntlm (0.4.0) shoulda-matchers (2.6.0) activesupport (>= 3.0.0) simplecov (0.5.4) multi_json (~> 1.0.3) simplecov-html (~> 0.5.3) simplecov-html (0.5.3) + sprockets (2.2.2) + hike (~> 1.2) + multi_json (~> 1.0) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + thor (0.19.1) + tilt (1.4.1) timecop (0.7.1) tzinfo (0.3.39) yard (0.8.7.4) @@ -66,20 +126,24 @@ DEPENDENCIES bcrypt database_cleaner factory_girl (>= 4.1.0) + factory_girl_rails fivemat (= 1.2.1) json - metasploit_data_models (~> 0.17.0) + metasploit-credential! + metasploit_data_models (~> 0.17.1) msgpack network_interface (~> 0.0.1) nokogiri packetfu (= 1.1.9) pcaprub pg (>= 0.11) + railties rake (>= 10.0.0) redcarpet rkelly-remix (= 0.0.6) robots rspec (>= 2.12) + rspec-rails shoulda-matchers simplecov (= 0.5.4) timecop diff --git a/Rakefile b/Rakefile index 749f886717..14d403ce70 100644 --- a/Rakefile +++ b/Rakefile @@ -1,81 +1,4 @@ -require 'bundler/setup' +#!/usr/bin/env rake +require File.expand_path('../config/application', __FILE__) -pathname = Pathname.new(__FILE__) -root = pathname.parent - -# add metasploit-framework/lib to load paths so rake files can just require -# files normally without having to use __FILE__ and recalculating root and the -# path to lib -lib_pathname = root.join('lib') -$LOAD_PATH.unshift(lib_pathname.to_s) - -# -# load rake files like a rails engine -# - -rakefile_glob = root.join('lib', 'tasks', '**', '*.rake').to_path - -Dir.glob(rakefile_glob) do |rakefile| - # Skip database tasks, will load them later if MDM is present - next if rakefile =~ /database\.rake$/ - load rakefile -end - -print_without = false - -begin - require 'rspec/core/rake_task' -rescue LoadError - puts "rspec not in bundle, so can't set up spec tasks. " \ - "To run specs ensure to install the development and test groups." - - print_without = true -else - RSpec::Core::RakeTask.new(:spec => 'db:test:prepare') - - task :default => :spec -end - -# Require yard before loading metasploit_data_models rake tasks as the yard tasks won't be defined if -# YARD is not defined when yard.rake is loaded. -begin - require 'yard' -rescue LoadError - puts "yard not in bundle, so can't set up yard tasks. " \ - "To generate documentation ensure to install the development group." - - print_without = true -end - -begin - require 'metasploit_data_models' -rescue LoadError - puts "metasploit_data_models not in bundle, so can't set up db tasks. " \ - "To run database tasks, ensure to install the db bundler group." - - print_without = true -else - load 'lib/tasks/database.rake' - metasploit_data_models_task_glob = MetasploitDataModels.root.join( - 'lib', - 'tasks', - '**', - '*.rake' - ).to_s - # include tasks from metasplioit_data_models, such as `rake yard`. - # metasploit-framework specific yard options are in .yardopts - Dir.glob(metasploit_data_models_task_glob) do |path| - load path - end -end - - - -if print_without - puts "Bundle currently installed " \ - "'--without #{Bundler.settings.without.join(' ')}'." - puts "To clear the without option do `bundle install --without ''` " \ - "(the --without flag with an empty string) or " \ - "`rm -rf .bundle` to remove the .bundle/config manually and " \ - "then `bundle install`" -end +Metasploit::Framework::Application.load_tasks diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000000..db0ee8d247 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,33 @@ +require 'rails' +require File.expand_path('../boot', __FILE__) + +# only the parts of 'rails/all' that metasploit-framework actually uses +require 'active_record/railtie' + +all_environments = [ + :development, + :production, + :test +] + +Bundler.require( + *Rails.groups( + db: all_environments, + pcap: all_environments + ) +) + +require 'msf/base/config' + +module Metasploit + module Framework + class Application < Rails::Application + user_config_root = Pathname.new(Msf::Config.get_config_root) + user_database_yaml = user_config_root.join('database.yml') + + if user_database_yaml.exist? + config.paths['config/database'] = [user_database_yaml.to_path] + end + end + end +end \ No newline at end of file diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000000..6978ea7d55 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,33 @@ +require 'pathname' +require 'rubygems' + +bundle_gemfile = ENV['BUNDLE_GEMFILE'] + +config_pathname = Pathname.new(__FILE__).expand_path.parent +root = config_pathname.parent + +if bundle_gemfile + bundle_gemfile = Pathname.new(bundle_gemfile) +else + bundle_gemfile = root.join('Gemfile') +end + +if bundle_gemfile.exist? + ENV['BUNDLE_GEMFILE'] = bundle_gemfile.to_path + + begin + require 'bundler' + rescue LoadError + $stderr.puts "[*] Metasploit requires the Bundler gem to be installed" + $stderr.puts " $ gem install bundler" + exit(0) + end +end + +Bundler.setup + +lib_path = root.join('lib').to_path + +unless $LOAD_PATH.include? lib_path + $LOAD_PATH.unshift lib_path +end diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000000..4aa34124b9 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the rails application +require File.expand_path('../application', __FILE__) + +# Initialize the rails application +Metasploit::Framework::Application.initialize! diff --git a/db/migrate/.git-keep b/db/migrate/.git-keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/db/schema.rb b/db/schema.rb index 42093e6764..f71e75026b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130717150737) do +ActiveRecord::Schema.define(:version => 20140417140933) do create_table "api_keys", :force => true do |t| t.text "token" @@ -167,6 +167,97 @@ ActiveRecord::Schema.define(:version => 20130717150737) do t.binary "prefs" end + create_table "metasploit_credential_cores", :force => true do |t| + t.integer "origin_id", :null => false + t.string "origin_type", :null => false + t.integer "private_id" + t.integer "public_id" + t.integer "realm_id" + t.integer "workspace_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_cores", ["origin_type", "origin_id"], :name => "index_metasploit_credential_cores_on_origin_type_and_origin_id" + add_index "metasploit_credential_cores", ["private_id"], :name => "index_metasploit_credential_cores_on_private_id" + add_index "metasploit_credential_cores", ["public_id"], :name => "index_metasploit_credential_cores_on_public_id" + add_index "metasploit_credential_cores", ["realm_id"], :name => "index_metasploit_credential_cores_on_realm_id" + add_index "metasploit_credential_cores", ["workspace_id"], :name => "index_metasploit_credential_cores_on_workspace_id" + + create_table "metasploit_credential_logins", :force => true do |t| + t.integer "core_id", :null => false + t.integer "service_id", :null => false + t.string "access_level" + t.string "status", :null => false + t.datetime "last_attempted_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_logins", ["core_id", "service_id"], :name => "index_metasploit_credential_logins_on_core_id_and_service_id", :unique => true + add_index "metasploit_credential_logins", ["service_id", "core_id"], :name => "index_metasploit_credential_logins_on_service_id_and_core_id", :unique => true + + create_table "metasploit_credential_origin_imports", :force => true do |t| + t.text "filename", :null => false + t.integer "task_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_origin_imports", ["task_id"], :name => "index_metasploit_credential_origin_imports_on_task_id" + + create_table "metasploit_credential_origin_manuals", :force => true do |t| + t.integer "user_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_origin_manuals", ["user_id"], :name => "index_metasploit_credential_origin_manuals_on_user_id" + + create_table "metasploit_credential_origin_services", :force => true do |t| + t.integer "service_id", :null => false + t.text "module_full_name", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_origin_services", ["service_id", "module_full_name"], :name => "unique_metasploit_credential_origin_services", :unique => true + + create_table "metasploit_credential_origin_sessions", :force => true do |t| + t.text "post_reference_name", :null => false + t.integer "session_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_origin_sessions", ["session_id", "post_reference_name"], :name => "unique_metasploit_credential_origin_sessions", :unique => true + + create_table "metasploit_credential_privates", :force => true do |t| + t.string "type", :null => false + t.text "data", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_privates", ["type", "data"], :name => "index_metasploit_credential_privates_on_type_and_data", :unique => true + + create_table "metasploit_credential_publics", :force => true do |t| + t.string "username", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_publics", ["username"], :name => "index_metasploit_credential_publics_on_username", :unique => true + + create_table "metasploit_credential_realms", :force => true do |t| + t.string "key", :null => false + t.string "value", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "metasploit_credential_realms", ["key", "value"], :name => "index_metasploit_credential_realms_on_key_and_value", :unique => true + create_table "mod_refs", :force => true do |t| t.string "module", :limit => 1024 t.string "mtype", :limit => 128 diff --git a/lib/metasploit/framework.rb b/lib/metasploit/framework.rb index f30e4d4399..6577352a45 100644 --- a/lib/metasploit/framework.rb +++ b/lib/metasploit/framework.rb @@ -5,30 +5,6 @@ module Metasploit # works in compatible manner with activerecord's rake tasks and other # railties. module Framework - # Returns the environment for {Metasploit::Framework}. Checks - # `METASPLOIT_FRAMEWORK_ENV` environment variable for value. Defaults to - # `'development'` if `METASPLOIT_FRAMEWORK_ENV` is not set in the - # environment variables. - # - # {env} is a ActiveSupport::StringInquirer like `Rails.env` so it can be - # queried for its value. - # - # @example check if environment is development - # if Metasploit::Framework.env.development? - # # runs only when in development - # end - # - # @return [ActiveSupport::StringInquirer] the environment name - def self.env - unless instance_variable_defined? :@env - name = ENV['METASPLOIT_FRAMEWORK_ENV'] - name ||= 'development' - @env = ActiveSupport::StringInquirer.new(name) - end - - @env - end - # Returns the root of the metasploit-framework project. Use in place of # `Rails.root`. # diff --git a/lib/metasploit/framework/database.rb b/lib/metasploit/framework/database.rb index 78d3525cf5..4c8f99d4da 100644 --- a/lib/metasploit/framework/database.rb +++ b/lib/metasploit/framework/database.rb @@ -8,7 +8,7 @@ module Metasploit end def self.configurations_pathname - Metasploit::Framework.root.join('config', 'database.yml') + Metasploit::Framework::Application.paths['config/database'].first end end end diff --git a/lib/msf/core/db_manager.rb b/lib/msf/core/db_manager.rb index 52d3eac6d5..187e54d57c 100644 --- a/lib/msf/core/db_manager.rb +++ b/lib/msf/core/db_manager.rb @@ -22,6 +22,13 @@ class DBManager include Msf::DBManager::Migration include Msf::Framework::Offspring + # + # CONSTANTS + # + + # The adapter to use to establish database connection. + ADAPTER = 'postgresql' + # Mainly, it's Ruby 1.9.1 that cause a lot of problems now, along with Ruby 1.8.6. # Ruby 1.8.7 actually seems okay, but why tempt fate? Let's say 1.9.3 and beyond. def warn_about_rubies @@ -86,9 +93,7 @@ class DBManager # Database drivers can reset our KCODE, do not let them $KCODE = 'NONE' if RUBY_VERSION =~ /^1\.8\./ - require "active_record" - - initialize_metasploit_data_models + add_rails_engine_migration_paths @usable = true @@ -98,22 +103,10 @@ class DBManager return false end - # Only include Mdm if we're not using Metasploit commercial versions - # If Mdm::Host is defined, the dynamically created classes - # are already in the object space - begin - unless defined? Mdm::Host - MetasploitDataModels.require_models - end - rescue NameError => e - warn_about_rubies - raise e - end - # # Determine what drivers are available # - initialize_drivers + initialize_adapter # # Instantiate the database sink @@ -126,50 +119,42 @@ class DBManager # # Scan through available drivers # - def initialize_drivers - self.drivers = [] - tdrivers = %W{ postgresql } - tdrivers.each do |driver| + def initialize_adapter + ActiveRecord::Base.default_timezone = :utc + + if ActiveRecord::Base.connected? && ActiveRecord::Base.connection_config[:adapter] == ADAPTER + dlog("Already connected to #{ADAPTER}, so reusing active connection.") + else begin - ActiveRecord::Base.default_timezone = :utc - ActiveRecord::Base.establish_connection(:adapter => driver) - if(self.respond_to?("driver_check_#{driver}")) - self.send("driver_check_#{driver}") - end + ActiveRecord::Base.establish_connection(adapter: ADAPTER) ActiveRecord::Base.remove_connection - self.drivers << driver - rescue ::Exception + rescue Exception => error + @adapter_error = error + else + # @deprecated Use in RPC_Db, but only postgresql is supported, so useless otherwise + self.drivers << ADAPTER + self.driver = ADAPTER end end - - if(not self.drivers.empty?) - self.driver = self.drivers[0] - end - - # Database drivers can reset our KCODE, do not let them - $KCODE = 'NONE' if RUBY_VERSION =~ /^1\.8\./ end # Loads Metasploit Data Models and adds its migrations to migrations paths. # # @return [void] - def initialize_metasploit_data_models - # Provide access to ActiveRecord models shared w/ commercial versions - require "metasploit_data_models" + def add_rails_engine_migration_paths + Rails.application.railties.engines.each do |engine| + migrations_paths = engine.paths['db/migrate'].existent_directories - metasploit_data_model_migrations_pathname = MetasploitDataModels.root.join( - 'db', - 'migrate' - ) - metasploit_data_model_migrations_path = metasploit_data_model_migrations_pathname.to_s - - # Since ActiveRecord::Migrator.migrations_paths can persist between - # instances of Msf::DBManager, such as in specs, - # metasploit_data_models_migrations_path may already be part of - # migrations_paths, in which case it should not be added or multiple - # migrations with the same version number errors will occur. - unless ActiveRecord::Migrator.migrations_paths.include? metasploit_data_model_migrations_path - ActiveRecord::Migrator.migrations_paths << metasploit_data_model_migrations_path + migrations_paths.each do |migrations_path| + # Since ActiveRecord::Migrator.migrations_paths can persist between + # instances of Msf::DBManager, such as in specs, + # migrations_path may already be part of + # migrations_paths, in which case it should not be added or multiple + # migrations with the same version number errors will occur. + unless ActiveRecord::Migrator.migrations_paths.include? migrations_path + ActiveRecord::Migrator.migrations_paths << migrations_path + end + end end end @@ -259,7 +244,13 @@ class DBManager errstr = e.to_s if errstr =~ /does not exist/i or errstr =~ /Unknown database/ ilog("Database doesn't exist \"#{opts['database']}\", attempting to create it.") - ActiveRecord::Base.establish_connection(opts.merge('database' => nil)) + ActiveRecord::Base.establish_connection( + opts.merge( + 'database' => 'postgres', + 'schema_search_path' => 'public' + ) + ) + ActiveRecord::Base.connection.create_database(opts['database']) else ilog("Trying to continue despite failed database creation: #{e}") diff --git a/lib/msfenv.rb b/lib/msfenv.rb index a6e00a1df6..0dce7761dd 100644 --- a/lib/msfenv.rb +++ b/lib/msfenv.rb @@ -2,11 +2,8 @@ # Use bundler to load dependencies # -ENV['BUNDLE_GEMFILE'] ||= ::File.expand_path(::File.join(::File.dirname(__FILE__), "..", "Gemfile")) -begin - require 'bundler/setup' -rescue ::LoadError - $stderr.puts "[*] Metasploit requires the Bundler gem to be installed" - $stderr.puts " $ gem install bundler" - exit(0) -end +require 'pathname' +root = Pathname.new(__FILE__).expand_path.parent.parent +config = root.join('config') +require config.join('boot') +require config.join('environment') diff --git a/lib/tasks/database.rake b/lib/tasks/database.rake deleted file mode 100644 index 646db409ff..0000000000 --- a/lib/tasks/database.rake +++ /dev/null @@ -1,73 +0,0 @@ -load 'active_record/railties/databases.rake' - -require 'metasploit/framework' -require 'metasploit/framework/database' - -# A modification to remove dependency on Rails.env -# -# @see https://github.com/rails/rails/blob/ddce29bfa12462fde2342a0c2bd0eefd420c0eab/activerecord/lib/active_record/railties/databases.rake#L550 -def configs_for_environment - environments = [Metasploit::Framework.env] - - if Metasploit::Framework.env.development? - environments << 'test' - end - - environment_configurations = ActiveRecord::Base.configurations.values_at(*environments) - present_environment_configurations = environment_configurations.compact - valid_environment_configurations = present_environment_configurations.reject { |config| - config['database'].blank? - } - - valid_environment_configurations -end - -# emulate initializer "active_record.initialize_database" from active_record/railtie -ActiveSupport.on_load(:active_record) do - self.configurations = Metasploit::Framework::Database.configurations - puts "Connecting to database specified by #{Metasploit::Framework::Database.configurations_pathname}" - - spec = configurations[Metasploit::Framework.env] - establish_connection(spec) -end - -# -# Remove tasks that aren't supported -# - -Rake::TaskManager.class_eval do - def remove_task(task_name) - @tasks.delete(task_name.to_s) - end -end - -Rake.application.remove_task('db:fixtures:load') - -# completely replace db:load_config and db:seed as they will attempt to use -# Rails.application, which does not exist -Rake::Task['db:load_config'].clear -Rake::Task['db:seed'].clear - -db_namespace = namespace :db do - task :load_config do - ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations - - ActiveRecord::Migrator.migrations_paths = [ - # rails isn't in Gemfile, so can't use the more appropriate - # Metasploit::Engine.instance.paths['db/migrate'].to_a since using - # Metasploit::Engine requires rails. - MetasploitDataModels.root.join('db', 'migrate').to_s - ] - end - - desc 'Load the seed data from db/seeds.rb' - task :seed do - db_namespace['abort_if_pending_migrations'].invoke - seeds_pathname = Metasploit::Framework.root.join('db', 'seeds.rb') - - if seeds_pathname.exist? - load(seeds_pathname) - end - end -end - diff --git a/lib/tasks/databases.rake b/lib/tasks/databases.rake new file mode 100644 index 0000000000..f75cd735ee --- /dev/null +++ b/lib/tasks/databases.rake @@ -0,0 +1,7 @@ +namespace :db do + # Add onto the task so that after adding Rails.application.paths['db/migrate'] + task :load_config do + # It's important to call to_a or the paths will just be relative and not realpaths + ActiveRecord::Migrator.migrations_paths += Metasploit::Credential::Engine.instance.paths['db/migrate'].to_a + end +end \ No newline at end of file diff --git a/lib/tasks/rails.rake b/lib/tasks/rails.rake deleted file mode 100644 index bcb90975aa..0000000000 --- a/lib/tasks/rails.rake +++ /dev/null @@ -1,21 +0,0 @@ -# Rake tasks added for compatibility with rake tasks that depend on a Rails -# environment, such as those in activerecord - -# Would normally load config/environment.rb of the rails application. -# -# @see https://github.com/rails/rails/blob/e2908356672d4459ada0064f773efd820efda822/railties/lib/rails/application.rb#L190 -task :environment do - # ensures that Mdm models are available for migrations which use the models - MetasploitDataModels.require_models - - # avoids the need for Rails.root in db:schema:dump - schema_pathname = Metasploit::Framework.root.join('db', 'schema.rb') - ENV['SCHEMA'] = schema_pathname.to_s -end - -# This would normally default RAILS_ENV to development if ENV['RAILS_ENV'] is -# not set -# -# @see https://github.com/rails/rails/blob/1a275730b290c1f06d4e8df680d22ae1b41ab585/railties/lib/rails/tasks/misc.rake#L3 -task :rails_env do -end diff --git a/script/rails b/script/rails new file mode 100644 index 0000000000..0839ba426e --- /dev/null +++ b/script/rails @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require 'rails/commands' \ No newline at end of file diff --git a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb index 45798cd30f..45b0d59ed7 100644 --- a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb +++ b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb @@ -8,12 +8,15 @@ require 'metasploit/framework' MetasploitDataModels.require_models describe ActiveRecord::ConnectionAdapters::ConnectionPool do + self.use_transactional_fixtures = false + def database_configurations YAML.load_file(database_configurations_pathname) end def database_configurations_pathname - Metasploit::Framework.root.join('config', 'database.yml') + # paths are always Array, but there should only be on 'config/database' entry + Rails.application.config.paths['config/database'].first end subject(:connection_pool) do @@ -24,7 +27,7 @@ describe ActiveRecord::ConnectionAdapters::ConnectionPool do # used, so have to manually establish connection. before(:each) do ActiveRecord::Base.configurations = database_configurations - spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] + spec = ActiveRecord::Base.configurations[Rails.env] ActiveRecord::Base.establish_connection(spec) end diff --git a/spec/lib/msf/core/modules/loader/base_spec.rb b/spec/lib/msf/core/modules/loader/base_spec.rb index 3f6e69063e..c348fd6e34 100644 --- a/spec/lib/msf/core/modules/loader/base_spec.rb +++ b/spec/lib/msf/core/modules/loader/base_spec.rb @@ -1177,10 +1177,22 @@ describe Msf::Modules::Loader::Base do end context 'with namespace_module nil' do + # + # lets + # + let(:namespace_module) do nil end + # + # Callbacks + # + + before(:each) do + parent_module.const_set(relative_name, Module.new) + end + it 'should remove relative_name' do parent_module.should_receive(:remove_const).with(relative_name) diff --git a/spec/lib/msf/db_manager/export_spec.rb b/spec/lib/msf/db_manager/export_spec.rb index 4f5de2e92f..d034343336 100644 --- a/spec/lib/msf/db_manager/export_spec.rb +++ b/spec/lib/msf/db_manager/export_spec.rb @@ -79,7 +79,7 @@ describe Msf::DBManager::Export do it 'should have Mdm::Module::Detail#disclosure_date from disclosure-date content' do node = module_detail_node.at_xpath('disclosure-date') - Date.parse(node.content).should == module_detail.disclosure_date + DateTime.parse(node.content).should == module_detail.disclosure_date end end diff --git a/spec/lib/msf/db_manager_spec.rb b/spec/lib/msf/db_manager_spec.rb index 76f215b8df..08707d90e0 100644 --- a/spec/lib/msf/db_manager_spec.rb +++ b/spec/lib/msf/db_manager_spec.rb @@ -21,16 +21,16 @@ describe Msf::DBManager do it_should_behave_like 'Msf::DBManager::Migration' it_should_behave_like 'Msf::DBManager::ImportMsfXml' - context '#initialize_metasploit_data_models' do - def initialize_metasploit_data_models - db_manager.initialize_metasploit_data_models + context '#add_rails_engine_migration_paths' do + def add_rails_engine_migration_paths + db_manager.add_rails_engine_migration_paths end it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do - initialize_metasploit_data_models + add_rails_engine_migration_paths expect { - initialize_metasploit_data_models + add_rails_engine_migration_paths }.to_not change { ActiveRecord::Migrator.migrations_paths.length } @@ -92,7 +92,7 @@ describe Msf::DBManager do it 'should create a connection' do # in purge_all_module_details # in after(:each) - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original + ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original purge_all_module_details end @@ -129,9 +129,7 @@ describe Msf::DBManager do end it 'should create connection' do - # 1st time from with_established_connection - # 2nd time from report_session - ActiveRecord::Base.connection_pool.should_receive(:with_connection).exactly(2).times + ActiveRecord::Base.connection_pool.should_receive(:with_connection) report_session end @@ -754,8 +752,7 @@ describe Msf::DBManager do it { should be_nil } it 'should not create a connection' do - # 1st time for with_established_connection - ActiveRecord::Base.connection_pool.should_receive(:with_connection).once + ActiveRecord::Base.connection_pool.should_not_receive(:with_connection) report_session end @@ -1274,7 +1271,7 @@ describe Msf::DBManager do end it 'should create a connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original + ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original update_all_module_details end @@ -1285,8 +1282,6 @@ describe Msf::DBManager do framework.should_receive(:cache_thread=).with(nil).ordered update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original end it 'should set modules_cached to false and then true around connection' do @@ -1295,8 +1290,6 @@ describe Msf::DBManager do db_manager.should_receive(:modules_cached=).with(true).ordered update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original end it 'should set modules_caching to true and then false around connection' do @@ -1305,8 +1298,6 @@ describe Msf::DBManager do db_manager.should_receive(:modules_caching=).with(false).ordered update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original end context 'with Mdm::Module::Details' do @@ -1483,7 +1474,6 @@ describe Msf::DBManager do it 'should create connection' do ActiveRecord::Base.connection_pool.should_receive(:with_connection) - ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original update_module_details end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 459103aba2..573c2ebae9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,49 +1,38 @@ -# -*- coding:binary -*- -require 'rubygems' -require 'bundler' -Bundler.require(:default, :test, :db) +# -*- coding: binary -*- +ENV['RAILS_ENV'] = 'test' -FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + "/file_fixtures/" - -# add project lib directory to load path -spec_pathname = Pathname.new(__FILE__).dirname -root_pathname = spec_pathname.join('..').expand_path -lib_pathname = root_pathname.join('lib') -$LOAD_PATH.unshift(lib_pathname.to_s) - -# must be first require and started before any other requires so that it can measure coverage of all following required -# code. It is after the rubygems and bundler only because Bundler.setup supplies the LOAD_PATH to simplecov. require 'simplecov' -# now that simplecov is loaded, load everything else +require File.expand_path('../../config/environment', __FILE__) + +# Don't `require 'rspec/rails'` as it includes support for pieces of rails that metasploit-framework doesn't use require 'rspec/core' +require 'rails/version' +require 'rspec/rails/adapters' +require 'rspec/rails/extensions' +require 'rspec/rails/fixture_support' +require 'rspec/rails/matchers' +require 'rspec/rails/mocks' + +FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + '/file_fixtures/' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. -support_glob = root_pathname.join('spec', 'support', '**', '*.rb') - -Dir.glob(support_glob) do |path| - require path +Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each do |f| + require f end RSpec.configure do |config| config.mock_with :rspec - # Can't use factory_girl_rails since not using rails, so emulate - # factory_girl.set_factory_paths initializer and after_initialize for - # FactoryGirl::Railtie - config.before(:suite) do - # Need to load Mdm models first so factories can use them - MetasploitDataModels.require_models + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = 'random' - FactoryGirl.definition_file_paths = [ - MetasploitDataModels.root.join('spec', 'factories'), - # Have metasploit-framework's definition file path last so it can - # modify gem factories. - Metasploit::Framework.root.join('spec', 'factories') - ] - - FactoryGirl.find_definitions - end + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true end - diff --git a/spec/support/shared/contexts/database_cleaner.rb b/spec/support/shared/contexts/database_cleaner.rb deleted file mode 100644 index 5bbb900e15..0000000000 --- a/spec/support/shared/contexts/database_cleaner.rb +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding:binary -*- -require 'metasploit/framework/database' - -shared_context 'DatabaseCleaner' do - def with_established_connection - begin - ActiveRecord::Base.connection_pool.with_connection do - yield - end - rescue ActiveRecord::ConnectionNotEstablished - # if there isn't a connection established, then established one and try - # again - ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations - spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] - ActiveRecord::Base.establish_connection(spec) - - retry - end - end - - # clean before all in case last test run was interrupted before - # after(:each) could clean up - before(:all) do - with_established_connection do - DatabaseCleaner.clean_with(:truncation) - end - end - - # Clean up after each test - after(:each) do - with_established_connection do - # Testing using both :truncation and :deletion; :truncation took long - # for testing. - DatabaseCleaner.clean_with(:deletion) - end - end -end diff --git a/spec/support/shared/contexts/msf/db_manager.rb b/spec/support/shared/contexts/msf/db_manager.rb index c060b5ceb3..7a164523a3 100644 --- a/spec/support/shared/contexts/msf/db_manager.rb +++ b/spec/support/shared/contexts/msf/db_manager.rb @@ -1,5 +1,4 @@ shared_context 'Msf::DBManager' do - include_context 'DatabaseCleaner' include_context 'Msf::Simple::Framework' let(:active) do @@ -11,13 +10,8 @@ shared_context 'Msf::DBManager' do end before(:each) do - configurations = Metasploit::Framework::Database.configurations - spec = configurations[Metasploit::Framework.env] - - # Need to connect or ActiveRecord::Base.connection_pool will raise an - # error. - db_manager.connect(spec) - + # already connected due to use_transactional_fixtures, but need some of the side-effects of #connect + framework.db.workspace = framework.db.default_workspace db_manager.stub(:active => active) end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb index 5e28d2f5e6..cccf0096e7 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb @@ -273,8 +273,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with :type' do - include_context 'DatabaseCleaner' - let(:source) do xml.tag!("web_#{type}") do web_site = web_vuln.web_site @@ -618,8 +616,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with required attributes' do - include_context 'DatabaseCleaner' - let(:element) do document.root end @@ -775,8 +771,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with required attributes' do - include_context 'DatabaseCleaner' - let(:element) do document.root end @@ -952,8 +946,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with required attributes' do - include_context 'DatabaseCleaner' - let(:element) do document.root end @@ -1030,8 +1022,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with web_forms/web_form elements' do - include_context 'DatabaseCleaner' - let(:data) do xml.tag!('MetasploitV4') do xml.web_forms do @@ -1071,8 +1061,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with web_pages/web_page elements' do - include_context 'DatabaseCleaner' - let(:data) do xml.tag!('MetasploitV4') do xml.web_pages do @@ -1124,8 +1112,6 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do end context 'with web_vulns/web_vuln elements' do - include_context 'DatabaseCleaner' - let(:data) do xml.tag!('MetasploitV4') do xml.web_vulns do diff --git a/spec/support/shared/examples/msf/db_manager/migration.rb b/spec/support/shared/examples/msf/db_manager/migration.rb index 1bdcbe44c1..a906b31251 100644 --- a/spec/support/shared/examples/msf/db_manager/migration.rb +++ b/spec/support/shared/examples/msf/db_manager/migration.rb @@ -7,7 +7,7 @@ shared_examples_for 'Msf::DBManager::Migration' do end it 'should create a connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice + ActiveRecord::Base.connection_pool.should_receive(:with_connection).once migrate end diff --git a/spec/support/shared/examples/msf/module_manager/cache.rb b/spec/support/shared/examples/msf/module_manager/cache.rb index 495ca3e2e6..7c69c60d9d 100644 --- a/spec/support/shared/examples/msf/module_manager/cache.rb +++ b/spec/support/shared/examples/msf/module_manager/cache.rb @@ -358,25 +358,12 @@ shared_examples_for 'Msf::ModuleManager::Cache' do end context 'with framework migrated' do - include_context 'DatabaseCleaner' - let(:framework_migrated?) do true end - before(:each) do - configurations = Metasploit::Framework::Database.configurations - spec = configurations[Metasploit::Framework.env] - - # Need to connect or ActiveRecord::Base.connection_pool will raise an - # error. - framework.db.connect(spec) - end - it 'should call ActiveRecord::Base.connection_pool.with_connection' do - # 1st is from with_established_connection - # 2nd is from module_info_by_path_from_database! - ActiveRecord::Base.connection_pool.should_receive(:with_connection).at_least(2).times + ActiveRecord::Base.connection_pool.should_receive(:with_connection) module_info_by_path_from_database! end @@ -408,7 +395,7 @@ shared_examples_for 'Msf::ModuleManager::Cache' do end it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do - Msf::Modules::Loader::Base.should_receive(:typed_path).with(type, reference_name).and_call_original + Msf::Modules::Loader::Base.should_receive(:typed_path).with(type, reference_name).at_least(:once).and_call_original module_info_by_path_from_database! end @@ -465,8 +452,6 @@ shared_examples_for 'Msf::ModuleManager::Cache' do false end - it { should_not query_the_database.when_calling(:module_info_by_path_from_database!) } - it 'should reset #module_info_by_path' do # pre-fill module_info_by_path so change can be detected module_manager.send(:module_info_by_path=, double('In-memory Cache'))