CVE-2014-1859 (published 2018-01-08T19:29Z)

(1) core/tests/test_memmap.py, (2) core/tests/test_multiarray.py, (3) f2py/f2py2e.py, and (4) lib/tests/test_io.py in NumPy before 1.8.1 allow local users to write to arbitrary files via a symlink attack on a temporary file.

Severity: LOW
CVSS v2 base score: 2.1 (AV:L/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-59
cwe_name: Improper Link Resolution Before File Access ('Link Following')
description: The product attempts to access a file based on the filename, but it does not properly prevent that filename from identifying a link or shortcut that resolves to an unintended resource.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/numpy/numpy
commit_details: repo_url: https://github.com/numpy/numpy
author: Julian Taylor
author_date: 2014-02-05 23:01:47+01:00
msg: ENH: remove insecure mktemp use

mktemp only returns a filename, a malicous user could replace it before
it gets used.
repository: repo_url: https://github.com/numpy/numpy
repo_name: numpy/numpy
description: The fundamental package for scientific computing with Python.
repo_language: Python
owner: numpy
file_changes: filename: test_memmap.py
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 from __future__ import division, absolute_import, print_function
 
 import sys
-from tempfile import NamedTemporaryFile, TemporaryFile, mktemp
+from tempfile import NamedTemporaryFile, TemporaryFile
 import os
 
 from numpy import memmap
@@ -33,12 +33,11 @@ def test_roundtrip(self):
         assert_array_equal(self.data, newfp)
 
     def test_open_with_filename(self):
-        tmpname = mktemp('', 'mmap')
-        fp = memmap(tmpname, dtype=self.dtype, mode='w+',
-                       shape=self.shape)
-        fp[:] = self.data[:]
-        del fp
-        os.unlink(tmpname)
+        with NamedTemporaryFile() as tmp:
+            fp = memmap(tmp.name, dtype=self.dtype, mode='w+',
+                        shape=self.shape)
+            fp[:] = self.data[:]
+            del fp
 
     def test_unnamed_file(self):
         with TemporaryFile() as f:
@@ -55,17 +54,16 @@ def test_attributes(self):
         del fp
 
     def test_filename(self):
-        tmpname = mktemp('', 'mmap')
-        fp = memmap(tmpname, dtype=self.dtype, mode='w+',
-                       shape=self.shape)
-        abspath = os.path.abspath(tmpname)
-        fp[:] = self.data[:]
-        self.assertEqual(abspath, fp.filename)
-        b = fp[:1]
-        self.assertEqual(abspath, b.filename)
-        del b
-        del fp
-        os.unlink(tmpname)
+        with NamedTemporaryFile() as tmp:
+            fp = memmap(tmp.name, dtype=self.dtype, mode='w+',
+                        shape=self.shape)
+            abspath = os.path.abspath(tmp.name)
+            fp[:] = self.data[:]
+            self.assertEqual(abspath, fp.filename)
+            b = fp[:1]
+            self.assertEqual(abspath, b.filename)
+            del b
+            del fp
 
     def test_filename_fileobj(self):
         fp = memmap(self.tmpfp, dtype=self.dtype, mode="w+",

programming_language: Python
filename: test_multiarray.py
change_type: ModificationType.MODIFY
diff: @@ -2316,12 +2316,11 @@ def setUp(self):
         self.x = rand(shape) + rand(shape).astype(np.complex)*1j
         self.x[0,:, 1] = [nan, inf, -inf, nan]
         self.dtype = self.x.dtype
-        self.filename = tempfile.mktemp()
+        self.file = tempfile.NamedTemporaryFile()
+        self.filename = self.file.name
 
     def tearDown(self):
-        if os.path.isfile(self.filename):
-            os.unlink(self.filename)
-            #tmp_file.close()
+        self.file.close()
 
     def test_bool_fromstring(self):
         v = np.array([True, False, True, False], dtype=np.bool_)
@@ -2349,7 +2348,6 @@ def test_roundtrip_file(self):
         y = np.fromfile(f, dtype=self.dtype)
         f.close()
         assert_array_equal(y, self.x.flat)
-        os.unlink(self.filename)
 
     def test_roundtrip_filename(self):
         self.x.tofile(self.filename)
@@ -2402,8 +2400,6 @@ def test_file_position_after_fromfile(self):
                 f.close()
                 assert_equal(pos, 10, err_msg=err_msg)
 
-        os.unlink(self.filename)
-
     def test_file_position_after_tofile(self):
         # gh-4118
         sizes = [io.DEFAULT_BUFFER_SIZE//8,
@@ -2431,8 +2427,6 @@ def test_file_position_after_tofile(self):
             f.close()
             assert_equal(pos, 10, err_msg=err_msg)
 
-        os.unlink(self.filename)
-
     def _check_from(self, s, value, **kw):
         y = np.fromstring(asbytes(s), **kw)
         assert_array_equal(y, value)
@@ -2535,7 +2529,6 @@ def test_tofile_sep(self):
         s = f.read()
         f.close()
         assert_equal(s, '1.51,2.0,3.51,4.0')
-        os.unlink(self.filename)
 
     def test_tofile_format(self):
         x = np.array([1.51, 2, 3.51, 4], dtype=float)

programming_language: Python
filename: __init__.py
change_type: ModificationType.MODIFY
diff: @@ -28,20 +28,20 @@ def compile(source,
     from numpy.distutils.exec_command import exec_command
     import tempfile
     if source_fn is None:
-        fname = os.path.join(tempfile.mktemp()+'.f')
+        f = tempfile.NamedTemporaryFile(suffix='.f')
     else:
-        fname = source_fn
-
-    f = open(fname, 'w')
-    f.write(source)
-    f.close()
-
-    args = ' -c -m %s %s %s'%(modulename, fname, extra_args)
-    c = '%s -c "import numpy.f2py as f2py2e;f2py2e.main()" %s' %(sys.executable, args)
-    s, o = exec_command(c)
-    if source_fn is None:
-        try: os.remove(fname)
-        except OSError: pass
+        f = open(source_fn, 'w')
+
+    try:
+        f.write(source)
+        f.flush()
+
+        args = ' -c -m %s %s %s'%(modulename, f.name, extra_args)
+        c = '%s -c "import numpy.f2py as f2py2e;f2py2e.main()" %s' % \
+                (sys.executable, args)
+        s, o = exec_command(c)
+    finally:
+        f.close()
     return s
 
 from numpy.testing import Tester

programming_language: Python
filename: f2py2e.py
change_type: ModificationType.MODIFY
diff: @@ -91,7 +91,7 @@
                    --lower is assumed with -h key, and --no-lower without -h key.
 
   --build-dir <dirname>  All f2py generated files are created in <dirname>.
-                   Default is tempfile.mktemp().
+                   Default is tempfile.mkdtemp().
 
   --overwrite-signature  Overwrite existing signature file.
 
@@ -424,7 +424,7 @@ def run_compile():
         del sys.argv[i]
     else:
         remove_build_dir = 1
-        build_dir = os.path.join(tempfile.mktemp())
+        build_dir = tempfile.mkdtemp()
 
     _reg1 = re.compile(r'[-][-]link[-]')
     sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)]

programming_language: Python
filename: test_io.py
change_type: ModificationType.MODIFY
diff: @@ -4,7 +4,9 @@
 import gzip
 import os
 import threading
-from tempfile import mkstemp, mktemp, NamedTemporaryFile
+import shutil
+import contextlib
+from tempfile import mkstemp, mkdtemp, NamedTemporaryFile
 import time
 import warnings
 import gc
@@ -21,6 +23,12 @@
                                 assert_raises, run_module_suite)
 from numpy.testing import assert_warns, assert_, build_err_msg
 
+@contextlib.contextmanager
+def tempdir(change_dir=False):
+    tmpdir = mkdtemp()
+    yield tmpdir
+    shutil.rmtree(tmpdir)
+
 
 class TextIO(BytesIO):
     """Helper IO class.
@@ -177,14 +185,14 @@ def roundtrip(self, *args, **kwargs):
     @np.testing.dec.slow
     def test_big_arrays(self):
         L = (1 << 31) + 100000
-        tmp = mktemp(suffix='.npz')
         a = np.empty(L, dtype=np.uint8)
-        np.savez(tmp, a=a)
-        del a
-        npfile = np.load(tmp)
-        a = npfile['a']
-        npfile.close()
-        os.remove(tmp)
+        with tempdir() as tmpdir:
+            tmp = open(os.path.join(tmpdir, "file.npz"), "w")
+            np.savez(tmp, a=a)
+            del a
+            npfile = np.load(tmp)
+            a = npfile['a']
+            npfile.close()
 
     def test_multiple_arrays(self):
         a = np.array([[1, 2], [3, 4]], float)

programming_language: Python