diff --git a/airbyte-cdk/python/CHANGELOG.md b/airbyte-cdk/python/CHANGELOG.md index d84eb7015181..81110d4269ec 100644 --- a/airbyte-cdk/python/CHANGELOG.md +++ b/airbyte-cdk/python/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.1.18 +No longer prints full config files on validation error to prevent exposing secrets to log file: https://github.com/airbytehq/airbyte/pull/5879 + ## 0.1.17 Fix mismatching between number of records actually read and number of records in logs by 1: https://github.com/airbytehq/airbyte/pull/5767 diff --git a/airbyte-cdk/python/airbyte_cdk/sources/utils/schema_helpers.py b/airbyte-cdk/python/airbyte_cdk/sources/utils/schema_helpers.py index c687c8272a8c..2cc15730342a 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/utils/schema_helpers.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/utils/schema_helpers.py @@ -142,7 +142,7 @@ def check_config_against_spec_or_exit(config: Mapping[str, Any], spec: Connector try: validate(instance=config, schema=spec_schema) except ValidationError as validation_error: - raise Exception("Config validation error: " + validation_error.message) + raise Exception("Config validation error: " + validation_error.message) from None class InternalConfig(BaseModel): @@ -159,7 +159,8 @@ def split_config(config: Mapping[str, Any]) -> Tuple[dict, InternalConfig]: Break config map object into 2 instances: first is a dict with user defined configuration and second is internal config that contains private keys for acceptance test configuration. - :param config - Dict object that has been loaded from config file. + :param + config - Dict object that has been loaded from config file. :return tuple of user defined config dict with filtered out internal parameters and SAT internal config object. """ diff --git a/airbyte-cdk/python/setup.py b/airbyte-cdk/python/setup.py index 735d5dc6253c..7e5223f33a26 100644 --- a/airbyte-cdk/python/setup.py +++ b/airbyte-cdk/python/setup.py @@ -35,7 +35,7 @@ setup( name="airbyte-cdk", - version="0.1.17", + version="0.1.18", description="A framework for writing Airbyte Connectors.", long_description=README, long_description_content_type="text/markdown", diff --git a/airbyte-cdk/python/unit_tests/sources/utils/test_schema_helpers.py b/airbyte-cdk/python/unit_tests/sources/utils/test_schema_helpers.py index 8e9b4404c9b1..48595d419639 100644 --- a/airbyte-cdk/python/unit_tests/sources/utils/test_schema_helpers.py +++ b/airbyte-cdk/python/unit_tests/sources/utils/test_schema_helpers.py @@ -27,11 +27,18 @@ import os import shutil import sys +import traceback from collections.abc import Mapping from pathlib import Path -from airbyte_cdk.sources.utils.schema_helpers import ResourceSchemaLoader +from airbyte_cdk.logger import AirbyteLogger +from airbyte_cdk.models.airbyte_protocol import ConnectorSpecification +from airbyte_cdk.sources.utils.schema_helpers import ResourceSchemaLoader, check_config_against_spec_or_exit from pytest import fixture +from pytest import raises as pytest_raises + +logger = AirbyteLogger() + MODULE = sys.modules[__name__] MODULE_NAME = MODULE.__name__.split(".")[0] @@ -53,6 +60,38 @@ def create_schema(name: str, content: Mapping): f.write(json.dumps(content)) +@fixture +def spec_object(): + spec = { + "connectionSpecification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": ["api_token"], + "additionalProperties": False, + "properties": { + "api_token": {"title": "API Token", "type": "string"}, + }, + }, + } + yield ConnectorSpecification.parse_obj(spec) + + +def test_check_config_against_spec_or_exit_does_not_print_schema(capsys, spec_object): + config = {"super_secret_token": "really_a_secret"} + with pytest_raises(SystemExit) as ex_info: + check_config_against_spec_or_exit(config, spec_object, logger) + exc = ex_info.value + traceback.print_exception(type(exc), exc, exc.__traceback__) + out, err = capsys.readouterr() + assert "really_a_secret" not in out + err + + +def test_should_not_fail_validation_for_valid_config(spec_object): + config = {"api_token": "something"} + check_config_against_spec_or_exit(config, spec_object, logger) + assert True, "should pass validation with valid config" + + class TestResourceSchemaLoader: # Test that a simple schema is loaded correctly @staticmethod diff --git a/airbyte-cdk/python/unit_tests/test_entrypoint.py b/airbyte-cdk/python/unit_tests/test_entrypoint.py index be6e628afd17..0fe100bea838 100644 --- a/airbyte-cdk/python/unit_tests/test_entrypoint.py +++ b/airbyte-cdk/python/unit_tests/test_entrypoint.py @@ -152,7 +152,7 @@ def config_mock(mocker, request): ], indirect=["config_mock"], ) -def test_config_validate(entrypoint: AirbyteEntrypoint, mocker, config_mock, schema, config_valid): +def test_config_validate(entrypoint: AirbyteEntrypoint, mocker, config_mock, schema, config_valid, capsys): parsed_args = Namespace(command="check", config="config_path") check_value = AirbyteConnectionStatus(status=Status.SUCCEEDED) mocker.patch.object(MockSource, "check", return_value=check_value) @@ -161,9 +161,10 @@ def test_config_validate(entrypoint: AirbyteEntrypoint, mocker, config_mock, sch messages = list(entrypoint.run(parsed_args)) assert [_wrap_message(check_value)] == messages else: - with pytest.raises(Exception) as ex_info: + with pytest.raises(SystemExit): list(entrypoint.run(parsed_args)) - assert "Config validation error:" in str(ex_info.value) + out, err = capsys.readouterr() + assert "Config validation error:" in out + err def test_run_check(entrypoint: AirbyteEntrypoint, mocker, spec_mock, config_mock):