File CVE-2015-20107-mailcap-unsafe-filenames.patch of Package python.32348
---
Doc/library/mailcap.rst | 13 +++++++++++++
Lib/mailcap.py | 28 ++++++++++++++++++++++++++--
2 files changed, 39 insertions(+), 2 deletions(-)
--- a/Doc/library/mailcap.rst
+++ b/Doc/library/mailcap.rst
@@ -55,6 +55,19 @@ standard. However, mailcap files are su
will automatically check such conditions and skip the entry if the check fails.
+.. versionchanged:: 3.11
+
+ To prevent security issues with shell metacharacters (symbols that have
+ special effects in a shell command line), ``findmatch`` will refuse
+ to inject ASCII characters other than alphanumerics and ``@+=:,./-_``
+ into the returned command line.
+
+ If a disallowed character appears in *filename*, ``findmatch`` will always
+ return ``(None, None)`` as if no entry was found.
+ If such a character appears elsewhere (a value in *plist* or in *MIMEtype*),
+ ``findmatch`` will ignore all mailcap entries which use that value.
+ A :mod:`warning <warnings>` will be raised in either case.
+
.. function:: getcaps()
Returns a dictionary mapping MIME types to a list of mailcap file entries. This
--- a/Lib/mailcap.py
+++ b/Lib/mailcap.py
@@ -1,9 +1,17 @@
"""Mailcap file handling. See RFC 1524."""
import os
+import warnings
+import re
__all__ = ["getcaps","findmatch"]
+_find_unsafe = re.compile(ur'[^\xa1-\U0010FFFF\w@+=:,./-]').search
+
+class UnsafeMailcapInput(Warning):
+ """Warning raised when refusing unsafe input"""
+
+
# Part 1: top-level interface.
def getcaps():
@@ -18,6 +26,10 @@ def getcaps():
"""
caps = {}
for mailcap in listmailcapfiles():
+ if _find_unsafe(mailcap):
+ msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (mailcap,)
+ warnings.warn(msg, UnsafeMailcapInput)
+ return None, None
try:
fp = open(mailcap, 'r')
except IOError:
@@ -149,10 +161,13 @@ def findmatch(caps, MIMEtype, key='view'
for e in entries:
if 'test' in e:
test = subst(e['test'], filename, plist)
+ if test is None:
+ continue
if test and os.system(test) != 0:
continue
command = subst(e[key], MIMEtype, filename, plist)
- return command, e
+ if command is not None:
+ return command, e
return None, None
def lookup(caps, MIMEtype, key=None):
@@ -184,6 +199,10 @@ def subst(field, MIMEtype, filename, pli
elif c == 's':
res = res + filename
elif c == 't':
+ if _find_unsafe(MIMEtype):
+ msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,)
+ warnings.warn(msg, UnsafeMailcapInput)
+ return None
res = res + MIMEtype
elif c == '{':
start = i
@@ -191,7 +210,12 @@ def subst(field, MIMEtype, filename, pli
i = i+1
name = field[start:i]
i = i+1
- res = res + findparam(name, plist)
+ param = findparam(name, plist)
+ if _find_unsafe(param):
+ msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name)
+ warnings.warn(msg, UnsafeMailcapInput)
+ return None
+ res = res + param
# XXX To do:
# %n == number of parts if type is multipart/*
# %F == list of alternating type and filename for parts