Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
15 changes: 5 additions & 10 deletions src/core/zowe/core_for_zowe_sdk/config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to be using this same string in multiple paces... wondering if we could move it somewhere else and reuse it.

self.__logger.warning(f"Loading a JSON schema from a remote URL is not supported: {schema}")
return []

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be raising the error regardless.

Right now, the only place that's calling the schema_list function is inside the get_env function, which is only called when the user specifies that they want to override_with_env.

If we return an empty list here, the developer/user may not realize that their environment variables were not loaded because the schema comes from a URL. This leads to unexpected behavior.


elif schema.startswith("file://") or os.path.isfile(schema):
try:
Expand Down
13 changes: 10 additions & 3 deletions src/core/zowe/core_for_zowe_sdk/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from typing import Union, Any

import json5
import requests
from jsonschema import validate


Expand All @@ -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://"):
Expand Down
15 changes: 14 additions & 1 deletion tests/unit/core/test_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import importlib.util
import os
from unittest import mock

import json5
from jsonschema import ValidationError, validate
Expand Down Expand Up @@ -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()
13 changes: 13 additions & 0 deletions tests/unit/core/test_profile_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down
Loading