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
8 changes: 8 additions & 0 deletions monai/auto3dseg/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,14 @@ def algo_from_json(filename: str, template_path: PathLike | None = None, **kwarg
if state_template_path:
algo_config["template_path"] = state_template_path

warnings.warn(
Comment thread
ericspod marked this conversation as resolved.
f"Loading {filename}: the file's `_target_` value is resolved to an imported callable and "
"invoked, and template directories from the file may be added to `sys.path`; only load "
"algo_object.json files from a source you trust "
"(see https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-2wx3-8x3w-r8qv).",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
stacklevel=2,
)

parser = ConfigParser(algo_config)
algo = parser.get_parsed_content()
used_template_path = path
Expand Down
35 changes: 35 additions & 0 deletions tests/apps/test_auto3dseg.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

from __future__ import annotations

import json
import os
import tempfile
import unittest
import warnings
from copy import deepcopy
from numbers import Number

Expand All @@ -36,6 +38,7 @@
SampleOperations,
SegSummarizer,
SummaryOperations,
algo_from_json,
datafold_read,
verify_report_format,
)
Expand Down Expand Up @@ -177,6 +180,20 @@ def __call__(self, data):
return d


class _DummyAlgo:
"""Minimal stand-in for an Auto3DSeg Algo object used in warning tests."""

def __init__(self) -> None:
self.template_path: str | None = None
self.output_path = os.getcwd()

def load_state_dict(self, state: dict) -> None:
pass

def get_output_path(self) -> str:
return self.output_path


class TestDataAnalyzer(unittest.TestCase):
def setUp(self):
self.test_dir = tempfile.TemporaryDirectory()
Expand Down Expand Up @@ -619,5 +636,23 @@ def tearDown(self) -> None:
self.test_dir.cleanup()


class TestAlgoFromJsonSecurityWarning(unittest.TestCase):
def test_warns_about_untrusted_target(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
algo_file = os.path.join(tmpdir, "algo_object.json")
with open(algo_file, "w", encoding="utf-8") as f:
json.dump({"_target_": f"{__name__}._DummyAlgo"}, f)

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
algo_from_json(algo_file)

messages = [str(w.message) for w in caught]
self.assertTrue(
any("algo_object.json" in msg and "trust" in msg for msg in messages),
f"Keywords 'algo_object.json' and 'trust' not found in warning messages: {messages}",
)


if __name__ == "__main__":
unittest.main()
Loading