Skip to content
Merged
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)
- Fixed secure `user`/`password` properties not being loaded for team-config profiles nested more than one level deep which caused 401 errors. [#411](https://github.com/zowe/zowe-client-python-sdk/pull/411)
- 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)
Expand Down
19 changes: 8 additions & 11 deletions src/core/zowe/core_for_zowe_sdk/config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
from typing import Any, NamedTuple, Optional, Union

import json5
import requests

from .credential_manager import CredentialManager
from .custom_warnings import ProfileNotFoundWarning, ProfileParsingWarning
from .exceptions import ProfileNotFound
from .logger import Log
from .profile_constants import GLOBAL_CONFIG_NAME, TEAM_CONFIG, USER_CONFIG
from .validators import validate_config_json
from .validators import REMOTE_SCHEMA_UNSUPPORTED, validate_config_json

HOME = os.path.expanduser("~")
GLOBAL_CONFIG_LOCATION = os.path.join(HOME, ".zowe")
Expand Down Expand Up @@ -170,6 +169,11 @@ def schema_list(self, cwd: Optional[str] = None) -> list[dict[str, Any]]:
-------
list[dict[str, Any]]
properties from schema

Raises
------
ValueError
When the $schema property points to a remote URL, which is not supported
"""
schema: Optional[Union[str, dict[str, Any]]] = self.schema_property

Expand All @@ -180,15 +184,8 @@ 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
raise ValueError(f"{REMOTE_SCHEMA_UNSUPPORTED}: {schema}")

elif schema.startswith("file://") or os.path.isfile(schema):
try:
Expand Down
17 changes: 13 additions & 4 deletions src/core/zowe/core_for_zowe_sdk/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
"""

import os
from typing import Union, Any
from typing import Any, Union

import json5
import requests
from jsonschema import validate

REMOTE_SCHEMA_UNSUPPORTED = "Loading a JSON schema from a remote URL is not supported"


def validate_config_json(path_config_json: Union[str, dict[str, Any]], path_schema_json: str, cwd: str) -> None:
"""
Expand All @@ -30,10 +31,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"{REMOTE_SCHEMA_UNSUPPORTED}: {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()
12 changes: 12 additions & 0 deletions tests/unit/core/test_profile_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,18 @@ 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 raises an error 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.assertRaises(ValueError):
config_file.schema_list()

mock_get.assert_not_called()

@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