|
msg76753 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-02 15:41 |
Race condition in the rmtree function in the shutils module allows local
users to delete arbitrary files and directories via a symlink attack.
See also http://bugs.debian.org/286922
Attack:
---
# emulate removing /etc
$ sudo cp -a /etc /root/etc/
$ sudo python2.6
>>> for i in xrange(0, 50000):
... with open("/root/etc/" + str(i), "w") as f:
... f.write("0")
...
$ ls /root/etc > orig_list.txt
$ mkdir /tmp/attack
$ cp -a /root/etc/* /tmp/attack
$ sudo python2.6
>>> from shutil import rmtree
>>> rmtree('/tmp/attack')
>>> # press ctrl-z to suspend execution
^Z
[1]+ Stopped sudo python2.6
$ mv /tmp/attack /tmp/dummy; ln -s /root/etc /tmp/attack
$ fg
sudo python2.6
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.6/shutil.py", line 225, in rmtree
onerror(os.rmdir, path, sys.exc_info())
File "/usr/local/lib/python2.6/shutil.py", line 223, in rmtree
os.rmdir(path)
OSError: [Errno 20] Not a directory: '/tmp/attack'
$ ls /root/etc > new_list.txt
$ diff -q orig_list.txt new_list.txt
Files orig_list.txt and new_list.txt differ
---
If the attack wasn't successful, /root/etc would not be modified and
orig_list.txt and new_list.txt would be identical.
|
|
msg78389 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-27 23:23 |
What course of action do you suggest? First chmod 0700 on the directory?
|
|
msg78391 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-27 23:31 |
Mmmh, very recent Linux kernels (>= 2.6.16) seem to have specific
functions to deal with this (see man pages for openat, unlinkat, etc.).
|
|
msg78398 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-28 11:56 |
A shameless copy of the Perl fix for the bug
http://bugs.debian.org/286922 looks like the evident solution.
Somebody has to examine the fix though, I'm afraid I'm not currently
able to do it.
|
|
msg78405 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-28 14:38 |
The Perl patch is here:
http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=36;filename=etch_03_fix_file_path;att=1;bug=286922
It is a recursive implementation of rmtree. What it does is 1) get the
inode of the path 2) unlink it altogether if not a dir 3) otherwise,
chdir to it 4) check that '.' still has the same inode, otherwise bail out.
|
|
msg78406 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-28 14:49 |
Mmmh, the problem with Perl's approach is that it changes the current
working directory (calls to chdir()), which is process-specific and not
thread-specific. Currently, no function in shutil changes the current
working directory, which is a nice behaviour and should IMO be preserved.
|
|
msg78418 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-28 16:38 |
> Mmmh, the problem with Perl's approach is that it changes the current
> working directory (calls to chdir()), which is process-specific and not
> thread-specific. Currently, no function in shutil changes the current
> working directory, which is a nice behaviour and should IMO be preserved.
Using chdir() makes sense and it doesn't look like a too big problem to me:
def rmtree(...):
...
curdir = os.getcwd()
try:
call chdir() as required
finally:
try:
os.chdir(curdir)
except:
warnings.warn("Unable to chdir to previous current dir")
...
|
|
msg78425 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-28 19:07 |
> Using chdir() makes sense and it doesn't look like a too big problem to me:
It's a problem if another thread in the process is making file
operations using relative paths at the same time.
Since shutil functions have until now been safe against this
possibility, I don't think it's ok to make them unsafe in future
versions.
|
|
msg78440 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 08:22 |
Ah, right you are. Attaching an initial alpha-quality patched shutil.py
and a script to test the attack.
Run the script by sourcing it with . test_issue4489.sh, not by executing
(job control won't work in this case).
|
|
msg78441 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 08:30 |
And here's the diff so you can review what I was up to.
Note that this does not yet fix the problem (although the logic looks
about right), I have to examine the problem more thoroughly.
|
|
msg78442 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 08:41 |
Aha, got it -- while removing /a/b/c/d, there's no easy way to detect
that b or c has become a symlink.
I.e.
given directory tree
a
`-- b
|-- c
`-- d
1. os.rmdir('/a/b/c') succeeds
2. execution is suspended
3. '/a/b' is made a symlink to a path that contains 'd'
4. '/a/b/d' is neither a symlink, nor has it's inode been recorded, so
os.rmdir('/a/b/d') succeeds
I'm afraid the solution for the Perl bug is susceptible to the same problem.
|
|
msg78443 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 08:46 |
A blunt, ineffective solution would be to walk the tree before removing
it and recording path : inode pairs in a dict on first pass and then
checking that the inodes have not changed during removal on second pass.
If no clever bulletproof fix emerges, perhaps this should be added as
shutil.rmtree_safe (duh, API bloat...)?
|
|
msg78444 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-29 10:05 |
> A blunt, ineffective solution would be to walk the tree before removing
> it and recording path : inode pairs in a dict on first pass and then
> checking that the inodes have not changed during removal on second pass.
There's no way to do the "check inode then remove" sequence atomically.
|
|
msg78445 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 11:15 |
Fixed a minor bug in test script and added Perl test as well.
Perl with File-Path-2.07 passes the test.
|
|
msg78446 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 11:19 |
Antoine, what if we add another function, rmtree_safe() that uses
chdir() and document that it is protected from the race condition but
may have the side effect of changing the current dir in threaded
environment?
|
|
msg78447 - (view) |
Author: Mart Sõmermaa (mrts) |
Date: 2008-12-29 11:26 |
Replying to previous comment:
> There's no way to do the "check inode then remove" sequence atomically.
Right, although the attack window would be tiny, this is not a real
solution.
|
|
msg78448 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-29 11:32 |
> Antoine, what if we add another function, rmtree_safe() that uses
> chdir() and document that it is protected from the race condition but
> may have the side effect of changing the current dir in threaded
> environment?
I don't have any strong opinion on it, maybe it should be brought on the
mailing-list (or just wait for some other devs to show up here).
|
|
msg78451 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2008-12-29 12:27 |
FWIW, I've opened a separate bug entry for the creation of the openat(),
etc. wrappers: #4761.
Those functions seem to exist on recent Linux distros (even Debian stable).
|
|
msg103686 - (view) |
Author: Tarek Ziadé (tarek) *  |
Date: 2010-04-20 09:26 |
Mart,
I took over the maintenance of shutil, if you are interested in contributing a bullet-proof version of rmtree, please drop a line at python-ideas, and let's see what we can do in a new function maybe.
I'll be happy to review and apply patches if we get a consensus
|
|
msg124472 - (view) |
Author: K Richard Pixley (teamnoir) |
Date: 2010-12-22 01:13 |
How does "rm -rf" address this issue? Or does it?
shutils.rmtree should probably do the same thing.
|
|
msg125425 - (view) |
Author: Ross Lagerwall (rosslagerwall)  |
Date: 2011-01-05 14:27 |
Here is a draft patch.
It uses the *at functions and fdlistdir consequently it only makes it safe if those functions are available. It works using a recursive implementation and an open file descriptor pointing to a directory, instead of maintaining state by changing the current directory. If the *at functions are unavailable, it falls back to the unsafe implementation.
It requires the patches from issue4761 and issue10755 to work.
|
|
msg125429 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2011-01-05 15:21 |
Thanks for the patch.
There seems to be a race remaining here:
+ try:
+ if os.path.islink(path):
+ # symlinks to directories are forbidden, see bug #1669
+ raise OSError("Cannot call rmtree on a symbolic link")
+ except OSError:
+ onerror(os.path.islink, path, sys.exc_info())
+ # can't continue even if onerror hook returns
+ return
+ fd = os.open(path, os.O_RDONLY)
Someone could change `path` to be a symlink between the calls to islink() and open(). You probably need to stat the fd instead.
Some other things:
- if close() is meant to be a private helper, it should be named _close()
- instead of a bare "except" in close(), use "except EnvironmentError" or "except OSError"
I haven't looked at the tests yet.
|
|
msg125435 - (view) |
Author: Ross Lagerwall (rosslagerwall)  |
Date: 2011-01-05 16:58 |
Updated patch removes the race condition. Since an open follows symlinks, you can't just fstat the fd to see if it is a link. I followed the following to overcome this:
https://www.securecoding.cert.org/confluence/display/seccode/POS35-C.+Avoid+race+conditions+while+checking+for+the+existence+of+a+symbolic+link
|
|
msg125436 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2011-01-05 17:06 |
Le mercredi 05 janvier 2011 à 16:58 +0000, Ross Lagerwall a écrit :
> Ross Lagerwall <rosslagerwall@gmail.com> added the comment:
>
> Updated patch removes the race condition. Since an open follows symlinks, you can't just fstat the fd to see if it is a link. I followed the following to overcome this:
> https://www.securecoding.cert.org/confluence/display/seccode/POS35-C.+Avoid+race+conditions+while+checking+for+the+existence+of+a+symbolic+link
Nice. I am unsure about the following piece of code:
+ if stat.S_ISDIR(mode):
+ if stat.S_ISLNK(mode):
+ try:
+ raise OSError("Cannot call rmtree on a symbolic
link")
+ except OSError:
+ onerror(os.fstatat, (dirfd, name), sys.exc_info())
If rmtree() encounters a symlink *inside* the tree, I would expect it to
simply remove the symlink, rather than choke and abort (it's also what
the unsafe implementation does).
|
|
msg125446 - (view) |
Author: Ross Lagerwall (rosslagerwall)  |
Date: 2011-01-05 18:19 |
I think I misread the original implementation. Here is an updated version with that code just taken out.
|
|
msg142609 - (view) |
Author: Éric Araujo (eric.araujo) *  |
Date: 2011-08-21 10:57 |
I made two comments on rietveld but the email was rejected.
|
|
msg144621 - (view) |
Author: Ross Lagerwall (rosslagerwall)  |
Date: 2011-09-29 19:42 |
Updated patch based on Eric's comments:
Store _supports_safe_rmdir at the module level.
Move imports up to module level
Skip test on non-threading build
|
|
msg145113 - (view) |
Author: Éric Araujo (eric.araujo) *  |
Date: 2011-10-07 17:35 |
I made another review but my mail was rejected.
http://bugs.python.org/review/4489/diff/3383/10563#newcode319
Lib/test/test_shutil.py:319: @unittest.skipIf(threading == None,
'requires threading')
You can just say skipUnless(threading, 'msg')
http://bugs.python.org/review/4489/diff/3383/10563#newcode344
Lib/test/test_shutil.py:344: raise
Shouldn’t this use self.fail?
|
|
msg145133 - (view) |
Author: Ross Lagerwall (rosslagerwall)  |
Date: 2011-10-07 19:34 |
http://bugs.python.org/review/4489/diff/3383/10563#newcode319
Lib/test/test_shutil.py:319: @unittest.skipIf(threading == None, 'requires
threading')
On 2011/10/07 19:29:47, eric.araujo wrote:
> You can just say skipUnless(threading, 'msg')
Right.
http://bugs.python.org/review/4489/diff/3383/10563#newcode344
Lib/test/test_shutil.py:344: raise
On 2011/10/07 19:29:47, eric.araujo wrote:
> Shouldn’t this use self.fail?
I would have thought it's better if the original exception is passed through and
displayed rather than some sort of failure message that just says "OSError
occurred".
|
|
msg147058 - (view) |
Author: Charles-François Natali (neologix) *  |
Date: 2011-11-04 23:58 |
There's a race:
"""
--- Lib/shutil.py 2011-11-05 00:11:05.745221315 +0100
+++ Lib/shutil.py.new 2011-11-05 00:11:01.445220324 +0100
@@ -307,6 +307,7 @@
try:
mode = os.fstatat(dirfd, name, os.AT_SYMLINK_NOFOLLOW).st_mode
except os.error:
mode = 0
if stat.S_ISDIR(mode):
+ input("press enter")
newfd = os.openat(dirfd, name, os.O_RDONLY)
_rmtree_safe(newfd, ignore_errors, onerror)
try:
"""
$ rm -rf /tmp/target
$ mkdir -p /tmp/target/etc
$ ./python -c "import shutil; shutil.rmtree('/tmp/target')"
press enter^Z
[1]+ Stopped ./python -c "import shutil; shutil.rmtree('/tmp/target')"
$ rm -r /tmp/target/etc; ln -s /etc /tmp/target/
$ fg
./python -c "import shutil; shutil.rmtree('/tmp/target')"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/cf/python/cpython/Lib/shutil.py", line 290, in rmtree
_rmtree_safe(fd, ignore_errors, onerror)
File "/home/cf/python/cpython/Lib/shutil.py", line 314, in _rmtree_safe
_rmtree_safe(newfd, ignore_errors, onerror)
File "/home/cf/python/cpython/Lib/shutil.py", line 323, in _rmtree_safe
onerror(os.unlinkat, (dirfd, name), sys.exc_info())
File "/home/cf/python/cpython/Lib/shutil.py", line 321, in _rmtree_safe
os.unlinkat(dirfd, name)
PermissionError: [Errno 13] Permission denied
[52334 refs]
"""
openat(3, "etc", O_RDONLY|O_LARGEFILE) = 4
dup(4) = 5
fstat64(5, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
fcntl64(5, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fcntl64(5, F_SETFD, FD_CLOEXEC) = 0
getdents64(5, /* 162 entries */, 32768) = 5176
getdents64(5, /* 0 entries */, 32768) = 0
close(5) = 0
fstatat64(4, "passwd", {st_mode=S_IFREG|0644, st_size=980, ...}, AT_SYMLINK_NOFOLLOW) = 0
unlinkat(4, "passwd", 0) = -1 EACCES (Permission denied)
"""
You should use the lstat/open/fstat idiom.
Also, here:
"""
mode1 = os.lstat(path).st_mode
if stat.S_ISLNK(mode1):
raise OSError("Cannot call rmtree on a symbolic link")
except OSError:
onerror(os.lstat, path, sys.exc_info())
# can't continue even if onerror hook returns
return
fd = os.open(path, os.O_RDONLY)
try:
mode2 = os.fstat(fd).st_mode
if mode1 != mode2:
raise OSError("Target changed")
"""
You check that path is not a symlink, then you open it, perform fstat on it, and check that the mode is the same.
But if someone replaces path by a symlink to a directory with the same mode, then rmtree won't catch this. You should also compare st_dev and st_ino to make sure we're dealing with the same file.
One more thing :-)
"""
fd = os.open(path, os.O_RDONLY)
try:
mode2 = os.fstat(fd).st_mode
if mode1 != mode2:
raise OSError("Target changed")
except OSError:
onerror(os.fstat, fd, sys.exc_info())
# can't continue if target has changed
return
"""
Here `fd` is not closed (there might be other places leaking FD).
Finally, since writting a such code is tricky, what do you - all - think of making this a generic walker method that would take as argument the methods to call on a directory and on a file (or link), so that we could reuse it to write chmodtree(), chowntree() and friends?
|
|
msg147059 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2011-11-05 00:11 |
> Finally, since writting a such code is tricky, what do you - all -
> think of making this a generic walker method that would take as
> argument the methods to call on a directory and on a file (or link),
> so that we could reuse it to write chmodtree(), chowntree() and
> friends?
Sounds good.
FYI, I have a pathlib experiment in
http://hg.python.org/features/pathlib/, with an optional openat-based
accessor.
|
|
msg147080 - (view) |
Author: Charles-François Natali (neologix) *  |
Date: 2011-11-05 11:56 |
> FYI, I have a pathlib experiment in
> http://hg.python.org/features/pathlib/, with an optional openat-based
> accessor.
Interesting: I used to think that the current API for dealing with paths was a little too basic and terse.
Concerning this issue, one (last) thing: rmtree performs a depth-first traversal of the directory tree, keeping an open FD at each directory level: in case of deeply-nested directory hierarchy, or if there are many open FDs, there's the risk of running out of FDs.
I think the best thing would be to let rmtree fail (provided it closes all the FDs it opened): falling back to the "unsafe" version would be stupid (an attacker would just have to create a deeply-nested hierarchy, and then use the same old symlink race).
|
|
msg147217 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2011-11-07 10:54 |
> I think the best thing would be to let rmtree fail (provided it closes
> all the FDs it opened)
Agreed.
|
|
msg147249 - (view) |
Author: Ross Lagerwall (rosslagerwall)  |
Date: 2011-11-07 19:11 |
Thanks Charles, I'll take your comments into account and take a look at making a general walker method.
|
|
msg150794 - (view) |
Author: Hynek Schlawack (hynek) |
Date: 2012-01-07 13:13 |
What's the current state here? Anyone working on a solution or are we waiting how http://hg.python.org/features/pathlib/ will work out?
If the consensus is to add a generic walker method, wouldn't be appropriate to open a new bug and add it as dependency? Or is there one I've missed?
|
|
msg150810 - (view) |
Author: Antoine Pitrou (pitrou) *  |
Date: 2012-01-07 17:56 |
> What's the current state here? Anyone working on a solution or are we
> waiting how http://hg.python.org/features/pathlib/ will work out?
Well, I am not working on that one, so waiting for it to work out might
be optimistic :)
I don't know what to do with it (the pathlib): is such a feature
desireable enough?
> If the consensus is to add a generic walker method, wouldn't be
> appropriate to open a new bug and add it as dependency?
Agreed.
|
|
msg150834 - (view) |
Author: Hynek Schlawack (hynek) |
Date: 2012-01-08 00:15 |
> > What's the current state here? Anyone working on a solution or are we
> > waiting how http://hg.python.org/features/pathlib/ will work out?
>
> Well, I am not working on that one, so waiting for it to work out might
> be optimistic :)
> I don't know what to do with it (the pathlib): is such a feature
> desireable enough?
Independently from this bug, I'd say it would be a good thing.
Proof: http://twistedmatrix.com/documents/current/api/twisted.python.filepath.html – Twisted already implemented something similar for themselves.
> > If the consensus is to add a generic walker method, wouldn't be
> > appropriate to open a new bug and add it as dependency?
>
> Agreed.
See #13734
|
|
msg150952 - (view) |
Author: Éric Araujo (eric.araujo) *  |
Date: 2012-01-09 16:40 |
In case you want opinions on pathlib: I, for one, disliked Jason Orendorff’s path module, because it did not distinguish between pure string operations and I/O-inducing operations, and also because it duplicated os/os.path functions. Your API doesn’t have these issues, so for my taste it’s conceptually better. I should clone your repo and play with the module a bit to see if I like it.
|
|
| Date |
User |
Action |
Args |
| 2012-01-09 16:40:01 | eric.araujo | set | messages:
+ msg150952 |
| 2012-01-09 14:29:21 | jcea | set | nosy:
+ jcea
|
| 2012-01-08 06:20:24 | rosslagerwall | set | dependencies:
+ Add a generic directory walker method to avoid symlink attacks |
| 2012-01-08 00:15:51 | hynek | set | messages:
+ msg150834 |
| 2012-01-07 17:56:23 | pitrou | set | messages:
+ msg150810 |
| 2012-01-07 13:13:06 | hynek | set | messages:
+ msg150794 |
| 2011-11-07 19:11:47 | rosslagerwall | set | messages:
+ msg147249 |
| 2011-11-07 10:54:48 | pitrou | set | messages:
+ msg147217 |
| 2011-11-05 11:56:49 | neologix | set | messages:
+ msg147080 |
| 2011-11-05 00:11:26 | pitrou | set | messages:
+ msg147059 |
| 2011-11-04 23:58:29 | neologix | set | nosy:
+ neologix messages:
+ msg147058
|
| 2011-10-07 19:34:36 | rosslagerwall | set | messages:
+ msg145133 |
| 2011-10-07 17:35:39 | eric.araujo | set | messages:
+ msg145113 |
| 2011-09-29 19:42:55 | rosslagerwall | set | files:
+ i4489_v4.patch
messages:
+ msg144621 |
| 2011-08-21 11:04:08 | petri.lehtinen | set | nosy:
+ petri.lehtinen
|
| 2011-08-21 11:01:53 | hynek | set | nosy:
+ hynek
|
| 2011-08-21 10:57:24 | eric.araujo | set | messages:
+ msg142609 |
| 2011-01-05 18:48:07 | pitrou | set | nosy:
pitrou, schmir, tarek, eric.araujo, mrts, teamnoir, rosslagerwall dependencies:
+ create Python wrappers for openat() and others, Add posix.fdlistdir |
| 2011-01-05 18:19:22 | rosslagerwall | set | files:
+ i4489_v3.patch nosy:
pitrou, schmir, tarek, eric.araujo, mrts, teamnoir, rosslagerwall messages:
+ msg125446
|
| 2011-01-05 17:06:01 | pitrou | set | nosy:
pitrou, schmir, tarek, eric.araujo, mrts, teamnoir, rosslagerwall messages:
+ msg125436 |
| 2011-01-05 16:58:16 | rosslagerwall | set | files:
+ i4489_v2.patch nosy:
pitrou, schmir, tarek, eric.araujo, mrts, teamnoir, rosslagerwall messages:
+ msg125435
|
| 2011-01-05 15:36:17 | schmir | set | nosy:
+ schmir
|
| 2011-01-05 15:21:16 | pitrou | set | nosy:
pitrou, tarek, eric.araujo, mrts, teamnoir, rosslagerwall versions:
- Python 2.6, Python 2.5, Python 3.1, Python 2.7, Python 3.2 messages:
+ msg125429 stage: needs patch -> patch review |
| 2011-01-05 14:27:03 | rosslagerwall | set | files:
+ i4489.patch nosy:
+ rosslagerwall messages:
+ msg125425
|
| 2010-12-22 09:21:02 | eric.araujo | set | nosy:
+ eric.araujo stage: needs patch
versions:
+ Python 2.5 |
| 2010-12-22 01:13:39 | teamnoir | set | nosy:
+ teamnoir messages:
+ msg124472
|
| 2010-04-20 09:26:07 | tarek | set | versions:
+ Python 3.1, Python 2.7, Python 3.2, Python 3.3, - Python 2.5, Python 2.4, Python 2.3, Python 3.0 nosy:
+ tarek
messages:
+ msg103686
assignee: tarek |
| 2008-12-29 12:27:27 | pitrou | set | messages:
+ msg78451 |
| 2008-12-29 11:32:50 | pitrou | set | messages:
+ msg78448 |
| 2008-12-29 11:26:41 | mrts | set | messages:
+ msg78447 |
| 2008-12-29 11:19:18 | mrts | set | messages:
+ msg78446 |
| 2008-12-29 11:15:02 | mrts | set | files:
+ test_issue4489.sh messages:
+ msg78445 |
| 2008-12-29 11:13:35 | mrts | set | files:
- test_issue4489.sh |
| 2008-12-29 10:05:04 | pitrou | set | messages:
+ msg78444 |
| 2008-12-29 08:46:05 | mrts | set | messages:
+ msg78443 |
| 2008-12-29 08:41:50 | mrts | set | messages:
+ msg78442 |
| 2008-12-29 08:30:40 | mrts | set | files:
+ issue4489_first_attempt.diff keywords:
+ patch messages:
+ msg78441 |
| 2008-12-29 08:23:14 | mrts | set | files:
+ test_issue4489.sh |
| 2008-12-29 08:22:55 | mrts | set | files:
+ shutil_patched.py messages:
+ msg78440 |
| 2008-12-28 19:07:15 | pitrou | set | messages:
+ msg78425 |
| 2008-12-28 16:38:35 | mrts | set | messages:
+ msg78418 |
| 2008-12-28 14:49:48 | pitrou | set | messages:
+ msg78406 |
| 2008-12-28 14:38:39 | pitrou | set | messages:
+ msg78405 |
| 2008-12-28 11:56:31 | mrts | set | messages:
+ msg78398 |
| 2008-12-27 23:31:55 | pitrou | set | messages:
+ msg78391 |
| 2008-12-27 23:23:11 | pitrou | set | nosy:
+ pitrou messages:
+ msg78389 |
| 2008-12-02 15:42:01 | mrts | create | |