##// END OF EJS Templates
Merge pull request from GHSA-pq7m-3gw7-gq5x...
Matthias Bussonnier -
Show More
@@ -0,0 +1,56 b''
1 """
2 Test that CVEs stay fixed.
3 """
4
5 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
6 from pathlib import Path
7 import random
8 import sys
9 import os
10 import string
11 import subprocess
12 import time
13
14 def test_cve_2022_21699():
15 """
16 Here we test CVE-2022-21699.
17
18 We create a temporary directory, cd into it.
19 Make a profile file that should not be executed and start IPython in a subprocess,
20 checking for the value.
21
22
23
24 """
25
26 dangerous_profile_dir = Path('profile_default')
27
28 dangerous_startup_dir = dangerous_profile_dir / 'startup'
29 dangerous_expected = 'CVE-2022-21699-'+''.join([random.choice(string.ascii_letters) for i in range(10)])
30
31 with TemporaryWorkingDirectory() as t:
32 dangerous_startup_dir.mkdir(parents=True)
33 (dangerous_startup_dir/ 'foo.py').write_text(f'print("{dangerous_expected}")')
34 # 1 sec to make sure FS is flushed.
35 #time.sleep(1)
36 cmd = [sys.executable,'-m', 'IPython']
37 env = os.environ.copy()
38 env['IPY_TEST_SIMPLE_PROMPT'] = '1'
39
40
41 # First we fake old behavior, making sure the profile is/was actually dangerous
42 p_dangerous = subprocess.Popen(cmd + [f'--profile-dir={dangerous_profile_dir}'], env=env, stdin=subprocess.PIPE,
43 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
44 out_dangerous, err_dangerouns = p_dangerous.communicate(b"exit\r")
45 assert dangerous_expected in out_dangerous.decode()
46
47 # Now that we know it _would_ have been dangerous, we test it's not loaded
48 p = subprocess.Popen(cmd, env=env, stdin=subprocess.PIPE,
49 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
50 out, err = p.communicate(b"exit\r")
51 assert b'IPython' in out
52 assert dangerous_expected not in out.decode()
53 assert err == b''
54
55
56
@@ -60,6 +60,10 b" __author__ = '%s <%s>' % (release.author, release.author_email)"
60 60 __license__ = release.license
61 61 __version__ = release.version
62 62 version_info = release.version_info
63 # list of CVEs that should have been patched in this release.
64 # this is informational and should not be relied upon.
65 __patched_cves__ = {"CVE-2022-21699"}
66
63 67
64 68 def embed_kernel(module=None, local_ns=None, **kwargs):
65 69 """Embed and start an IPython kernel in a given scope.
@@ -157,7 +157,7 b' class BaseIPythonApplication(Application):'
157 157 config_file_paths = List(Unicode())
158 158 @default('config_file_paths')
159 159 def _config_file_paths_default(self):
160 return [os.getcwd()]
160 return []
161 161
162 162 extra_config_file = Unicode(
163 163 help="""Path to an extra config file to load.
@@ -181,8 +181,9 b' class ProfileList(Application):'
181 181 profiles = list_profiles_in(os.getcwd())
182 182 if profiles:
183 183 print()
184 print("Available profiles in current directory (%s):" % os.getcwd())
185 self._print_profiles(profiles)
184 print(
185 "Profiles from CWD have been removed for security reason, see CVE-2022-21699:"
186 )
186 187
187 188 print()
188 189 print("To use any of the above profiles, start IPython with:")
@@ -188,7 +188,7 b' class ProfileDir(LoggingConfigurable):'
188 188 is not found, a :class:`ProfileDirError` exception will be raised.
189 189
190 190 The search path algorithm is:
191 1. ``os.getcwd()``
191 1. ``os.getcwd()`` # removed for security reason.
192 192 2. ``ipython_dir``
193 193
194 194 Parameters
@@ -200,7 +200,7 b' class ProfileDir(LoggingConfigurable):'
200 200 will be "profile_<profile>".
201 201 """
202 202 dirname = u'profile_' + name
203 paths = [os.getcwd(), ipython_dir]
203 paths = [ipython_dir]
204 204 for p in paths:
205 205 profile_dir = os.path.join(p, dirname)
206 206 if os.path.isdir(profile_dir):
@@ -2,6 +2,50 b''
2 2 8.x Series
3 3 ============
4 4
5
6 IPython 8.0.1 (CVE-2022-21699)
7 ------------------------------
8
9 IPython 8.0.1, 7.31.1 and 5.11 are security releases that change some default
10 values in order to prevent potential Execution with Unnecessary Privileges.
11
12 Almost all version of IPython looks for configuration and profiles in current
13 working directory. Since IPython was developed before pip and environments
14 existed it was used a convenient way to load code/packages in a project
15 dependant way.
16
17 In 2022, it is not necessary anymore, and can lead to confusing behavior where
18 for example cloning a repository and starting IPython or loading a notebook from
19 any Jupyter-Compatible interface that has ipython set as a kernel can lead to
20 code execution.
21
22
23 I did not find any standard way for packaged to advertise CVEs they fix, I'm
24 thus trying to add a ``__patched_cves__`` attribute to the IPython module that
25 list the CVEs that should have been fixed. This attribute is informational only
26 as if a executable has a flaw, this value can always be changed by an attacker.
27
28 .. code::
29
30 In [1]: import IPython
31
32 In [2]: IPython.__patched_cves__
33 Out[2]: {'CVE-2022-21699'}
34
35 In [3]: 'CVE-2022-21699' in IPython.__patched_cves__
36 Out[3]: True
37
38 Thus starting with this version:
39
40 - The current working directory is not searched anymore for profiles or
41 configurations files.
42 - Added a ``__patched_cves__`` attribute (set of strings) to IPython module that contain
43 the list of fixed CVE. This is informational only.
44
45 Further details can be read on the `GitHub Advisory <https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x>`__
46
47
48
5 49 IPython 8.0
6 50 -----------
7 51
General Comments 0
You need to be logged in to leave comments. Login now