diff --git a/Gemfile.lock b/Gemfile.lock index 700863cc49..fe6e7ce938 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,6 +35,7 @@ PATH redcarpet rex-arch rex-java + rex-mime rex-ole rex-powershell rex-random_identifier @@ -180,7 +181,7 @@ GEM multi_json (1.12.1) multi_test (0.1.2) multipart-post (2.0.0) - nessus_rest (0.1.3) + nessus_rest (0.1.4) net-ssh (3.2.0) network_interface (0.0.1) nokogiri (1.6.8) @@ -224,15 +225,17 @@ GEM thor (>= 0.18.1, < 2.0) rake (11.2.2) rb-readline-r7 (0.5.2.0) - recog (2.0.21) + recog (2.0.22) nokogiri redcarpet (3.3.4) rex-arch (0.1.1) rex-text rex-java (0.1.2) + rex-mime (0.1.1) + rex-text rex-ole (0.1.2) rex-text - rex-powershell (0.1.1) + rex-powershell (0.1.64) rex-random_identifier rex-text rex-random_identifier (0.1.0) diff --git a/lib/rex.rb b/lib/rex.rb index f317e147cd..00b02c004a 100644 --- a/lib/rex.rb +++ b/lib/rex.rb @@ -57,6 +57,8 @@ require 'rex/java' require 'rex/struct2' # Library for working with OLE require 'rex/ole' +# Library for creating and/or parsing MIME messages +require 'rex/mime' # Generic classes require 'rex/exceptions' diff --git a/lib/rex/mime.rb b/lib/rex/mime.rb deleted file mode 100644 index 840b4ab2bc..0000000000 --- a/lib/rex/mime.rb +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: binary -*- -module Rex -module MIME - -require 'rex/mime/header' -require 'rex/mime/part' -require 'rex/mime/message' - -end -end diff --git a/lib/rex/mime/encoding.rb b/lib/rex/mime/encoding.rb deleted file mode 100644 index d9805c00e1..0000000000 --- a/lib/rex/mime/encoding.rb +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: binary -*- -module Rex -module MIME -# Set of helpers methods to deal with SMTP encoding related topics. -module Encoding - - # Enforces CRLF on the input data - # - # @param data [String] The data to CRLF enforce. - # @return [String] CRLF enforced data. - def force_crlf(data) - data.gsub("\r", '').gsub("\n", "\r\n") - end - -end -end -end diff --git a/lib/rex/mime/header.rb b/lib/rex/mime/header.rb deleted file mode 100644 index a1830f838b..0000000000 --- a/lib/rex/mime/header.rb +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: binary -*- -module Rex -module MIME -class Header - - require 'rex/text' - - attr_accessor :headers - - def initialize(data='') - self.headers = [] - parse(data) - end - - def parse(data) - prev = nil - data.gsub("\r", '').split("\n").each do |line| - - # Handle header folding - if (line =~ /^\s+/) - # Ignore if there is no previous header - next if not prev - next if not self.headers[prev] - self.headers[prev][1] << line.strip - next - end - - var, val = line.split(':', 2) - next if val.nil? - - self.headers << [ var.to_s.strip, val.to_s.strip ] - prev = self.headers.length - 1 - end - end - - def to_s - self.headers.map{ |pair| "#{pair[0]}: #{pair[1]}\r\n" }.join - end - - def find(idx) - if (idx.class == ::Fixnum) - return self.headers[idx] - else - self.headers.each do |pair| - if (pair[0] == idx.to_s) - return pair - end - end - end - nil - end - - def set(var, val) - hdr = self.find(var) || self.add(var, '') - hdr[1] = val - end - - def add(var, val) - self.headers << [var, val] - self.headers[-1] - end - - def remove(idx) - if (idx.class == ::Fixnum) - self.headers.delete_at(idx) - else - self.headers.each_index do |i| - pair = self.headers[i] - if (pair[0] == idx.to_s) - self.headers.delete_at(i) - end - end - end - end - -end -end -end - diff --git a/lib/rex/mime/message.rb b/lib/rex/mime/message.rb deleted file mode 100644 index 205f561350..0000000000 --- a/lib/rex/mime/message.rb +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: binary -*- -module Rex -module MIME -class Message - - require 'rex/mime/header' - require 'rex/mime/part' - require 'rex/mime/encoding' - require 'rex/text' - - include Rex::MIME::Encoding - - attr_accessor :header, :parts, :bound, :content - - - def initialize(data=nil) - self.header = Rex::MIME::Header.new - self.parts = [] - self.bound = "_Part_#{rand(1024)}_#{rand(0xffffffff)}_#{rand(0xffffffff)}" - self.content = '' - if data - head,body = data.split(/\r?\n\r?\n/, 2) - - self.header.parse(head) - ctype = self.header.find('Content-Type') - - if ctype && ctype[1] && ctype[1] =~ /multipart\/mixed;\s*boundary="?([A-Za-z0-9'\(\)\+\_,\-\.\/:=\?^\s]+)"?/ - self.bound = $1 - chunks = body.to_s.split(/--#{self.bound}(--)?\r?\n/) - self.content = chunks.shift.to_s.gsub(/\s+$/, '') - self.content << "\r\n" unless self.content.empty? - - chunks.each do |chunk| - break if chunk == "--" - head,body = chunk.split(/\r?\n\r?\n/, 2) - part = Rex::MIME::Part.new - part.header.parse(head) - part.content = body.gsub(/\s+$/, '') - self.parts << part - end - else - self.content = body.to_s.gsub(/\s+$/, '') + "\r\n" - end - end - end - - def to - (self.header.find('To') || [nil, nil])[1] - end - - def to=(val) - self.header.set("To", val) - end - - def from=(val) - self.header.set("From", val) - end - - def from - (self.header.find('From') || [nil, nil])[1] - end - - def subject=(val) - self.header.set("Subject", val) - end - - def subject - (self.header.find('Subject') || [nil, nil])[1] - end - - def mime_defaults - self.header.set("MIME-Version", "1.0") - self.header.set("Content-Type", "multipart/mixed; boundary=\"#{self.bound}\"") - self.header.set("Subject", '') # placeholder - self.header.set("Date", Time.now.strftime("%a,%e %b %Y %H:%M:%S %z")) - self.header.set("Message-ID", - "<"+ - Rex::Text.rand_text_alphanumeric(rand(20)+40)+ - "@"+ - Rex::Text.rand_text_alpha(rand(20)+3)+ - ">" - ) - self.header.set("From", '') # placeholder - self.header.set("To", '') # placeholder - end - - - def add_part(data='', content_type='text/plain', transfer_encoding="8bit", content_disposition=nil) - part = Rex::MIME::Part.new - - if content_disposition - part.header.set("Content-Disposition", content_disposition) - end - - part.header.set("Content-Type", content_type) if content_type - - if transfer_encoding - part.header.set("Content-Transfer-Encoding", transfer_encoding) - end - - part.content = data - self.parts << part - part - end - - def add_part_attachment(data, name) - self.add_part( - Rex::Text.encode_base64(data, "\r\n"), - "application/octet-stream; name=\"#{name}\"", - "base64", - "attachment; filename=\"#{name}\"" - ) - end - - - def add_part_inline_attachment(data, name) - self.add_part( - Rex::Text.encode_base64(data, "\r\n"), - "application/octet-stream; name=\"#{name}\"", - "base64", - "inline; filename=\"#{name}\"" - ) - end - - def to_s - header_string = self.header.to_s - - msg = header_string.empty? ? '' : force_crlf(self.header.to_s + "\r\n") - msg << force_crlf(self.content + "\r\n") unless self.content.to_s.strip.empty? - - self.parts.each do |part| - msg << force_crlf("--" + self.bound + "\r\n") - msg << part.to_s - end - - msg << force_crlf("--" + self.bound + "--\r\n") if self.parts.length > 0 - - msg - end - -end -end -end - diff --git a/lib/rex/mime/part.rb b/lib/rex/mime/part.rb deleted file mode 100644 index ef80bb5ec5..0000000000 --- a/lib/rex/mime/part.rb +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: binary -*- -module Rex -module MIME -class Part - - require 'rex/mime/header' - require 'rex/mime/encoding' - - include Rex::MIME::Encoding - - attr_accessor :header, :content - - def initialize - self.header = Rex::MIME::Header.new - self.content = '' - end - - def to_s - self.header.to_s + "\r\n" + content_encoded + "\r\n" - end - - # Returns the part content with any necessary encoding or transformation - # applied. - # - # @return [String] Content with encoding or transformations applied. - def content_encoded - binary_content? ? content : force_crlf(content) - end - - # Answers if the part content is binary. - # - # @return [Boolean] true if the part content is binary, false otherwise. - def binary_content? - transfer_encoding && transfer_encoding == 'binary' - end - - # Returns the Content-Transfer-Encoding of the part. - # - # @return [nil] if the part hasn't Content-Transfer-Encoding. - # @return [String] The Content-Transfer-Encoding or the part. - def transfer_encoding - h = header.find('Content-Transfer-Encoding') - return nil if h.nil? - - h[1] - end - -end -end -end diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index da73f8c982..1b18e4fd59 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -132,6 +132,8 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'rex-arch' # Library for working with OLE. spec.add_runtime_dependency 'rex-ole' + # Library for creating and/or parsing MIME messages. + spec.add_runtime_dependency 'rex-mime' # rb-readline doesn't work with Ruby Installer due to error with Fiddle: # NoMethodError undefined method `dlopen' for Fiddle:Module diff --git a/spec/lib/rex/mime/encoding_spec.rb b/spec/lib/rex/mime/encoding_spec.rb deleted file mode 100644 index 2f45d9179d..0000000000 --- a/spec/lib/rex/mime/encoding_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'rex/mime' - -RSpec.describe Rex::MIME::Encoding do - - subject do - mod = Class.new - mod.extend described_class - mod - end - - describe "#force_crlf" do - it "deletes \\r characters" do - expect(subject.force_crlf("Test\r1\r")).to_not include("\\r") - end - - it "substitutes \\n characters by \\r\\n sequences" do - expect(subject.force_crlf("Test 2\n")).to end_with("\r\n") - end - - it "preserves \r\n sequences" do - expect(subject.force_crlf("\r\nTest 3\r\n")).to eq("\r\nTest 3\r\n") - end - - it "first deletes \\r characters, then substitutes \\n characters" do - expect(subject.force_crlf("\rTest 4\r\n\r\r\n")).to eq("Test 4\r\n\r\n") - end - end - -end diff --git a/spec/lib/rex/mime/header_spec.rb b/spec/lib/rex/mime/header_spec.rb deleted file mode 100644 index 38fa11218d..0000000000 --- a/spec/lib/rex/mime/header_spec.rb +++ /dev/null @@ -1,151 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'rex/mime' - -RSpec.describe Rex::MIME::Header do - - let(:mime_headers_test) do - <<-EOS -Content-Type: text/plain; -Content-Disposition: attachment; filename="test.txt" - EOS - end - - subject do - described_class.new - end - - describe "#initialize" do - subject(:header_class) do - described_class.allocate - end - - it "returns an Array" do - expect(header_class.send(:initialize)).to be_a(Array) - end - - it "creates an empty headers array by default" do - expect(header_class.send(:initialize)).to be_empty - end - - it "populates headers array with data from argument" do - header_class.send(:initialize, mime_headers_test) - expect(header_class.headers.length).to be(2) - end - end - - describe "#add" do - it "returns the added entry" do - expect(subject.add('var', 'val')).to eq(['var', 'val']) - end - - it "adds a new entry into the headers array" do - subject.add('var', 'val') - expect(subject.headers.length).to eq(1) - end - end - - describe "#set" do - it "returns the set value" do - expect(subject.set('var', 'val')).to eq('val') - end - - it "modifies the header entry if it exists" do - subject.add('var', 'val') - subject.set('var', 'val2') - expect(subject.headers.length).to eq(1) - expect(subject.headers[0]).to eq(['var', 'val2']) - end - - it "creates the header entry if doesn't exist" do - subject.set('var2', 'val2') - expect(subject.headers.length).to eq(1) - expect(subject.headers[0]).to eq(['var2', 'val2']) - end - end - - describe "#remove" do - it "doesn't remove any header if index doesn't exist" do - subject.add('var', 'val') - subject.remove(10000) - expect(subject.headers.length).to eq(1) - end - - it "doesn't remove any header if var name doesn't exist" do - subject.add('var', 'val') - subject.remove('var2') - expect(subject.headers.length).to eq(1) - end - - it "removes header entry if index exists" do - subject.add('var', 'val') - subject.remove(0) - expect(subject.headers.length).to eq(0) - end - - it "removes any header entry with var name" do - subject.add('var', 'val') - subject.add('var2', 'val2') - subject.add('var', 'val3') - subject.remove('var') - expect(subject.headers.length).to eq(1) - end - end - - describe "#find" do - it "returns nil if header index doesn't exist" do - expect(subject.find(1)).to be_nil - end - - it "returns nil if header var name doesn't exist" do - expect(subject.find('var')).to be_nil - end - - it "returns the header at index if exists" do - subject.add('var', 'val') - expect(subject.find(0)).to eq(['var', 'val']) - end - - it "returns the first header with var name if exists" do - subject.add('var', 'val') - subject.add('var', 'val2') - subject.add('var', 'val3') - expect(subject.find('var')).to eq(['var', 'val']) - end - end - - describe "#to_s" do - it "returns empty String if there aren't headers" do - expect(subject.to_s).to be_empty - end - - it "returns string with headers separated by \\r\\n sequences" do - subject.add('var', 'val') - subject.add('var', 'val2') - subject.add('var3', 'val3') - expect(subject.to_s).to eq("var: val\r\nvar: val2\r\nvar3: val3\r\n") - end - end - - describe "#parse" do - let(:complex_header) do - 'Date: Wed,20 Aug 2014 08:45:38 -0500' - end - - it "parses headers separated by lines" do - subject.parse(mime_headers_test) - expect(subject.headers.length).to eq(2) - end - - it "parses headers names and values separated by :" do - subject.parse(mime_headers_test) - expect(subject.headers).to eq([['Content-Type', 'text/plain;'], ['Content-Disposition', 'attachment; filename="test.txt"']]) - end - - it "parses headers with ':' characters in the value" do - subject.parse(complex_header) - expect(subject.headers).to eq([['Date', 'Wed,20 Aug 2014 08:45:38 -0500']]) - end - end -end diff --git a/spec/lib/rex/mime/message_spec.rb b/spec/lib/rex/mime/message_spec.rb deleted file mode 100644 index e38f03e332..0000000000 --- a/spec/lib/rex/mime/message_spec.rb +++ /dev/null @@ -1,411 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'rex/mime' -require 'rex/text' - -RSpec.describe Rex::MIME::Message do - - subject do - described_class.new - end - - describe "#initialize" do - subject(:message_class) do - described_class.allocate - end - - let(:raw_message) do - message = "MIME-Version: 1.0\r\n" - message << "Content-Type: multipart/mixed; boundary=\"_Part_12_3195573780_381739540\"\r\n" - message << "Subject: Pull Request\r\n" - message << "Date: Wed,20 Aug 2014 08:45:38 -0500\r\n" - message << "Message-ID: \r\n" - message << "From: contributor@msfdev.int\r\n" - message << "To: msfdev@msfdev.int\r\n" - message << "\r\n" - message << "--_Part_12_3195573780_381739540\r\n" - message << "Content-Disposition: inline; filename=\"content\"\r\n" - message << "Content-Type: application/octet-stream; name=\"content\"\r\n" - message << "Content-Transfer-Encoding: base64\r\n" - message << "\r\n" - message << "Q29udGVudHM=\r\n" - message << "\r\n" - message << "--_Part_12_3195573780_381739540--\r\n" - - message - end - - it "creates a new Rex::MIME::Header" do - message_class.send(:initialize) - expect(message_class.header).to be_a(Rex::MIME::Header) - end - - it "creates an empty array of parts" do - message_class.send(:initialize) - expect(message_class.parts).to be_empty - end - - it "creates a random bound" do - message_class.send(:initialize) - expect(message_class.bound).to include('_Part_') - end - - it "allows to populate headers from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.headers.length).to eq(7) - end - - it "allows to create a MIME-Version header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('MIME-Version')).to eq(['MIME-Version', '1.0']) - end - - it "allows to create a Content-Type header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('Content-Type')).to eq(['Content-Type', "multipart/mixed; boundary=\"_Part_12_3195573780_381739540\""]) - end - - it "allows to create a Subject header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('Subject')).to eq(['Subject', 'Pull Request']) - end - - it "allows to create a Date header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('Date')).to eq(['Date', 'Wed,20 Aug 2014 08:45:38 -0500']) - end - - it "allows to create a Message-ID header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('Message-ID')).to eq(['Message-ID', '']) - end - - it "allows to create a From header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('From')).to eq(['From', 'contributor@msfdev.int']) - end - - it "allows to create a To header from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.header.find('To')).to eq(['To', 'msfdev@msfdev.int']) - end - - it "allows to populate parts from argument" do - message_class.send(:initialize, raw_message) - expect(message_class.parts.length).to eq(1) - end - - it "allows to populate parts headers from argument" do - message_class.send(:initialize, raw_message) - part = message_class.parts[0] - expect(part.header.headers.length).to eq(3) - end - - it "allows to populate parts contents from argument" do - message_class.send(:initialize, raw_message) - part = message_class.parts[0] - expect(part.content).to eq("Q29udGVudHM=") - end - end - - describe "#to" do - it "returns nil if To: header doesn't exist" do - expect(subject.to).to be_nil - end - - it "returns the To: header value if it exists" do - subject.header.add('To', 'msfdev') - expect(subject.to).to eq('msfdev') - end - end - - describe "#to=" do - it "sets the To: header value" do - subject.to = 'msfdev' - expect(subject.to).to eq('msfdev') - end - end - - - describe "#from" do - it "returns nil if From: header doesn't exist" do - expect(subject.from).to be_nil - end - - it "returns the From: header value if it exists" do - subject.header.add('From', 'msfdev') - expect(subject.from).to eq('msfdev') - end - end - - describe "#from=" do - it "sets the From: header value" do - subject.from = 'msfdev' - expect(subject.from).to eq('msfdev') - end - end - - describe "#subject" do - it "returns nil if Subject: header doesn't exist" do - expect(subject.subject).to be_nil - end - - it "returns the Subject: header value if it exists" do - subject.header.add('Subject', 'msfdev') - expect(subject.subject).to eq('msfdev') - end - end - - describe "#subject=" do - it "sets the Subject: header value" do - subject.subject = 'msfdev' - expect(subject.subject).to eq('msfdev') - end - end - - describe "#mime_defaults" do - it "sets the MIME-Version header" do - subject.mime_defaults - expect(subject.header.find('MIME-Version')).to_not be_nil - end - - it "sets the MIME-Version header to '1.0'" do - subject.mime_defaults - expect(subject.header.find('MIME-Version')).to eq(['MIME-Version', '1.0']) - end - - it "sets the Content-Type header" do - subject.mime_defaults - expect(subject.header.find('Content-Type')).to_not be_nil - end - - it "sets the Content-Type header to multipart/mixed" do - subject.mime_defaults - expect(subject.header.find('Content-Type')[1]).to include('multipart/mixed') - end - - it "sets the Subject header" do - subject.mime_defaults - expect(subject.header.find('Subject')).to_not be_nil - end - - it "sets the Subject header to empty string" do - subject.mime_defaults - expect(subject.header.find('Subject')).to eq(['Subject', '']) - end - - it "sets the Message-ID header" do - subject.mime_defaults - expect(subject.header.find('Message-ID')).to_not be_nil - end - - it "sets the From header" do - subject.mime_defaults - expect(subject.header.find('From')).to_not be_nil - end - - it "sets the From header to empty string" do - subject.mime_defaults - expect(subject.header.find('From')).to eq(['From', '']) - end - - it "sets the To header" do - subject.mime_defaults - expect(subject.header.find('To')).to_not be_nil - end - - it "sets the To header to empty string" do - subject.mime_defaults - expect(subject.header.find('To')).to eq(['To', '']) - end - end - - describe "#add_part" do - subject(:part) do - described_class.new.add_part(*args) - end - - let(:args) { [] } - - it "returns the new part" do - expect(part).to be_a(Rex::MIME::Part) - end - - it "set part's Content-Type to text/plain by default" do - expect(part.header.find('Content-Type')[1]).to eq('text/plain') - end - - it "set part's Content-Transfer-Encoding to 8bit by default" do - expect(part.header.find('Content-Transfer-Encoding')[1]).to eq('8bit') - end - - it "doesn't set part's Content-Disposition by default" do - expect(part.header.find('Content-Disposition')).to be_nil - end - - context "with Content-Type argument" do - let(:args) { ['', 'application/pdf'] } - - it "creates a part Content-Type header" do - expect(part.header.find('Content-Type')[1]).to eq('application/pdf') - end - end - - context "with Content-Transfer-Encoding argument" do - let(:args) { ['', 'application/pdf', 'binary'] } - - it "creates a part Content-Transfer-Encoding header" do - expect(part.header.find('Content-Transfer-Encoding')[1]).to eq('binary') - end - end - - context "with Content-Disposition argument" do - let(:args) { ['', 'application/pdf', 'binary', 'attachment; filename="fname.ext"'] } - - it "creates a part Content-Disposition header" do - expect(part.header.find('Content-Disposition')[1]).to eq('attachment; filename="fname.ext"') - end - end - - context "with content argument" do - let(:args) { ['msfdev'] } - - it "creates part content" do - expect(part.content).to eq('msfdev') - end - end - - end - - describe "#add_part_attachment" do - it "requires data argument" do - expect { subject.add_part_attachment }.to raise_error(ArgumentError) - end - - it "requires name argument" do - expect { subject.add_part_attachment('data') }.to raise_error(ArgumentError) - end - - it 'returns the new Rex::MIME::Part' do - expect(subject.add_part_attachment('data', 'name')).to be_a(Rex::MIME::Part) - end - - it 'encodes the part content with base64' do - part = subject.add_part_attachment('data', 'name') - expect(part.content).to eq(Rex::Text.encode_base64('data', "\r\n")) - end - - it 'setup Content-Type as application/octet-stream' do - part = subject.add_part_attachment('data', 'name') - expect(part.header.find('Content-Type')[1]).to eq('application/octet-stream; name="name"') - end - - it 'setup Content-Transfer-Encoding as base64' do - part = subject.add_part_attachment('data', 'name') - expect(part.header.find('Content-Transfer-Encoding')[1]).to eq('base64') - end - - it 'setup Content-Disposition as attachment' do - part = subject.add_part_attachment('data', 'name') - expect(part.header.find('Content-Disposition')[1]).to eq('attachment; filename="name"') - end - end - - describe "#add_part_inline_attachment" do - it "requires data argument" do - expect { subject.add_part_inline_attachment }.to raise_error(ArgumentError) - end - - it "requires name argument" do - expect { subject.add_part_inline_attachment('data') }.to raise_error(ArgumentError) - end - - it 'returns the new Rex::MIME::Part' do - expect(subject.add_part_inline_attachment('data', 'name')).to be_a(Rex::MIME::Part) - end - - it 'encodes the part content with base64' do - part = subject.add_part_inline_attachment('data', 'name') - expect(part.content).to eq(Rex::Text.encode_base64('data', "\r\n")) - end - - it 'setup Content-Type as application/octet-stream' do - part = subject.add_part_inline_attachment('data', 'name') - expect(part.header.find('Content-Type')[1]).to eq('application/octet-stream; name="name"') - end - - it 'setup Content-Transfer-Encoding as base64' do - part = subject.add_part_inline_attachment('data', 'name') - expect(part.header.find('Content-Transfer-Encoding')[1]).to eq('base64') - end - - it 'setup Content-Disposition as attachment' do - part = subject.add_part_inline_attachment('data', 'name') - expect(part.header.find('Content-Disposition')[1]).to eq('inline; filename="name"') - end - end - - describe "#to_s" do - let(:regexp_mail) do - regex = "MIME-Version: 1.0\r\n" - regex << "Content-Type: multipart/mixed; boundary=\"_Part_.*\"\r\n" - regex << "Subject: Pull Request\r\n" - regex << "Date: .*\r\n" - regex << "Message-ID: <.*@.*>\r\n" - regex << "From: contributor@msfdev.int\r\n" - regex << "To: msfdev@msfdev.int\r\n" - regex << "\r\n" - regex << "--_Part_.*\r\n" - regex << "Content-Disposition: inline\r\n" - regex << "Content-Type: text/plain\r\n" - regex << "Content-Transfer-Encoding: base64\r\n" - regex << "\r\n" - regex << "Q29udGVudHM=\r\n" - regex << "\r\n" - regex << "--_Part_.*--\r\n" - - Regexp.new(regex) - end - - let(:regexp_web) do - regex = "--_Part_.*\r\n" - regex << "Content-Disposition: form-data; name=\"action\"\r\n" - regex << "\r\n" - regex << "save\r\n" - regex << "--_Part_.*\r\n" - regex << "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" - regex << "Content-Type: application/octet-stream\r\n" - regex << "\r\n" - regex << "Contents\r\n" - regex << "--_Part_.*\r\n" - regex << "Content-Disposition: form-data; name=\"title\"\r\n" - regex << "\r\n" - regex << "Title\r\n" - regex << "--_Part_.*--\r\n" - - Regexp.new(regex) - end - - it "returns empty string if Rex::MIME::Message is empty" do - expect(subject.to_s).to be_empty - end - - it "generates valid MIME email messages" do - subject.mime_defaults - subject.from = "contributor@msfdev.int" - subject.to = "msfdev@msfdev.int" - subject.subject = "Pull Request" - subject.add_part(Rex::Text.encode_base64("Contents", "\r\n"), "text/plain", "base64", "inline") - expect(regexp_mail.match(subject.to_s)).to_not be_nil - end - - it "generates valid MIME web forms" do - subject.add_part("save", nil, nil, "form-data; name=\"action\"") - subject.add_part("Contents", "application/octet-stream", nil, "form-data; name=\"file\"; filename=\"test.txt\"") - subject.add_part("Title", nil, nil, "form-data; name=\"title\"") - expect(regexp_web.match(subject.to_s)).to_not be_nil - end - end - -end diff --git a/spec/lib/rex/mime/part_spec.rb b/spec/lib/rex/mime/part_spec.rb deleted file mode 100644 index 232fbf42cd..0000000000 --- a/spec/lib/rex/mime/part_spec.rb +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding:binary -*- -require 'spec_helper' - -require 'rex/mime' - -RSpec.describe Rex::MIME::Part do - - subject do - described_class.new - end - - describe "#initialize" do - subject(:part_class) do - described_class.allocate - end - - it "initializes the Rex::MIME::Header object" do - part_class.send(:initialize) - expect(part_class.header).to be_a(Rex::MIME::Header) - end - - it "initializes the Rex::MIME::Header with an empty array of headers" do - part_class.send(:initialize) - expect(part_class.header.headers).to be_empty - end - - it "Initializes content with an empty String" do - part_class.send(:initialize) - expect(part_class.content).to be_empty - end - end - - describe "#transfer_encoding" do - it "returns nil if the part hasn't a Content-Transfer-Encoding header" do - expect(subject.transfer_encoding).to be_nil - end - - it "returns the transfer encoding value if a Content-Transfer-Encoding header exists" do - subject.header.add('Content-Transfer-Encoding', 'base64') - expect(subject.transfer_encoding).to eq('base64') - end - end - - describe "#binary_content?" do - it "returns false if transfer encoding isn't defined" do - expect(subject.binary_content?).to be_falsey - end - - it "returns false if transfer encoding isn't binary" do - subject.header.add('Content-Transfer-Encoding', 'base64') - expect(subject.binary_content?).to be_falsey - end - - it "returns true if transfer encoding is binary" do - subject.header.add('Content-Transfer-Encoding', 'binary') - expect(subject.binary_content?).to be_truthy - end - end - - describe "#content_encoded" do - let(:content_test) do - "\rTest1\n" - end - - it "returns the exact content if transfer encoding is binary" do - subject.header.add('Content-Transfer-Encoding', 'binary') - subject.content = content_test - expect(subject.content_encoded).to eq(content_test) - end - - it "returns the content crlf encoded if transfer encoding isn't binary" do - subject.content = content_test - expect(subject.content_encoded).to eq("Test1\r\n") - end - end - - describe "#to_s" do - it "returns headers and content separated by two \\r\\n sequences" do - subject.header.add('var', 'val') - subject.content = 'content' - expect(subject.to_s).to eq("var: val\r\n\r\ncontent\r\n") - end - - it "returns two \\r\\n sequences if part is empty" do - expect(subject.to_s).to eq("\r\n\r\n") - end - - it "ends with \\r\\n sequence" do - expect(subject.to_s).to end_with("\r\n") - end - end -end