412 lines
13 KiB
Ruby
412 lines
13 KiB
Ruby
# -*- coding:binary -*-
|
|
require 'spec_helper'
|
|
|
|
require 'rex/mime'
|
|
require 'rex/text'
|
|
|
|
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: <WRobqc7gEyQVIQwEkLS7FN3ZNhS1Xj9pU2szC24rggMg@tqUqGjjSLEvssbwm>\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', '<WRobqc7gEyQVIQwEkLS7FN3ZNhS1Xj9pU2szC24rggMg@tqUqGjjSLEvssbwm>'])
|
|
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
|