Skip to content

Commit 89bd5b4

Browse files
committed
Reset column information after running migrations
[#50179803] [SeeRM rapid7#7967] [SeeRM rapid7#7870] Because metasploit-framework runs migrations with the same process and with the same connection as it later accesses the database, the column information can become cached prematurely and be incorrect by the end of the migrations. Fix the bad cache by automatically resetting the column information for all model classes after the migrations have run.
1 parent 398dcfa commit 89bd5b4

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

lib/msf/core/db_manager/migration.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,33 @@ def migrate(verbose=false)
2626
end
2727
end
2828

29+
# Since the connections that existed before the migrations ran could
30+
# have outdated column information, reset column information for all
31+
# ActiveRecord::Base descendents to prevent missing method errors for
32+
# column methods for columns created in migrations after the column
33+
# information was cached.
34+
reset_column_information
35+
2936
return ran
3037
end
3138

3239
# Flag to indicate database migration has completed
3340
#
3441
# @return [Boolean]
3542
attr_accessor :migrated
43+
44+
private
45+
46+
# Resets the column information for all descendants of ActiveRecord::Base
47+
# since some of the migrations may have cached column information that
48+
# has been updated by later migrations.
49+
#
50+
# @return [void]
51+
def reset_column_information
52+
ActiveRecord::Base.descendants.each do |descendant|
53+
descendant.reset_column_information
54+
end
55+
end
3656
end
3757
end
3858
end

spec/support/shared/examples/msf/db_manager/migration.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ def migrate
2727
migrate.should == migrations
2828
end
2929

30+
it 'should reset the column information' do
31+
db_manager.should_receive(:reset_column_information)
32+
33+
migrate
34+
end
35+
3036
context 'with StandardError from ActiveRecord::Migration.migrate' do
3137
let(:error) do
3238
StandardError.new(message)
@@ -106,4 +112,32 @@ def migrate
106112
it { should respond_to :migrated }
107113
it { should respond_to :migrated= }
108114
end
115+
116+
context '#reset_column_information' do
117+
def reset_column_information
118+
db_manager.send(:reset_column_information)
119+
end
120+
121+
it 'should use ActiveRecord::Base.descendants to find both direct and indirect subclasses' do
122+
ActiveRecord::Base.should_receive(:descendants).and_return([])
123+
124+
reset_column_information
125+
end
126+
127+
it 'should reset column information on each descendant of ActiveRecord::Base' do
128+
descendants = []
129+
130+
1.upto(2) do |i|
131+
descendants << mock("Descendant #{i}")
132+
end
133+
134+
ActiveRecord::Base.stub(:descendants => descendants)
135+
136+
descendants.each do |descendant|
137+
descendant.should_receive(:reset_column_information)
138+
end
139+
140+
reset_column_information
141+
end
142+
end
109143
end

0 commit comments

Comments
 (0)