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
9 changes: 9 additions & 0 deletions sdk/python/feast/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ def from_error_detail(detail: str) -> Optional["FeastError"]:
module_name = m["module"]
class_name = m["class"]
message = m["message"]

# Security: Only allow imports from feast.* modules to prevent
# arbitrary code execution via malicious error responses.
if not module_name.startswith("feast."):
logger.warning(
f"Rejected error detail with non-feast module: {module_name}"
)
return None

module = importlib.import_module(module_name)
class_reference = getattr(module, class_name)

Expand Down
16 changes: 16 additions & 0 deletions sdk/python/tests/unit/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,19 @@ def test_error_error_detail():
def test_invalid_error_error_detail():
e = errors.FeastError.from_error_detail("invalid")
assertpy.assert_that(e).is_none()


def test_from_error_detail_rejects_non_feast_modules():
"""Security: from_error_detail should reject modules outside feast.*"""
malicious_detail = '{"module": "os", "class": "system", "message": "whoami"}'
e = errors.FeastError.from_error_detail(malicious_detail)
assertpy.assert_that(e).is_none()


def test_from_error_detail_allows_feast_modules():
"""from_error_detail should still work for legitimate feast errors."""
original = errors.FeatureViewNotFoundException("test")
detail = original.to_error_detail()
restored = errors.FeastError.from_error_detail(detail)
assertpy.assert_that(restored).is_not_none()
assertpy.assert_that(str(restored)).is_equal_to(str(original))
Loading