diff --git a/.flake8 b/.flake8 index 9db7fa80aa..0d131f8ba9 100644 --- a/.flake8 +++ b/.flake8 @@ -123,7 +123,7 @@ per-file-ignores = src/awx_plugins/credentials/plugins.py: B950,D100, D101, D103, D105, D107, D205, D400, LN001, WPS204, WPS229, WPS433, WPS440 src/awx_plugins/credentials/tss.py: ANN003, ANN201, D100, D103, E712, WPS433, WPS440, WPS503 src/awx_plugins/inventory/plugins.py: ANN001, ANN002, ANN003, ANN101, ANN102, ANN201, ANN202, ANN206, B950, C812, C819, D100, D101, D102, D205, D209, D400, D401, LN001, LN002, N801, WPS110, WPS111, WPS202, WPS210, WPS214, WPS301, WPS319, WPS324, WPS331, WPS336, WPS337, WPS338, WPS347, WPS421, WPS433, WPS450, WPS510, WPS529 - tests/credential_plugins_test.py: ANN101, B017, C419, D100, D102, D103, D205, D209, D400, DAR, PT011, S105, WPS111, WPS117, WPS118, WPS202, WPS352, WPS421, WPS433, WPS507 + tests/credential_plugins_test.py: ANN101, B017, C419, D100, D102, D103, D205, D209, D400, DAR, PT011, S105, WPS111, WPS117, WPS118, WPS202, WPS352, WPS421, WPS433, WPS507, WPS441 tests/importable_test.py: ANN101, DAR # Count the number of occurrences of each error/warning code and print a report: diff --git a/src/awx_plugins/credentials/aim.py b/src/awx_plugins/credentials/aim.py index f535e02194..ccac67ac14 100644 --- a/src/awx_plugins/credentials/aim.py +++ b/src/awx_plugins/credentials/aim.py @@ -7,7 +7,7 @@ gettext_noop as _, ) -import requests +import requests as requests from .plugin import CertFiles, CredentialPlugin, raise_for_status @@ -111,6 +111,20 @@ def aim_backend(**kwargs): verify=verify, allow_redirects=False, ) + sensitive_query_params = { + 'AppId': '****', + 'Query': '****', + 'QueryFormat': object_query_format, + } + if reason: + sensitive_query_params['reason'] = '****' + sensitive_request_qs = urlencode( + sensitive_query_params, + safe='*', + quote_via=quote, + ) + res.url = f'{request_url}?{sensitive_request_qs}' + raise_for_status(res) # CCP returns the property name capitalized, username is camel case # so we need to handle that case diff --git a/tests/credential_plugins_test.py b/tests/credential_plugins_test.py index 2c2f3eae67..acd8c10c12 100644 --- a/tests/credential_plugins_test.py +++ b/tests/credential_plugins_test.py @@ -5,7 +5,10 @@ import pytest -from awx_plugins.credentials import hashivault +import requests +from pytest_mock import MockerFixture + +from awx_plugins.credentials import aim, hashivault def test_imported_azure_cloud_sdk_vars() -> None: @@ -162,3 +165,65 @@ def test_tss_import(self) -> None: ): # assert this module as opposed to older thycotic.secrets.server assert cls.__module__ == 'delinea.secrets.server' + + +@pytest.mark.parametrize( + ( + 'reason', + 'expected_url_in_exc', + 'expected_response_url_literal', + ), + ( + pytest.param( + 'foobar123', + r'.*http://testurl\.com/AIMWebService/api/Accounts\?' + r'AppId=\*\*\*\*&Query=\*\*\*\*&QueryFormat=test&' + r'reason=\*\*\*\*.*', + 'http://testurl.com/AIMWebService/api/Accounts?' + 'AppId=****&Query=****&QueryFormat=test&reason=****', + id='with-reason', + ), + pytest.param( + '', + r'.*http://testurl\.com/AIMWebService/api/Accounts\?' + r'AppId=\*\*\*\*&Query=\*\*\*\*&QueryFormat=test.*', + 'http://testurl.com/AIMWebService/api/Accounts?' + 'AppId=****&Query=****&QueryFormat=test', + id='no-reason', + ), + ), +) +def test_aim_sensitive_traceback_masked( + mocker: MockerFixture, + monkeypatch: pytest.MonkeyPatch, + reason: str, + expected_url_in_exc: str, + expected_response_url_literal: str, +) -> None: + """Ensure that the sensitive information is not leaked in the traceback.""" + my_response = requests.Response() + my_response.status_code = 404 + my_response.url = 'not_found' + + aim_request_mock = mocker.Mock( + autospec=True, + name='aim_request', + return_value=my_response, + ) + monkeypatch.setattr(aim.requests, 'get', aim_request_mock) + + with pytest.raises( + requests.exceptions.HTTPError, + match=expected_url_in_exc, + ) as e: + aim.aim_backend( + url='http://testurl.com', + app_id='foobar123', + object_query='foobar123', + object_query_format='test', + reason=reason, + verify=True, + ) + + assert e.value.response.url == expected_response_url_literal + assert 'foobar123' not in str(e)