1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
From: Matthias Bussonnier <bussonniermatthias@gmail.com>
Date: Wed, 19 Jan 2022 14:21:24 +0100
Subject: Merge pull request from GHSA-pq7m-3gw7-gq5x
FIX CVE-2022-21699
IPython/__init__.py | 4 ++++
IPython/core/application.py | 2 +-
IPython/core/profileapp.py | 7 +++---
IPython/core/profiledir.py | 4 ++--
IPython/tests/cve.py | 56 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 67 insertions(+), 6 deletions(-)
create mode 100644 IPython/tests/cve.py
diff --git a/IPython/__init__.py b/IPython/__init__.py
index 4fb7710..c17ec76 100644
@@ -65,6 +65,10 @@
__license__ = release.license
__version__ = release.version
version_info = release.version_info
+# list of CVEs that should have been patched in this release.
+# this is informational and should not be relied upon.
+__patched_cves__ = {"CVE-2022-21699"}
+
def embed_kernel(module=None, local_ns=None, **kwargs):
"""Embed and start an IPython kernel in a given scope.
diff --git a/IPython/core/application.py b/IPython/core/application.py
index 93639d8..4f679df 100644
@@ -133,7 +133,7 @@ def _config_file_name_changed(self, change):
config_file_paths = List(Unicode())
@default('config_file_paths')
def _config_file_paths_default(self):
- return [os.getcwd()]
+ return []
extra_config_file = Unicode(
help="""Path to an extra config file to load.
diff --git a/IPython/core/profileapp.py b/IPython/core/profileapp.py
index 97434e3..9a1bae5 100644
@@ -181,9 +181,10 @@ def list_profile_dirs(self):
profiles = list_profiles_in(os.getcwd())
if profiles:
print()
- print("Available profiles in current directory (%s):" % os.getcwd())
- self._print_profiles(profiles)
-
+ print(
+ "Profiles from CWD have been removed for security reason, see CVE-2022-21699:"
+ )
+
print()
print("To use any of the above profiles, start IPython with:")
print(" ipython --profile=<name>")
diff --git a/IPython/core/profiledir.py b/IPython/core/profiledir.py
index 3199dfd..2c48e4c 100644
@@ -186,7 +186,7 @@ def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
is not found, a :class:`ProfileDirError` exception will be raised.
The search path algorithm is:
- 1. ``os.getcwd()``
+ 1. ``os.getcwd()`` # removed for security reason.
2. ``ipython_dir``
Parameters
@@ -198,7 +198,7 @@ def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
will be "profile_<profile>".
"""
dirname = u'profile_' + name
- paths = [os.getcwd(), ipython_dir]
+ paths = [ipython_dir]
for p in paths:
profile_dir = os.path.join(p, dirname)
if os.path.isdir(profile_dir):
diff --git a/IPython/tests/cve.py b/IPython/tests/cve.py
new file mode 100644
index 0000000..026415a
@@ -0,0 +1,56 @@
+"""
+Test that CVEs stay fixed.
+"""
+
+from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
+from pathlib import Path
+import random
+import sys
+import os
+import string
+import subprocess
+import time
+
+def test_cve_2022_21699():
+ """
+ Here we test CVE-2022-21699.
+
+ We create a temporary directory, cd into it.
+ Make a profile file that should not be executed and start IPython in a subprocess,
+ checking for the value.
+
+
+
+ """
+
+ dangerous_profile_dir = Path('profile_default')
+
+ dangerous_startup_dir = dangerous_profile_dir / 'startup'
+ dangerous_expected = 'CVE-2022-21699-'+''.join([random.choice(string.ascii_letters) for i in range(10)])
+
+ with TemporaryWorkingDirectory() as t:
+ dangerous_startup_dir.mkdir(parents=True)
+ (dangerous_startup_dir/ 'foo.py').write_text(f'print("{dangerous_expected}")')
+ # 1 sec to make sure FS is flushed.
+ #time.sleep(1)
+ cmd = [sys.executable,'-m', 'IPython']
+ env = os.environ.copy()
+ env['IPY_TEST_SIMPLE_PROMPT'] = '1'
+
+
+ # First we fake old behavior, making sure the profile is/was actually dangerous
+ p_dangerous = subprocess.Popen(cmd + [f'--profile-dir={dangerous_profile_dir}'], env=env, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out_dangerous, err_dangerouns = p_dangerous.communicate(b"exit\r")
+ assert dangerous_expected in out_dangerous.decode()
+
+ # Now that we know it _would_ have been dangerous, we test it's not loaded
+ p = subprocess.Popen(cmd, env=env, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = p.communicate(b"exit\r")
+ assert b'IPython' in out
+ assert dangerous_expected not in out.decode()
+ assert err == b''
+
+
+
|