From 78ea30c1216c716e57f510f5d705aeaea018118b Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 23 Jun 2014 10:20:11 -0500 Subject: [PATCH 01/15] Use sequence for Mdm::Service#port for all factories MSP-10034 --- spec/factories/mdm/services.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/factories/mdm/services.rb b/spec/factories/mdm/services.rb index 5c7e2d4a..2ff63be1 100644 --- a/spec/factories/mdm/services.rb +++ b/spec/factories/mdm/services.rb @@ -9,14 +9,13 @@ # Attributes # name { generate :mdm_service_name } - port 4567 + port { generate :port } proto 'snmp' state 'open' factory :web_service do proto 'tcp' name { FactoryGirl.generate(:web_service_name) } - port { FactoryGirl.generate(:port) } end end From 75ebebd5b7635ad4a9b9384c321b917b709cf341 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 23 Jun 2014 10:20:52 -0500 Subject: [PATCH 02/15] Spec Mdm::Service#port search MSP-10034 --- .../search/visitor/relation_spec.rb | 238 +++++++++++++----- 1 file changed, 172 insertions(+), 66 deletions(-) diff --git a/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb index 7206d32a..8072423a 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb @@ -12,10 +12,14 @@ "name:\"#{value}\"" end + let(:klass) { + Mdm::Host + } + let(:query) do Metasploit::Model::Search::Query.new( :formatted => formatted, - :klass => Mdm::Host + :klass => klass ) end @@ -150,81 +154,183 @@ end context 'matching record' do - context 'with Mdm::Host' do - # - # lets - # - # Don't use factories to prevent prefix aliasing when sequences go from 1 to 10 or 10 to 100 - # - - let(:matching_record_name) { - 'mdm_host_name_a' - } - - let(:matching_service_name) { - 'mdm_service_name_a' - } - - let(:non_matching_record_name) { - 'mdm_host_name_b' - } - - let(:non_matching_service_name) { - 'mdm_service_name_b' - } - - # - # let!s - # - - let!(:matching_record) do - FactoryGirl.build( - :mdm_host, - name: matching_record_name - ) - end + context 'Metasploit::Model::Search::Query#klass' do + context 'with Mdm::Service' do + let(:klass) { + Mdm::Service + } - let!(:matching_service) do - FactoryGirl.create( - :mdm_service, - host: matching_record, - name: matching_service_name - ) - end + let(:matching_ports) { + [ + 1, + 2 + ] + } - let!(:non_matching_record) do - FactoryGirl.build( - :mdm_host, - name: non_matching_record_name - ) - end + let(:non_matching_port) { + 3 + } + + # + # let!s + # + + let!(:matching_record_by_port) { + matching_ports.each_with_object({}) { |matching_port, matching_record_by_port| + matching_record_by_port[matching_port] = FactoryGirl.create( + :mdm_service, + port: matching_port + ) + } + } + + let!(:non_matching_record) { + FactoryGirl.create(:mdm_service) + } + + context 'with port' do + context 'with single port number' do + let(:formatted) { + "port:#{matching_port}" + } + + let(:matching_port) { + matching_ports.sample + } + + let(:matching_record) { + matching_record_by_port[matching_port] + } + + it 'should find only record with that port number' do + expect(visit).to match_array([matching_record]) + end + end + + context 'with port range' do + let(:formatted) { + "port:#{matching_ports.min}-#{matching_ports.max}" + } + + it 'should find all records with port numbers within the range' do + expect(visit).to match_array(matching_records) + end + end + + context 'with comma separated port numbers' do + let(:formatted) { + "port:#{matching_ports.join(',')}" + } + + it 'should find all records with the port numbers' do + expect(visit).to match_array(matching_records) + end + end + + context 'with overlapping comma separated port number and range' do + let(:matching_port) { + matching_ports.sample + } + + let(:formatted) { + %Q{port:#{matching_port},#{matching_ports.min}-#{matching_ports.max}} + } + + it 'should find all records with the matching ports once' do + expect(visit).to match_array(matching_records) + end + end + end - let!(:non_matching_service) do - FactoryGirl.create( - :mdm_service, - host: non_matching_record, - name: non_matching_service_name - ) + context 'with all operators' do + let(:formatted) { + %Q{port:#{matching_port}} + } + end end - it_should_behave_like 'MetasploitDataModels::Search::Visitor::Relation#visit matching record', - :attribute => :name + context 'with Mdm::Host' do + # + # lets + # - it_should_behave_like 'MetasploitDataModels::Search::Visitor::Relation#visit matching record', - association: :services, - attribute: :name + let(:klass) { + Mdm::Host + } - context 'with all operators' do - let(:formatted) { - %Q{name:"#{matching_record_name}" services.name:"#{matching_service_name}"} + # + # Don't use factories to prevent prefix aliasing when sequences go from 1 to 10 or 10 to 100 + # + + let(:matching_record_name) { + 'mdm_host_name_a' } - it 'should find only matching record' do - if visit.to_a != [matching_record] - true - end + let(:matching_service_name) { + 'mdm_service_name_a' + } + + let(:non_matching_record_name) { + 'mdm_host_name_b' + } + + let(:non_matching_service_name) { + 'mdm_service_name_b' + } + + # + # let!s + # - expect(visit).to match_array([matching_record]) + let!(:matching_record) do + FactoryGirl.build( + :mdm_host, + name: matching_record_name + ) + end + + let!(:matching_service) do + FactoryGirl.create( + :mdm_service, + host: matching_record, + name: matching_service_name + ) + end + + let!(:non_matching_record) do + FactoryGirl.build( + :mdm_host, + name: non_matching_record_name + ) + end + + let!(:non_matching_service) do + FactoryGirl.create( + :mdm_service, + host: non_matching_record, + name: non_matching_service_name + ) + end + + it_should_behave_like 'MetasploitDataModels::Search::Visitor::Relation#visit matching record', + :attribute => :name + + it_should_behave_like 'MetasploitDataModels::Search::Visitor::Relation#visit matching record', + association: :services, + attribute: :name + + context 'with all operators' do + let(:formatted) { + %Q{name:"#{matching_record_name}" services.name:"#{matching_service_name}"} + } + + it 'should find only matching record' do + if visit.to_a != [matching_record] + true + end + + expect(visit).to match_array([matching_record]) + end end end end From 1f74a51187c6382c39218bbcaeda5244c3140ada Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 23 Jun 2014 15:23:43 -0500 Subject: [PATCH 03/15] MetasploitDataModels::Search::Operation::Port::Number MSP-10034 --- .../search/operation/port/number.rb | 25 +++++++++++ .../search/operation/port/number_spec.rb | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 app/models/metasploit_data_models/search/operation/port/number.rb create mode 100644 spec/app/models/metasploit_data_models/search/operation/port/number_spec.rb diff --git a/app/models/metasploit_data_models/search/operation/port/number.rb b/app/models/metasploit_data_models/search/operation/port/number.rb new file mode 100644 index 00000000..77037b8d --- /dev/null +++ b/app/models/metasploit_data_models/search/operation/port/number.rb @@ -0,0 +1,25 @@ +# Search operation on an attribute that holds a port number and is being searched with a single Integer port number. +class MetasploitDataModels::Search::Operation::Port::Number < Metasploit::Model::Search::Operation::Integer + # + # CONSTANTS + # + + # The number of bits in a port number + BITS = 16 + # The maximum port number + MAXIMUM = (1 << BITS) - 1 + # The minimum port number + MINIMUM = 0 + + # The range of valid port numbers from {MINIMUM} to {MAXIMUM}, inclusive. + RANGE = (MINIMUM..MAXIMUM) + + # + # Validations + # + + validates :value, + inclusion: { + in: RANGE + } +end \ No newline at end of file diff --git a/spec/app/models/metasploit_data_models/search/operation/port/number_spec.rb b/spec/app/models/metasploit_data_models/search/operation/port/number_spec.rb new file mode 100644 index 00000000..e2666f56 --- /dev/null +++ b/spec/app/models/metasploit_data_models/search/operation/port/number_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe MetasploitDataModels::Search::Operation::Port::Number do + context 'CONSTANTS' do + context 'BITS' do + subject(:bits) { + described_class::BITS + } + + it { should == 16 } + end + + context 'MAXIMUM' do + subject(:maxium) { + described_class::MAXIMUM + } + + it { should == 65535 } + end + + context 'MINIMUM' do + subject(:minimum) { + described_class::MINIMUM + } + + it { should == 0 } + end + + context 'RANGE' do + subject(:range) { + described_class::RANGE + } + + it { should == (0..65535) } + end + end + + context 'validations' do + it { should ensure_inclusion_of(:value).in_range(described_class::RANGE) } + end +end \ No newline at end of file From be65b3d0ba6797ad2dee62c24697e558a0663ef4 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Mon, 23 Jun 2014 16:36:52 -0500 Subject: [PATCH 04/15] MetasploitDataModels::Search::Operation::Range MSP-10034 Generate support for operations which should output a `Range` that AREL can turn into a SQL `BETWEEN` query. --- .../search/operation/range.rb | 56 +++++ .../search/operation/range_spec.rb | 235 ++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 app/models/metasploit_data_models/search/operation/range.rb create mode 100644 spec/app/models/metasploit_data_models/search/operation/range_spec.rb diff --git a/app/models/metasploit_data_models/search/operation/range.rb b/app/models/metasploit_data_models/search/operation/range.rb new file mode 100644 index 00000000..349242e2 --- /dev/null +++ b/app/models/metasploit_data_models/search/operation/range.rb @@ -0,0 +1,56 @@ +# Search operation on a `Range`. +class MetasploitDataModels::Search::Operation::Range < Metasploit::Model::Search::Operation::Base + # + # CONSTANTS + # + + # Separates beginning from end of the range. + SEPARATOR = '-' + + # + # Validation + # + + validate :ordered + validate :range + + # + # Instance Methods + # + + # Sets `#value` to a `Range` composed by separating `formatted_value` by `-`. + # + # @param formatted_value [#to_s] + # @return [Range] + def value=(formatted_value) + range_arguments = formatted_value.to_s.split(SEPARATOR, 2) + + begin + @value = Range.new(*range_arguments) + rescue ArgumentError + @value = formatted_value + end + + @value + end + + private + + # Validates that `#value` is a `Range` with `Range#begin` less than or equal to `Range#begin` + # + # @return [void] + def ordered + if value.is_a?(Range) && value.begin > value.end + errors.add(:value, :order, begin: value.begin.inspect, end: value.end.inspect) + end + end + + # Validates that `#value` is a `Range` + # + # @return [void] + def range + unless value.is_a? Range + errors.add(:value, :range) + end + end +end \ No newline at end of file diff --git a/spec/app/models/metasploit_data_models/search/operation/range_spec.rb b/spec/app/models/metasploit_data_models/search/operation/range_spec.rb new file mode 100644 index 00000000..799b6865 --- /dev/null +++ b/spec/app/models/metasploit_data_models/search/operation/range_spec.rb @@ -0,0 +1,235 @@ +require 'spec_helper' + +describe MetasploitDataModels::Search::Operation::Range do + subject(:range_operation) { + described_class.new(attributes) + } + + context 'CONSTANTS' do + context 'SEPARATOR' do + subject(:separator) { + described_class::SEPARATOR + } + + it { should == '-' } + end + end + + context 'validations' do + # + # lets + # + + let(:attributes) { + { + value: value + } + } + + # + # Callbacks + # + + before(:each) do + range_operation.valid? + end + + context 'errors on #value' do + subject(:errors) { + range_operation.errors[:value] + } + + context '#ordered' do + let(:error) { + I18n.translate!('metasploit.model.errors.models.metasploit_data_models/search/operation/range.attributes.value.order', error_attributes) + } + + context 'with Range' do + context 'with begin before end' do + let(:error_attributes) { + { + begin: '"1"', + end: '"2"' + } + } + + let(:value) { + '1-2' + } + + it { should_not include(error) } + end + + context 'with begin same as end' do + let(:error_attributes) { + { + begin: '"1"', + end: '"1"' + } + } + + let(:value) { + '1-1' + } + + it { should_not include(error) } + end + + context 'with begin after end' do + let(:error_attributes) { + { + begin: '"2"', + end: '"1"' + } + } + + let(:value) { + '2-1' + } + + it { should include error } + end + end + + context 'without Range' do + let(:error_attributes) { + { + begin: '"1"', + end: '"2"' + } + } + + let(:value) { + '1..2' + } + + it { should_not include(error) } + end + end + + context '#range' do + context 'with Range' do + let(:value) { + '1-2' + } + + it { should be_empty } + end + + context 'without Range' do + let(:error) { + I18n.translate!('metasploit.model.errors.models.metasploit_data_models/search/operation/range.attributes.value.range') + } + + let(:value) { + '1..2' + } + + it { should include error } + end + end + end + end + + context '#value' do + subject(:value) { + range_operation.value + } + + # + # lets + # + + let(:attributes) { + { + value: formatted_value + } + } + + context "without '-'" do + let(:formatted_value) { + 'a..b' + } + + it 'returns unconvertable value' do + expect(value).to eq(formatted_value) + end + end + + context "with one '-'" do + let(:formatted_begin) { + 'a' + } + + let(:formatted_end) { + 'b' + } + + let(:formatted_value) { + "#{formatted_begin}-#{formatted_end}" + } + + it 'returns Ranges' do + expect(value).to be_a Range + end + + context '#begin' do + subject(:range_begin) { + value.begin + } + + it "is part before '-'" do + expect(range_begin).to eq(formatted_begin) + end + end + + context '#end' do + subject(:range_end) { + value.end + } + + it "is part after '-'" do + expect(range_end).to eq(formatted_end) + end + end + end + + context "with multiple '-'" do + let(:formatted_begin) { + 'a' + } + + let(:formatted_end) { + 'b-c' + } + + let(:formatted_value) { + "#{formatted_begin}-#{formatted_end}" + } + + it 'returns Ranges' do + expect(value).to be_a Range + end + + context '#begin' do + subject(:range_begin) { + value.begin + } + + it "is part before first '-'" do + expect(range_begin).to eq(formatted_begin) + end + end + + context '#end' do + subject(:range_end) { + value.end + } + + it "is part after first '-'" do + expect(range_end).to eq(formatted_end) + end + end + end + end +end \ No newline at end of file From 3d44bc60834c01bf34d88ee8d1b0e53781ac92c8 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 08:58:45 -0500 Subject: [PATCH 05/15] Add translations file that was missed MSP-10034 --- config/locales/en.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 43fe2be5..c5880af2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -4,4 +4,11 @@ en: errors: messages: # have to duplicate activerecord.model.errors.message.taken because of the different i18n_scope - taken: "has already been taken" \ No newline at end of file + taken: "has already been taken" + + models: + metasploit_data_models/search/operation/range: + attributes: + value: + order: "is not in order: begin (%{begin} is greater than end (%{end})." + range: "is not a range" From e37f0deca07ab68d10c83987b0c2fd5bee57f6fc Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 09:54:51 -0500 Subject: [PATCH 06/15] MetasploitDataModels::Search::Operation::Port::Range MSP-10034 Extend MetasploitDataModels::Seach::Operation::Range to ensure that the Range is composed of Integers that are valid port numbers. --- .../search/operation/port/range.rb | 79 ++++++++++ config/locales/en.yml | 5 + .../search/operation/port/range_spec.rb | 140 ++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 app/models/metasploit_data_models/search/operation/port/range.rb create mode 100644 spec/app/models/metasploit_data_models/search/operation/port/range_spec.rb diff --git a/app/models/metasploit_data_models/search/operation/port/range.rb b/app/models/metasploit_data_models/search/operation/port/range.rb new file mode 100644 index 00000000..e9a28a8f --- /dev/null +++ b/app/models/metasploit_data_models/search/operation/port/range.rb @@ -0,0 +1,79 @@ +# Search operation on an attribute that holds a port number and is being search with a range of port numbers. The +# range is specified as `-` with `` being less than `` and both being within +# {MetasploitDataModels::Search::Operation::Port::Range the valid port range}. +class MetasploitDataModels::Search::Operation::Port::Range < MetasploitDataModels::Search::Operation::Range + # + # Validations + # + + validate :ports + + # + # Instance Methods + # + + # Sets `#value` to a range of ports. + # + # @param formatted_value [#to_s] '\d+-\d+' + def value=(formatted_value) + super(formatted_value) + + # could not be a `Range` if super conversion failed + # setters return the argument, not the return value from the method, so access `#value` directly + if value.is_a? Range + begin + # use Integer() instead of String#to_i as String#to_i will ignore trailing letters (i.e. '1two' -> 1) and turn all + # string without an integer in it to 0. + integer_begin = Integer(value.begin.to_s) + integer_end = Integer(value.end.to_s) + rescue ArgumentError + # setter returned is ignored in MRI, but do it anyway for other implementation + @value + else + @value = Range.new(integer_begin, integer_end) + end + else + # setter returned is ignored in MRI, but do it anyway for other implementation + # return unconvertible value from `super` + @value + end + end + + private + + # @note `#value` should be check to be a `Range` before calling {#port}. + # + # Validate that either `Range#begin` or `Range#end` is a valid port number in `#value` + # + # @param extreme [:begin, :end] Which extreme of the `Range` in `value` to validate. + # @return [void] + def port(extreme) + extreme_value = value.send(extreme) + + if extreme_value.is_a? Integer + unless MetasploitDataModels::Search::Operation::Port::Number::RANGE.cover?(extreme_value) + errors.add( + :value, + :port_range_extreme_inclusion, + extreme: extreme, + extreme_value: extreme_value, + maximum: MetasploitDataModels::Search::Operation::Port::Number::MAXIMUM, + minimum: MetasploitDataModels::Search::Operation::Port::Number::MINIMUM + ) + end + else + errors.add(:value, :port_range_extreme_not_an_integer, extreme: extreme, extreme_value: extreme_value) + end + end + + # Validates that the `Range#begin` and `Range#end` of `#value` are valid port numbers. + # + # @return [void] + def ports + if value.is_a? Range + [:begin, :end].each do |extreme| + port(extreme) + end + end + end +end \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index c5880af2..bb401e76 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -7,6 +7,11 @@ en: taken: "has already been taken" models: + metasploit_data_models/search/operation/port/range: + attributes: + value: + port_range_extreme_inclusion: "has extreme (%{extreme}) value (%{extreme_value}) outside range (%{minimum}-%{maximum})." + port_range_extreme_not_an_integer: "has extreme (%{extreme}) value (%{extreme_value}) that is not an integer." metasploit_data_models/search/operation/range: attributes: value: diff --git a/spec/app/models/metasploit_data_models/search/operation/port/range_spec.rb b/spec/app/models/metasploit_data_models/search/operation/port/range_spec.rb new file mode 100644 index 00000000..4c8e68ef --- /dev/null +++ b/spec/app/models/metasploit_data_models/search/operation/port/range_spec.rb @@ -0,0 +1,140 @@ +require 'spec_helper' + +describe MetasploitDataModels::Search::Operation::Port::Range do + subject(:port_range_operation) { + described_class.new( + value: formatted_value + ) + } + + let(:formatted_value) { + '1' + } + + it { should be_a MetasploitDataModels::Search::Operation::Range } + + context 'validations' do + before(:each) do + port_range_operation.valid? + end + + context 'errors on #value' do + subject(:value_errors) { + port_range_operation.errors[:value] + } + + context 'with Range' do + context 'with Integers' do + context 'covered by MetasploitDataModels::Search::Operation::Port::Number::RANGE' do + let(:formatted_value) { + '1-2' + } + + it { should be_empty } + end + + # this can't actually happen because the minimum is 0 and a negative number can't be parsed, but validation + # is there in case @value is set directly. + context 'without Range#begin covered by MetasploitDataModels::Search::Operation::Port::Number::RANGE' do + let(:error) { + I18n.translate!( + 'metasploit.model.errors.models.metasploit_data_models/search/operation/port/range.attributes.value.port_range_extreme_inclusion', + extreme: :begin, + extreme_value: range_begin, + maximum: MetasploitDataModels::Search::Operation::Port::Number::MAXIMUM, + minimum: MetasploitDataModels::Search::Operation::Port::Number::MINIMUM + ) + } + + let(:formatted_value) { + nil + } + + let(:port_range_operation) { + super().tap { |port_range_operation| + port_range_operation.instance_variable_set(:@value, Range.new(range_begin, range_end)) + } + } + + let(:range_begin) { + -1 + } + + let(:range_end) { + 1 + } + + it { should include error } + end + + context 'without Range#begin covered by MetasploitDataModels::Search::Operation::Port::Number::RANGE' do + let(:error) { + I18n.translate!( + 'metasploit.model.errors.models.metasploit_data_models/search/operation/port/range.attributes.value.port_range_extreme_inclusion', + extreme: :end, + extreme_value: range_end, + maximum: MetasploitDataModels::Search::Operation::Port::Number::MAXIMUM, + minimum: MetasploitDataModels::Search::Operation::Port::Number::MINIMUM + ) + } + + let(:formatted_value) { + "0-#{range_end}" + } + + let(:range_end) { + MetasploitDataModels::Search::Operation::Port::Number::MAXIMUM + 1 + } + + it { should include error } + end + end + + context 'without Integers' do + let(:begin_error) { + I18n.translate!( + 'metasploit.model.errors.models.metasploit_data_models/search/operation/port/range.attributes.value.port_range_extreme_not_an_integer', + extreme: :begin, + extreme_value: range_begin + ) + } + + let(:end_error) { + I18n.translate!( + 'metasploit.model.errors.models.metasploit_data_models/search/operation/port/range.attributes.value.port_range_extreme_not_an_integer', + extreme: :end, + extreme_value: range_end + ) + } + + let(:formatted_value) { + "#{range_begin}-#{range_end}" + } + + let(:range_begin) { + 'a' + } + + let(:range_end) { + 'b' + } + + it { should include begin_error } + it { should include end_error } + end + end + + context 'without Range' do + let(:error) { + I18n.translate!('metasploit.model.errors.models.metasploit_data_models/search/operation/range.attributes.value.range') + } + + let(:formatted_value) { + '1' + } + + it { should include(error) } + end + end + end +end \ No newline at end of file From 37cbc59da3578898ea4de3ab0a281fca56128d08 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 13:47:54 -0500 Subject: [PATCH 07/15] MetasploitDataModels::Search::Operator::Port::List MSP-10034 The port list operator is able to take a comma separated list of port numbers and dash separated port number ranges and turn them into equality searchs and between searches. --- .../search/operator/port/list.rb | 67 +++++++ .../search/operator/port/list_spec.rb | 164 ++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 app/models/metasploit_data_models/search/operator/port/list.rb create mode 100644 spec/app/models/metasploit_data_models/search/operator/port/list_spec.rb diff --git a/app/models/metasploit_data_models/search/operator/port/list.rb b/app/models/metasploit_data_models/search/operator/port/list.rb new file mode 100644 index 00000000..56459e47 --- /dev/null +++ b/app/models/metasploit_data_models/search/operator/port/list.rb @@ -0,0 +1,67 @@ +# Searches for a network port attribute. Ports can be given as a single number or range of numbers and either or both +# forms can be combined into a comma separated list. Individual port numbers are validated to be greater than 0 and +class MetasploitDataModels::Search::Operator::Port::List < Metasploit::Model::Search::Operator::Union + # + # CONSTANTS + # + + # Separates port number and/or port ranges + SEPARATOR = ',' + + # + # Attributes + # + + # @!attribute [rw] attribute + # Attribute holding port number. + # + # @return [Symbol] `:port` + attr_writer :attribute + + # + # Class Methods + # + + # @note Can't be called `name` because it would alias `Class#name` + # + # Name of this operator. + # + # @return [String] `'port_list'` + def self.operator_name + 'port_list' + end + + # + # Instance Methods + # + + # Defaults to `:port`. + # + # @return [Symbol] + def attribute + @attribute ||= :port + end + + # Turns `{#attribute}:,` into the union of port and port searches. + # + # @param formatted_value [String] comma separated list of port numbers and ranges. + # @return [Array Date: Tue, 24 Jun 2014 10:08:36 -0500 Subject: [PATCH 08/15] Visit MetasploitDataModels::Search::Operator::Port::List for attributes MSP-10034 --- .../search/visitor/attribute.rb | 3 +- .../search/visitor/attribute_spec.rb | 74 ++++++++++++------- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/app/models/metasploit_data_models/search/visitor/attribute.rb b/app/models/metasploit_data_models/search/visitor/attribute.rb index 73d02a75..f50e7716 100644 --- a/app/models/metasploit_data_models/search/visitor/attribute.rb +++ b/app/models/metasploit_data_models/search/visitor/attribute.rb @@ -6,7 +6,8 @@ class MetasploitDataModels::Search::Visitor::Attribute visit operator.attribute_operator end - visit 'Metasploit::Model::Search::Operator::Attribute' do |operator| + visit 'Metasploit::Model::Search::Operator::Attribute', + 'MetasploitDataModels::Search::Operator::Port::List' do |operator| table = operator.klass.arel_table table[operator.attribute] end diff --git a/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb index 69dcd409..f6ef5a58 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb @@ -10,6 +10,40 @@ visitor.visit(node) end + # + # Shared examples + # + + shared_examples_for 'operator.klass.arel_table[operator.attribute]' do |options={}| + options.assert_valid_keys(:node_class) + + node_class = options.fetch(:node_class) + + context "with #{node_class}" do + it { should be_a Arel::Attributes::Attribute } + + context '#name' do + subject(:name) do + visit.name + end + + it "should be #{node_class}#attribute" do + name.should == node.attribute + end + end + + context '#relation' do + subject(:relation) do + visit.relation + end + + it "should be Class#arel_table for #{node_class}#klass" do + relation.should == node.klass.arel_table + end + end + end + end + context 'with Metasploit::Model::Search::Operator::Association' do let(:attribute_operator) do double('Attribute Operator') @@ -38,37 +72,25 @@ end end - context 'with Metasploit::Model::Search::Operator::Attribute' do - let(:node) do + it_should_behave_like 'operator.klass.arel_table[operator.attribute]', + node_class: Metasploit::Model::Search::Operator::Attribute do + let(:node) { Metasploit::Model::Search::Operator::Attribute.new( # needs to be a real column so look up on AREL table works - :attribute => :name, + attribute: :name, # needs to be a real class so Class#arel_table works - :klass => Mdm::Host + klass: Mdm::Host ) - end - - it { should be_a Arel::Attributes::Attribute } - - context 'name' do - subject(:name) do - visit.name - end - - it 'should be Metasploit::Model::Search::Operator::Attribute#attribute' do - name.should == node.attribute - end - end - - context 'relation' do - subject(:relation) do - visit.relation - end + } + end - it 'should be Class#arel_table for Metasploit::Model::Search::Operator::Attribute#klass' do - relation.should == node.klass.arel_table - end - end + it_should_behave_like 'operator.klass.arel_table[operator.attribute]', + node_class: MetasploitDataModels::Search::Operator::Port::List do + let(:node) { + Metasploit::Model::Search::Operator::Attribute.new( + klass: Mdm::Service + ) + } end end end \ No newline at end of file From 6882eebc5b088c1728235ab69c78975c0747f76f Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 10:15:32 -0500 Subject: [PATCH 09/15] No includes for MetasploitDataModels::Search::Operator::Port::List MSP-10034 --- .../metasploit_data_models/search/visitor/includes.rb | 3 ++- .../search/visitor/includes_spec.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/metasploit_data_models/search/visitor/includes.rb b/app/models/metasploit_data_models/search/visitor/includes.rb index 47b39e6f..89c0214a 100644 --- a/app/models/metasploit_data_models/search/visitor/includes.rb +++ b/app/models/metasploit_data_models/search/visitor/includes.rb @@ -22,7 +22,8 @@ class MetasploitDataModels::Search::Visitor::Includes [operator.association] end - visit 'Metasploit::Model::Search::Operator::Attribute' do |_operator| + visit 'Metasploit::Model::Search::Operator::Attribute', + 'MetasploitDataModels::Search::Operator::Port::List' do |_operator| [] end end diff --git a/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb index 49912830..5513af7a 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb @@ -76,6 +76,14 @@ it { should == [] } end + context 'with MetasploitDataModels::Search::Operator::Port::List' do + let(:node) do + MetasploitDataModels::Search::Operator::Port::List.new + end + + it { should == [] } + end + context 'with Metasploit::Model::Search::Query#tree' do let(:node) do query.tree From 3c19c78d85a4b7e4e7c426c99327c24ddabe9fe9 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 10:25:53 -0500 Subject: [PATCH 10/15] No joins for MetasploitDataModels::Search::Operator::Port::List MSP-10034 --- app/models/metasploit_data_models/search/visitor/joins.rb | 3 ++- .../metasploit_data_models/search/visitor/joins_spec.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/metasploit_data_models/search/visitor/joins.rb b/app/models/metasploit_data_models/search/visitor/joins.rb index 59bef6b4..cc80e27f 100644 --- a/app/models/metasploit_data_models/search/visitor/joins.rb +++ b/app/models/metasploit_data_models/search/visitor/joins.rb @@ -41,7 +41,8 @@ class MetasploitDataModels::Search::Visitor::Joins [operator.association] end - visit 'Metasploit::Model::Search::Operator::Attribute' do |_| + visit 'Metasploit::Model::Search::Operator::Attribute', + 'MetasploitDataModels::Search::Operator::Port::List' do |_| [] end end \ No newline at end of file diff --git a/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb index eb0cf6fc..b0aa3ece 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb @@ -243,6 +243,14 @@ it { should == [] } end + context "with MetasploitDataModels::Search::Operator::Port::List" do + let(:node) do + MetasploitDataModels::Search::Operator::Port::List.new + end + + it { should == [] } + end + context 'with Metasploit::Model::Search::Query#tree' do let(:node) do query.tree From 69b74370ab7274ec990515f618f226476d8929c2 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 11:22:42 -0500 Subject: [PATCH 11/15] Turn MetaspoitDataModels::Seach::Operation::Port::Range into BETWEEN SQL MSP-10034 --- .../search/visitor/where.rb | 8 +++- .../search/visitor/where_spec.rb | 38 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/models/metasploit_data_models/search/visitor/where.rb b/app/models/metasploit_data_models/search/visitor/where.rb index d964196f..c85b52cf 100644 --- a/app/models/metasploit_data_models/search/visitor/where.rb +++ b/app/models/metasploit_data_models/search/visitor/where.rb @@ -43,7 +43,13 @@ class MetasploitDataModels::Search::Visitor::Where match_value = "%#{operation.value}%" attribute.matches(match_value) - end + end + + visit 'MetasploitDataModels::Search::Operation::Port::Range' do |range_operation| + attribute = attribute_visitor.visit range_operation.operator + + attribute.in(range_operation.value) + end # # Methods diff --git a/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb index b5195b14..af807c57 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb @@ -95,6 +95,44 @@ end end + context 'with MetasploitDataModels::Search::Operation::Port::Range' do + let(:node) { + MetasploitDataModels::Search::Operation::Port::Range.new( + operator: operator, + value: value + ) + } + + let(:operator) { + MetasploitDataModels::Search::Operator::Port::List.new( + klass: Mdm::Service + ) + } + + let(:range) { + 1..2 + } + + let(:value) { + "#{range.begin}-#{range.end}" + } + + it 'should visit operation.operator with attribute_visitor' do + expect(visitor.attribute_visitor).to receive(:visit).with(operator).and_call_original + + visit + end + + it 'should call in on Arel::Attributes::Attribute from attribute_visitor' do + attribute = double('Visited Operator') + allow(visitor.attribute_visitor).to receive(:visit).with(operator).and_return(attribute) + + expect(attribute).to receive(:in).with(range) + + visit + end + end + context 'with Metasploit::Model::Search::Query#tree' do let(:node) do query.tree From b03f97fd037ab1dc27d7e9dfdff1efd808f9d4e6 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 13:59:02 -0500 Subject: [PATCH 12/15] Fix MetasploitDataModels::Search::Visitor::Relation spec MSP-10034 Fix spec for Mdm::Service#port. --- app/models/mdm/service.rb | 13 +++++++++++++ .../search/visitor/relation_spec.rb | 9 ++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/models/mdm/service.rb b/app/models/mdm/service.rb index 67193da7..7fad45f8 100755 --- a/app/models/mdm/service.rb +++ b/app/models/mdm/service.rb @@ -176,6 +176,12 @@ class Mdm::Service < ActiveRecord::Base ]) } + # + # + # Search + # + # + # # Search Attributes # @@ -183,6 +189,13 @@ class Mdm::Service < ActiveRecord::Base search_attribute :name, type: :string + + # + # Search Withs + # + + search_with MetasploitDataModels::Search::Operator::Port::List + # # Validations # diff --git a/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb index 8072423a..5da2463f 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb @@ -167,6 +167,10 @@ ] } + let(:matching_records) { + matching_record_by_port.values + } + let(:non_matching_port) { 3 } @@ -185,7 +189,10 @@ } let!(:non_matching_record) { - FactoryGirl.create(:mdm_service) + FactoryGirl.create( + :mdm_service, + port: non_matching_port + ) } context 'with port' do From 220efcfab3fe04b03ad9c462eacdade5b36bd2dc Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 15:05:37 -0500 Subject: [PATCH 13/15] Fix spacing issues MSP-10034 Fix spacing issues highlighted by Pull Review. --- app/models/mdm/service.rb | 2 -- .../metasploit_data_models/search/visitor/attribute_spec.rb | 2 +- .../models/metasploit_data_models/search/visitor/joins_spec.rb | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/models/mdm/service.rb b/app/models/mdm/service.rb index 7fad45f8..1131941c 100755 --- a/app/models/mdm/service.rb +++ b/app/models/mdm/service.rb @@ -189,7 +189,6 @@ class Mdm::Service < ActiveRecord::Base search_attribute :name, type: :string - # # Search Withs # @@ -217,4 +216,3 @@ def normalize_host_os ActiveSupport.run_load_hooks(:mdm_service, self) end - diff --git a/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb index f6ef5a58..fb16283e 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb @@ -14,7 +14,7 @@ # Shared examples # - shared_examples_for 'operator.klass.arel_table[operator.attribute]' do |options={}| + shared_examples_for 'operator.klass.arel_table[operator.attribute]' do |options = {}| options.assert_valid_keys(:node_class) node_class = options.fetch(:node_class) diff --git a/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb index b0aa3ece..7446d7e8 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb @@ -269,7 +269,6 @@ Mdm::Host } - context 'with name' do let(:name) do FactoryGirl.generate :mdm_host_name From 2d374e12ac3884afda0e5e902ce5d85ba0910891 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 24 Jun 2014 15:26:56 -0500 Subject: [PATCH 14/15] Fix quoting issues MSP-10034 Convert " to ' where flagged by Pull Review. --- .../metasploit_data_models/search/visitor/joins_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb b/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb index 7446d7e8..582cbfc7 100644 --- a/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb +++ b/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb @@ -235,7 +235,7 @@ end end - context "with Metasploit::Model::Search::Operator::Attribute" do + context 'with Metasploit::Model::Search::Operator::Attribute' do let(:node) do Metasploit::Model::Search::Operator::Attribute.new end @@ -243,7 +243,7 @@ it { should == [] } end - context "with MetasploitDataModels::Search::Operator::Port::List" do + context 'with MetasploitDataModels::Search::Operator::Port::List' do let(:node) do MetasploitDataModels::Search::Operator::Port::List.new end From 55bc810dcf988d1f9aeb00817b8ce0e0708a7e76 Mon Sep 17 00:00:00 2001 From: lsanchez-r7 Date: Fri, 27 Jun 2014 10:23:09 -0500 Subject: [PATCH 15/15] Incrementing the patch number --- lib/metasploit_data_models/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metasploit_data_models/version.rb b/lib/metasploit_data_models/version.rb index 12b306b6..ba8ed81c 100755 --- a/lib/metasploit_data_models/version.rb +++ b/lib/metasploit_data_models/version.rb @@ -4,5 +4,5 @@ module MetasploitDataModels # metasploit-framework/data/sql/migrate to db/migrate in this project, not all models have specs that verify the # migrations (with have_db_column and have_db_index) and certain models may not be shared between metasploit-framework # and pro, so models may be removed in the future. Because of the unstable API the version should remain below 1.0.0 - VERSION = '0.17.4' + VERSION = '0.17.5' end