Browse Source

Fixed unpacking of single-payload mails which contain only attachments

tags/0.5.1
Johann Schmitz 2 years ago
parent
commit
13f476fce7
Signed by: Johann Schmitz <johann@j-schmitz.net> GPG Key ID: A084064277C501ED
4 changed files with 83 additions and 15 deletions
  1. 37
    15
      amavisvt/client.py
  2. 14
    0
      tests/samples/test3_rfc822_with_single_payload.eml
  3. 1
    0
      tests/test_client.py
  4. 31
    0
      tests/test_unpack.py

+ 37
- 15
amavisvt/client.py View File

@@ -274,7 +274,23 @@ class Resource(object):
for part in self.unpack_mail_payload(payload):
yield part
else:
logger.debug("Skipping single payload message")
# single payload message. Since it's possible that the only payload is the message itself and the
# payload is an attachment we need this extra block
ct = msg.get_content_type() if 'Content-Type' in msg else None
if ct and not ct.startswith('text/'): # exclude common single payloads
try:
res = Resource._extract_mailpart(msg.get_payload(), msg.get_filename())
if res and AmavisVT.is_included(res):
logger.info("Found a single, included payload in mail")
yield res
except:
logger.exception("Could not single payload from mail")

else:
logger.debug("Skipping single payload message (Content-Type: %s)", ct)

except:
logger.exception("Failed to parse mail file %s", self.path)
@@ -297,22 +313,28 @@ class Resource(object):
continue

try:
partpayload = part.get_payload()
if len(partpayload) > 27892121: # roughly 20 MiB as base64
logger.warning("Skipping part (larger than 20MiB")
else:
fd, temp_path = tempfile.mkstemp('-mailpart', prefix='amavisvt-')
try:
os.write(fd, base64.b64decode(partpayload))
finally:
os.close(fd)
res = Resource._extract_mailpart(part.get_payload(), filename)
if res:
logger.debug("Mail part %s (%s): orig filename: %s, mime type: %s", i, res.path, filename,
res.mime_type)
yield res
except:
logger.exception("Could not extract attachment %s: %s", partname)

logger.debug("Mail part %s (%s): orig filename: %s, mime type: %s", i, temp_path, filename,
Resource(temp_path).mime_type)
@staticmethod
def _extract_mailpart(payload, filename):
if len(payload) > 27892121: # roughly 20 MiB as base64
logger.warning("Skipping part (larger than 20MiB")
return

fd, temp_path = tempfile.mkstemp('-mailpart', prefix='amavisvt-')
try:
os.write(fd, base64.b64decode(payload))
finally:
os.close(fd)

yield Resource(temp_path, filename=filename)
except Exception as ex:
logger.exception("Could not extract attachment %s: %s", partname, ex)
return Resource(temp_path, filename=filename)

def __str__(self):
return self.filename

+ 14
- 0
tests/samples/test3_rfc822_with_single_payload.eml View File

@@ -0,0 +1,14 @@
To: alice@example.com
From: bob@example.com
Subject: Mail with attachment
Message-ID: <ffca4c38-244b-7c0b-6c96-71645df67e3f@j-schmitz.net>
Date: Wed, 29 Jun 2016 08:15:53 +0200
MIME-Version: 1.0
Content-Type: application/zip; name="textfile.zip"
Content-Transfer-Encoding: base64
UEsDBAoAAAAAAJE23EjbrpF9BwAAAAcAAAAKABwAZm9vYmFyLnR4dFVUCQADkQJyV5ECcld1
eAsAAQToAwAABOgDAABGb29iYXIKUEsBAh4DCgAAAAAAkTbcSNuukX0HAAAABwAAAAoAGAAA
AAAAAQAAAKSBAAAAAGZvb2Jhci50eHRVVAUAA5ECcld1eAsAAQToAwAABOgDAABQSwUGAAAA
AAEAAQBQAAAASwAAAAAA

+ 1
- 0
tests/test_client.py View File

@@ -381,6 +381,7 @@ class TestClientRun(object):
avt):
mail = os.path.join(os.path.dirname(__file__), 'samples/mail_with_attachment.eml')
result = avt.run(mail)
print(result)
# the zip file in the attachment
assert len(result) == 1
# the zip file in the attachment

+ 31
- 0
tests/test_unpack.py View File

@@ -23,6 +23,7 @@ class TestUnpack(object):
return os.path.join(self.samples_dir, name)
def test_empty_file(self):
# literally, an empty mail
path = self._resource('test1_empty.eml')
r = Resource(path)
assert r.path == path
@@ -33,6 +34,7 @@ class TestUnpack(object):
assert r.mime_type == 'application/x-empty'
def test_mail_file(self):
# a mail with an single text/plain part
path = self._resource('test2_rfc822.eml')
r = Resource(path)
assert r.path == path
@@ -41,8 +43,36 @@ class TestUnpack(object):
assert r.sha1 == "b0c42741af78f8311abeff543be8f3c62247168a"
assert r.sha256 == "8179aa7716740f099a43d6c0aa8b77622dbbd7050bc56ce21cda2109444cf3d6"
assert r.mime_type == 'message/rfc822'
resources = list(r.unpack())
assert len(resources) == 0
def test_mail_with_single_payload(self):
path = self._resource('test3_rfc822_with_single_payload.eml')
r = Resource(path)
assert r.path == path
assert r.can_unpack
assert r.md5 == "6f25abac24792456c3741ef64cdd7a4d"
assert r.sha1 == "605f7ace5dcac01ebfb154c5b559cbd07662fad0"
assert r.sha256 == "c958f80e05844e7504d4ff705ffaebefa49bbfd8ce882e9a7267b9f176d8e96d"
assert r.mime_type == 'message/rfc822'
resources = list(r.unpack())
assert len(resources) == 1

zip_attachment = resources[0]
assert not zip_attachment.can_unpack
assert zip_attachment.md5 == "e77d94e09fbcf6641c1f848d98963298"
assert zip_attachment.sha1 == "acbfc25a642cb7fa574f38a361932d1c2fdc1a9e"
assert zip_attachment.sha256 == "93440551540584e48d911586606c319744c8e671c20ee6b12cca4b922127a127"
assert zip_attachment.mime_type == "application/zip"
for x in resources:
if not x.path == r.path:
os.remove(x.path)

def test_unpack_mail(self):
# multi-part email with a zip file attachment
path = self._resource('mail_with_attachment.eml')
r = Resource(path)
assert r.mime_type == "message/rfc822"
@@ -67,5 +97,6 @@ class TestUnpack(object):
os.remove(x.path)
def test_unpack_error(self):
# invalid file
uer = UnpackExceptionResource('/dev/null')
assert not list(uer.unpack())

Loading…
Cancel
Save