diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 66ceee0..45ed4e2 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -3013,7 +3013,7 @@ class InteractiveShell(SingletonConfigurable): runner = _pseudo_sync_runner try: - return runner(coro) + result = runner(coro) except BaseException as e: info = ExecutionInfo( raw_cell, store_history, silent, shell_futures, cell_id @@ -3021,6 +3021,7 @@ class InteractiveShell(SingletonConfigurable): result = ExecutionResult(info) result.error_in_exec = e self.showtraceback(running_compiled_code=True) + finally: return result def should_run_async( diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 4ad5474..920d911 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -1127,3 +1127,49 @@ def test_set_custom_completer(): # clean up ip.Completer.custom_matchers.pop() + + +class TestShowTracebackAttack(unittest.TestCase): + """Test that the interactive shell is resilient against the client attack of + manipulating the showtracebacks method. These attacks shouldn't result in an + unhandled exception in the kernel.""" + + def setUp(self): + self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback + + def tearDown(self): + interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback + + def test_set_show_tracebacks_none(self): + """Test the case of the client setting showtracebacks to None""" + + result = ip.run_cell( + """ + import IPython.core.interactiveshell + IPython.core.interactiveshell.InteractiveShell.showtraceback = None + + assert False, "This should not raise an exception" + """ + ) + print(result) + + assert result.result is None + assert isinstance(result.error_in_exec, TypeError) + assert str(result.error_in_exec) == "'NoneType' object is not callable" + + def test_set_show_tracebacks_noop(self): + """Test the case of the client setting showtracebacks to a no op lambda""" + + result = ip.run_cell( + """ + import IPython.core.interactiveshell + IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None + + assert False, "This should not raise an exception" + """ + ) + print(result) + + assert result.result is None + assert isinstance(result.error_in_exec, AssertionError) + assert str(result.error_in_exec) == "This should not raise an exception"