From 1d58dcaae11cb9d7bc97cf6b67f10fb2898a2f2d Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sun, 4 Aug 2024 11:18:22 -0700 Subject: [PATCH] actions: Define and allow a new alias for str; secret_str - Method parameters marked with secret_str will not be logged. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/actions.py | 14 ++++++++++++++ plinth/tests/test_actions.py | 19 ++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/plinth/actions.py b/plinth/actions.py index 0e8f39098..a5cbb1be1 100644 --- a/plinth/actions.py +++ b/plinth/actions.py @@ -23,6 +23,13 @@ EXIT_PERM = 20 logger = logging.getLogger(__name__) +# An alias for 'str' to mark some strings as sensitive. Sensitive strings are +# not logged. Use 'type secret_str = str' when Python 3.11 support is no longer +# needed. +class secret_str(str): + pass + + def privileged(func): """Mark a method as allowed to be run as privileged method. @@ -405,6 +412,13 @@ def _privileged_assert_valid_type(arg_name, value, annotation): return + # secret_str should be a regular string + if annotation == secret_str: + if not isinstance(value, str): + raise TypeError(f'Expected type str for {arg_name}') + + return + # 'int | str' or 'typing.Union[int, str]' if (isinstance(annotation, types.UnionType) or getattr(annotation, '__origin__', None) == typing.Union): diff --git a/plinth/tests/test_actions.py b/plinth/tests/test_actions.py index b4e8f3f3f..e9b67f14e 100644 --- a/plinth/tests/test_actions.py +++ b/plinth/tests/test_actions.py @@ -16,7 +16,7 @@ from unittest.mock import Mock, call, patch import pytest from plinth import actions, cfg -from plinth.actions import privileged +from plinth.actions import privileged, secret_str actions_name = 'actions' @@ -84,14 +84,22 @@ def test_privileged_argument_annotation_check(): def func2(_a: int, _b): return - def func_valid(_a: int, _b: dict[int, str]): + # Parameter with 'password' in the name should be typed 'secret_str' + def func3(_password: str): return - for func in (func1, func2): + def func1_valid(_a: int, _b: dict[int, str]): + return + + def func2_valid(_password: secret_str): + return + + for func in (func1, func2, func3): with pytest.raises(SyntaxError): privileged(func) - privileged(func_valid) + privileged(func1_valid) + privileged(func2_valid) @patch('plinth.actions._get_privileged_action_module_name') @@ -282,7 +290,7 @@ def test_assert_valid_type(): assert_valid(None, None, typing.Any) # Invalid values for int, str, float and Optional - values = [[1, bool], ['foo', int], [1, str], [1, float], + values = [[1, bool], ['foo', int], [1, str], [1, secret_str], [1, float], [1, typing.Optional[str]], [1, str | None], [1.1, typing.Union[int, str]], [1.1, int | str], [1, list], [1, dict], [[1], list[str]], [{ @@ -298,6 +306,7 @@ def test_assert_valid_type(): assert_valid('arg', True, bool) assert_valid('arg', 1, int) assert_valid('arg', '1', str) + assert_valid('arg', '1', secret_str) assert_valid('arg', 1.1, float) assert_valid('arg', None, typing.Optional[int]) assert_valid('arg', 1, typing.Optional[int])