From: Zooko <zo...@zo...> - 2002-03-17 15:28:48
|
The shell command invoked for sending mail is: /bin/mail -s "CVS: %(SUBJECT)s" %(PEOPLE)s 2>&1 > /dev/null SUBJECT is determined from the file name, version number, and tags. Are you entirely sure that the file name, version number, and tags, couldn't contain a string like: `rm -rf /` or: ";cat /etc/passwd ? I'm not entirely sure of that. Anyway, with my patch for subject lines derived from log entries (in another message), it is *definitely* possible that the SUBJECT string could have such stuff. My fix is to change the command to: /bin/mail -s 'CVS: %(SUBJECT)s' %(PEOPLE)s 2>&1 > /dev/null And to run SUBJECT through: SUBJECT = string.replace(SUBJECT, "'", '"') My reading of the bash 2.05 man page tells me that this should render the contents of SUBJECT inert when used in that command. I hope the same applies to Bourne shell. This much was already in the "subject lines from log entries" patch that I already submitted, but I subsequently went hunting for similar problems and appended is a patch which ensures that no other such sneakiness could get in to the other commands that syncmail invokes. As a bonus, it also makes syncmail handle filenames with spaces (and all other kinds of weird character, except for '\'). Regards, Zooko --- zooko.com Security and Distributed Systems Engineering --- Index: syncmail =================================================================== RCS file: /cvsroot/mnet/CVSROOT/syncmail,v retrieving revision 1.14 retrieving revision 1.17 diff -u -r1.14 -r1.17 --- syncmail 17 Mar 2002 14:26:25 -0000 1.14 +++ syncmail 17 Mar 2002 15:17:03 -0000 1.17 @@ -61,11 +61,13 @@ """ +# standard Python modules +import getopt import os -import sys +import re import string +import sys import time -import getopt # Notification command MAILCMD = "/bin/mail -s 'CVS: %(SUBJECT)s' %(PEOPLE)s 2>&1 > /dev/null" @@ -90,9 +92,29 @@ +REV_RE=re.compile("^[0-9.]+$") def calculate_diff(filespec, contextlines): try: file, oldrev, newrev = string.split(filespec, ',') + if not REV_RE.match(oldrev): + raise ValueError + if not REV_RE.match(newrev): + raise ValueError + if string.find(file, '\\') != -1: + # I'm sorry, a file name that contains a backslash is just too much. + # XXX if someone wants to figure out how to escape the backslashes in a safe way to allow filenames containing backslashes, this is the place to do it. --Zooko 2002-03-17 + raise ValueError + if string.find(file, "'") != -1: + # Those crazy users put single-quotes in their file names! + # Now we have to escape everything that is meaningful inside double-quotes. + filestr = string.replace(file, '`', '\`') + filestr = string.replace(filestr, '"', '\"') + filestr = string.replace(filestr, '$', '\$') + # and quote it with double-quotes. + filestr = '"' + filestr + '"' + else: + # quote it with single-quotes. + filestr = "'" + file + "'" except ValueError: # No diff to report return '***** Bogus filespec: %s' % filespec @@ -101,7 +123,7 @@ if os.path.exists(file): fp = open(file) else: - update_cmd = 'cvs -fn update -r %s -p %s' % (newrev, file) + update_cmd = "cvs -fn update -r %s -p %s" % (newrev, filestr) fp = os.popen(update_cmd) lines = fp.readlines() fp.close() @@ -125,8 +147,7 @@ difftype = "-C " + str(contextlines) else: difftype = "-u" - diffcmd = "/usr/bin/cvs -f diff -kk %s --minimal -r %s -r %s '%s'" % ( - difftype, oldrev, newrev, file) + diffcmd = "/usr/bin/cvs -f diff -kk %s --minimal -r %s -r %s %s" % (difftype, oldrev, newrev, filestr) fp = os.popen(diffcmd) lines = fp.readlines() sts = fp.close() |