Skip to content

Commit

Permalink
Merge pull request from GHSA-pq7m-3gw7-gq5x
Browse files Browse the repository at this point in the history
  • Loading branch information
Carreau committed Jan 19, 2022
2 parents 50b3d1f + 5a3dd92 commit 46a51ed
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 6 deletions.
4 changes: 4 additions & 0 deletions IPython/__init__.py
Expand Up @@ -60,6 +60,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.
Expand Down
2 changes: 1 addition & 1 deletion IPython/core/application.py
Expand Up @@ -157,7 +157,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.
Expand Down
7 changes: 4 additions & 3 deletions IPython/core/profileapp.py
Expand Up @@ -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>")
Expand Down
4 changes: 2 additions & 2 deletions IPython/core/profiledir.py
Expand Up @@ -188,7 +188,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
Expand All @@ -200,7 +200,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):
Expand Down
56 changes: 56 additions & 0 deletions IPython/tests/cve.py
@@ -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''



44 changes: 44 additions & 0 deletions docs/source/whatsnew/version8.rst
Expand Up @@ -2,6 +2,50 @@
8.x Series
============


IPython 8.0.1 (CVE-2022-21699)
------------------------------

IPython 8.0.1, 7.31.1 and 5.11 are security releases that change some default
values in order to prevent potential Execution with Unnecessary Privileges.

Almost all version of IPython looks for configuration and profiles in current
working directory. Since IPython was developed before pip and environments
existed it was used a convenient way to load code/packages in a project
dependant way.

In 2022, it is not necessary anymore, and can lead to confusing behavior where
for example cloning a repository and starting IPython or loading a notebook from
any Jupyter-Compatible interface that has ipython set as a kernel can lead to
code execution.


I did not find any standard way for packaged to advertise CVEs they fix, I'm
thus trying to add a ``__patched_cves__`` attribute to the IPython module that
list the CVEs that should have been fixed. This attribute is informational only
as if a executable has a flaw, this value can always be changed by an attacker.

.. code::
In [1]: import IPython
In [2]: IPython.__patched_cves__
Out[2]: {'CVE-2022-21699'}
In [3]: 'CVE-2022-21699' in IPython.__patched_cves__
Out[3]: True
Thus starting with this version:

- The current working directory is not searched anymore for profiles or
configurations files.
- Added a ``__patched_cves__`` attribute (set of strings) to IPython module that contain
the list of fixed CVE. This is informational only.

Further details can be read on the `GitHub Advisory <https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x>`__



IPython 8.0
-----------

Expand Down

0 comments on commit 46a51ed

Please sign in to comment.