Skip to content

Commit 5a1559d

Browse files
authored
gh-112997: Don't log arguments in asyncio unless debugging (#115667)
Nothing else in Python generally logs the contents of variables, so this can be very unexpected for developers and could leak sensitive information in to terminals and log files.
1 parent a355f60 commit 5a1559d

File tree

4 files changed

+42
-12
lines changed

4 files changed

+42
-12
lines changed

Lib/asyncio/events.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def _repr_info(self):
5454
info.append('cancelled')
5555
if self._callback is not None:
5656
info.append(format_helpers._format_callback_source(
57-
self._callback, self._args))
57+
self._callback, self._args,
58+
debug=self._loop.get_debug()))
5859
if self._source_traceback:
5960
frame = self._source_traceback[-1]
6061
info.append(f'created at {frame[0]}:{frame[1]}')
@@ -90,7 +91,8 @@ def _run(self):
9091
raise
9192
except BaseException as exc:
9293
cb = format_helpers._format_callback_source(
93-
self._callback, self._args)
94+
self._callback, self._args,
95+
debug=self._loop.get_debug())
9496
msg = f'Exception in callback {cb}'
9597
context = {
9698
'message': msg,

Lib/asyncio/format_helpers.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,26 @@ def _get_function_source(func):
1919
return None
2020

2121

22-
def _format_callback_source(func, args):
23-
func_repr = _format_callback(func, args, None)
22+
def _format_callback_source(func, args, *, debug=False):
23+
func_repr = _format_callback(func, args, None, debug=debug)
2424
source = _get_function_source(func)
2525
if source:
2626
func_repr += f' at {source[0]}:{source[1]}'
2727
return func_repr
2828

2929

30-
def _format_args_and_kwargs(args, kwargs):
30+
def _format_args_and_kwargs(args, kwargs, *, debug=False):
3131
"""Format function arguments and keyword arguments.
3232
3333
Special case for a single parameter: ('hello',) is formatted as ('hello').
34+
35+
Note that this function only returns argument details when
36+
debug=True is specified, as arguments may contain sensitive
37+
information.
3438
"""
39+
if not debug:
40+
return '()'
41+
3542
# use reprlib to limit the length of the output
3643
items = []
3744
if args:
@@ -41,10 +48,11 @@ def _format_args_and_kwargs(args, kwargs):
4148
return '({})'.format(', '.join(items))
4249

4350

44-
def _format_callback(func, args, kwargs, suffix=''):
51+
def _format_callback(func, args, kwargs, *, debug=False, suffix=''):
4552
if isinstance(func, functools.partial):
46-
suffix = _format_args_and_kwargs(args, kwargs) + suffix
47-
return _format_callback(func.func, func.args, func.keywords, suffix)
53+
suffix = _format_args_and_kwargs(args, kwargs, debug=debug) + suffix
54+
return _format_callback(func.func, func.args, func.keywords,
55+
debug=debug, suffix=suffix)
4856

4957
if hasattr(func, '__qualname__') and func.__qualname__:
5058
func_repr = func.__qualname__
@@ -53,7 +61,7 @@ def _format_callback(func, args, kwargs, suffix=''):
5361
else:
5462
func_repr = repr(func)
5563

56-
func_repr += _format_args_and_kwargs(args, kwargs)
64+
func_repr += _format_args_and_kwargs(args, kwargs, debug=debug)
5765
if suffix:
5866
func_repr += suffix
5967
return func_repr

Lib/test/test_asyncio/test_events.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -2250,7 +2250,7 @@ def test_handle_repr(self):
22502250
h = asyncio.Handle(noop, (1, 2), self.loop)
22512251
filename, lineno = test_utils.get_function_source(noop)
22522252
self.assertEqual(repr(h),
2253-
'<Handle noop(1, 2) at %s:%s>'
2253+
'<Handle noop() at %s:%s>'
22542254
% (filename, lineno))
22552255

22562256
# cancelled handle
@@ -2268,14 +2268,14 @@ def test_handle_repr(self):
22682268
# partial function
22692269
cb = functools.partial(noop, 1, 2)
22702270
h = asyncio.Handle(cb, (3,), self.loop)
2271-
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s>$'
2271+
regex = (r'^<Handle noop\(\)\(\) at %s:%s>$'
22722272
% (re.escape(filename), lineno))
22732273
self.assertRegex(repr(h), regex)
22742274

22752275
# partial function with keyword args
22762276
cb = functools.partial(noop, x=1)
22772277
h = asyncio.Handle(cb, (2, 3), self.loop)
2278-
regex = (r'^<Handle noop\(x=1\)\(2, 3\) at %s:%s>$'
2278+
regex = (r'^<Handle noop\(\)\(\) at %s:%s>$'
22792279
% (re.escape(filename), lineno))
22802280
self.assertRegex(repr(h), regex)
22812281

@@ -2316,6 +2316,24 @@ def test_handle_repr_debug(self):
23162316
'<Handle cancelled noop(1, 2) at %s:%s created at %s:%s>'
23172317
% (filename, lineno, create_filename, create_lineno))
23182318

2319+
# partial function
2320+
cb = functools.partial(noop, 1, 2)
2321+
create_lineno = sys._getframe().f_lineno + 1
2322+
h = asyncio.Handle(cb, (3,), self.loop)
2323+
regex = (r'^<Handle noop\(1, 2\)\(3\) at %s:%s created at %s:%s>$'
2324+
% (re.escape(filename), lineno,
2325+
re.escape(create_filename), create_lineno))
2326+
self.assertRegex(repr(h), regex)
2327+
2328+
# partial function with keyword args
2329+
cb = functools.partial(noop, x=1)
2330+
create_lineno = sys._getframe().f_lineno + 1
2331+
h = asyncio.Handle(cb, (2, 3), self.loop)
2332+
regex = (r'^<Handle noop\(x=1\)\(2, 3\) at %s:%s created at %s:%s>$'
2333+
% (re.escape(filename), lineno,
2334+
re.escape(create_filename), create_lineno))
2335+
self.assertRegex(repr(h), regex)
2336+
23192337
def test_handle_source_traceback(self):
23202338
loop = asyncio.get_event_loop_policy().new_event_loop()
23212339
loop.set_debug(True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Stop logging potentially sensitive callback arguments in :mod:`asyncio`
2+
unless debug mode is active.

0 commit comments

Comments
 (0)