diff --git a/CHANGELOG.md b/CHANGELOG.md index d579cf23..c69d9167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to the Zowe Client Python SDK will be documented in this fil ### Bug Fixes +- **Breaking**: Removed support for loading a JSON schema from a remote `http(s)://` URL via the `$schema` config property. Local schema files are still supported. [#412](https://github.com/zowe/zowe-client-python-sdk/pull/412) - Redacted request headers and restricted log directory/file to owner-only access. [#404](https://github.com/zowe/zowe-client-python-sdk/pull/404) - Fixed `Jobs.get_job_output_as_files` writing to a directory it never created, and made job output paths stay within the target directory. [#403](https://github.com/zowe/zowe-client-python-sdk/pull/403) - Updated the `pyo3` dependency of the Secrets SDK for technical currency. [#399](https://github.com/zowe/zowe-client-python-sdk/pull/399) diff --git a/src/core/zowe/core_for_zowe_sdk/config_file.py b/src/core/zowe/core_for_zowe_sdk/config_file.py index 82bc14d4..5d929c52 100644 --- a/src/core/zowe/core_for_zowe_sdk/config_file.py +++ b/src/core/zowe/core_for_zowe_sdk/config_file.py @@ -19,7 +19,6 @@ from typing import Any, NamedTuple, Optional, Union import json5 -import requests from .credential_manager import CredentialManager from .custom_warnings import ProfileNotFoundWarning, ProfileParsingWarning @@ -180,15 +179,11 @@ def schema_list(self, cwd: Optional[str] = None) -> list[dict[str, Any]]: schema_json: dict[str, Any] = {} if schema.startswith(("https://", "http://")): - try: - response = requests.get(schema) - response.raise_for_status() # Ensure it's a valid response - schema_json = response.json() - except requests.RequestException as e: - if not self.__suppress_config_file_warnings: - warnings.warn(f"Invalid schema request: {e}") - self.__logger.warning(f"Invalid schema request: {e}") - return [] + # remote schema loading is not supported + if not self.__suppress_config_file_warnings: + warnings.warn(f"Loading a JSON schema from a remote URL is not supported: {schema}") + self.__logger.warning(f"Loading a JSON schema from a remote URL is not supported: {schema}") + return [] elif schema.startswith("file://") or os.path.isfile(schema): try: diff --git a/src/core/zowe/core_for_zowe_sdk/validators.py b/src/core/zowe/core_for_zowe_sdk/validators.py index f340c75f..9a2b683c 100644 --- a/src/core/zowe/core_for_zowe_sdk/validators.py +++ b/src/core/zowe/core_for_zowe_sdk/validators.py @@ -14,7 +14,6 @@ from typing import Union, Any import json5 -import requests from jsonschema import validate @@ -30,10 +29,18 @@ def validate_config_json(path_config_json: Union[str, dict[str, Any]], path_sche Absolute path to zowe.schema.json cwd: str Path of the current working directory + + Raises + ------ + ValueError + When path_schema_json is a remote URL, which is not supported """ - # checks if the path_schema_json point to an internet URI and download the schema using the URI + # remote ($schema pointing to an http(s):// URL) schema loading is not supported; only local files may be used if path_schema_json.startswith("https://") or path_schema_json.startswith("http://"): - schema_json = requests.get(path_schema_json).json() + raise ValueError( + f"Loading a JSON schema from a remote URL is not supported: {path_schema_json}. " + "Use a local file path for the $schema property instead." + ) # checks if the path_schema_json is a file elif os.path.isfile(path_schema_json) or path_schema_json.startswith("file://"): diff --git a/tests/unit/core/test_config.py b/tests/unit/core/test_config.py index 2fe193df..b5878e6a 100644 --- a/tests/unit/core/test_config.py +++ b/tests/unit/core/test_config.py @@ -1,5 +1,6 @@ import importlib.util import os +from unittest import mock import json5 from jsonschema import ValidationError, validate @@ -136,6 +137,18 @@ def test_validate_config_json_with_block_comments(self): loaded_schema = json5.load(open(commented_schema_path, encoding="utf-8")) expected = validate(loaded_config, loaded_schema) - result = validate_config_json(commented_config_path, commented_schema_path, cwd=os.path.dirname(commented_config_path)) + result = validate_config_json( + commented_config_path, commented_schema_path, cwd=os.path.dirname(commented_config_path) + ) self.assertEqual(result, expected) + + def test_validate_config_json_rejects_remote_schema(self): + """Test validate_config_json rejects http(s):// schema URLs without making a network request.""" + with mock.patch("requests.get") as mock_get: + with self.assertRaises(ValueError): + validate_config_json(self.original_file_path, "https://example.com/zowe.schema.json", cwd=FIXTURES_PATH) + with self.assertRaises(ValueError): + validate_config_json(self.original_file_path, "http://example.com/zowe.schema.json", cwd=FIXTURES_PATH) + + mock_get.assert_not_called() diff --git a/tests/unit/core/test_profile_manager.py b/tests/unit/core/test_profile_manager.py index 1e797a43..ec98f7a9 100644 --- a/tests/unit/core/test_profile_manager.py +++ b/tests/unit/core/test_profile_manager.py @@ -296,6 +296,19 @@ def test_validate_schema_logger(self, get_pass_func, mock_logger_warning: mock.M config_file.validate_schema() self.assertEqual(mock_logger_warning.call_args[0][0], "Could not find $schema property") + def test_schema_list_rejects_remote_schema(self): + """Test that schema_list does not fetch a remote schema URL and returns an empty list instead.""" + with mock.patch("requests.get") as mock_get: + config_file = ConfigFile( + name="zowe_abcd", type="User Config", schema_property="https://example.com/zowe.schema.json" + ) + config_file.suppress_config_warnings(False) + with self.assertWarns(UserWarning): + result = config_file.schema_list() + + mock_get.assert_not_called() + self.assertEqual(result, []) + @mock.patch("zowe.secrets_for_zowe_sdk.keyring.get_password", side_effect=keyring_get_password_exception) def test_secure_props_loading_warning(self, get_pass_func): """