Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CVE-2015-20107 #1833

Merged
merged 2 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions Src/StdLib/Lib/mailcap.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
"""Mailcap file handling. See RFC 1524."""

import os
import warnings
import re

__all__ = ["getcaps","findmatch"]

# https://github.com/IronLanguages/ironpython3/issues/1274
# _find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search
_find_unsafe = re.compile(r'[^\w@+=:,./-]').search

class UnsafeMailcapInput(Warning):
"""Warning raised when refusing unsafe input"""

# Part 1: top-level interface.

def getcaps():
Expand Down Expand Up @@ -144,15 +153,22 @@ def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]):
entry to use.

"""
if _find_unsafe(filename):
msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,)
warnings.warn(msg, UnsafeMailcapInput)
return None, None
entries = lookup(caps, MIMEtype, key)
# XXX This code should somehow check for the needsterminal flag.
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):
Expand Down Expand Up @@ -184,14 +200,23 @@ def subst(field, MIMEtype, filename, plist=[]):
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
while i < n and field[i] != '}':
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
Expand Down
10 changes: 7 additions & 3 deletions Src/StdLib/Lib/test/test_mailcap.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ def test_subst(self):
(["", "audio/*", "foo.txt"], ""),
(["echo foo", "audio/*", "foo.txt"], "echo foo"),
(["echo %s", "audio/*", "foo.txt"], "echo foo.txt"),
(["echo %t", "audio/*", "foo.txt"], "echo audio/*"),
(["echo \%t", "audio/*", "foo.txt"], "echo %t"),
(["echo %t", "audio/*", "foo.txt"], None),
(["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"),
(["echo \\%t", "audio/*", "foo.txt"], "echo %t"),
(["echo foo", "audio/*", "foo.txt", plist], "echo foo"),
(["echo %{total}", "audio/*", "foo.txt", plist], "echo 3")
]
Expand Down Expand Up @@ -183,7 +184,10 @@ def test_findmatch(self):
('"An audio fragment"', audio_basic_entry)),
([c, "audio/*"],
{"filename": fname},
("/usr/local/bin/showaudio audio/*", audio_entry)),
(None, None)),
([c, "audio/wav"],
{"filename": fname},
("/usr/local/bin/showaudio audio/wav", audio_entry)),
([c, "message/external-body"],
{"plist": plist},
("showexternal /dev/null default john python.org /tmp foo bar", message_entry))
Expand Down
Loading