Skip to content

Commit 1107e9a

Browse files
committed
Allow configuring local resolved hosts
1 parent 1c5c23c commit 1107e9a

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

src/slicer_uri_bridge/handler.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ def load_config() -> dict:
122122
logger.warning("Invalid security.allow_any_original_host; using false")
123123
security["allow_any_original_host"] = False
124124

125+
if not isinstance(security.get("allow_local_resolved_hosts", False), bool):
126+
logger.warning("Invalid security.allow_local_resolved_hosts; using false")
127+
security["allow_local_resolved_hosts"] = False
128+
125129
security["post_process_action"] = normalize_post_process_action(security.get("post_process_action"))
126130

127131
allowed_hosts = security.get("allowed_hosts", [])
@@ -312,6 +316,7 @@ def validate_remote_url(
312316
allow_any_original_host: bool,
313317
allow_plain_http: bool,
314318
check_allowlist: bool,
319+
allow_local_resolved_hosts: bool,
315320
) -> None:
316321
parsed = urllib.parse.urlsplit(url)
317322
if not parsed.scheme or not parsed.netloc:
@@ -331,7 +336,8 @@ def validate_remote_url(
331336
if normalize_host(host) not in allowed_hosts:
332337
raise BridgeError(f"Download host is not allow-listed: {host}")
333338

334-
assert_public_host(host)
339+
if not allow_local_resolved_hosts:
340+
assert_public_host(host)
335341

336342

337343
def download_folder_from_config(config: dict) -> Path | None:
@@ -410,6 +416,7 @@ def download_model(
410416
allowed_hosts: set[str],
411417
allow_any_original_host: bool,
412418
allow_plain_http: bool,
419+
allow_local_resolved_hosts: bool,
413420
) -> Path:
414421
opener = urllib.request.build_opener(NoRedirectHandler())
415422
current_url = initial_url
@@ -422,6 +429,7 @@ def download_model(
422429
allow_any_original_host=allow_any_original_host,
423430
allow_plain_http=allow_plain_http,
424431
check_allowlist=redirect_index == 0,
432+
allow_local_resolved_hosts=allow_local_resolved_hosts,
425433
)
426434

427435
request = urllib.request.Request(
@@ -701,6 +709,7 @@ def main(argv: list[str] | None = None) -> int:
701709
config = load_config()
702710
security = config["security"]
703711
allow_plain_http = security.get("allow_plain_http", False)
712+
allow_local_resolved_hosts = security.get("allow_local_resolved_hosts", False)
704713
allowed_extensions = security["allowed_extensions"]
705714
download_folder = download_folder_from_config(config)
706715

@@ -722,6 +731,7 @@ def main(argv: list[str] | None = None) -> int:
722731
allow_any_original_host=allow_any_original_host,
723732
allow_plain_http=allow_plain_http,
724733
check_allowlist=True,
734+
allow_local_resolved_hosts=allow_local_resolved_hosts,
725735
)
726736

727737
command = resolve_bambu_command(config)
@@ -734,6 +744,7 @@ def main(argv: list[str] | None = None) -> int:
734744
allowed_hosts=allowed_hosts,
735745
allow_any_original_host=allow_any_original_host,
736746
allow_plain_http=allow_plain_http,
747+
allow_local_resolved_hosts=allow_local_resolved_hosts,
737748
)
738749
validate_downloaded_file(local_path)
739750
check_3mf_post_process(local_path, security["post_process_action"])

src/slicer_uri_bridge/resources/default_config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[security]
44
allow_plain_http = false
55
allow_any_original_host = true
6+
allow_local_resolved_hosts = false
67
allowed_extensions = [".3mf", ".stl", ".step", ".stp", ".obj"]
78
# 3MF files can contain Bambu/Orca post-processing commands.
89
# Values: "ignore", "warn" (show a warning and open), or "block" (refuse to open).

tests/test_handler.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ def test_validate_remote_url_checks_allowlist_and_public_host(self) -> None:
213213
allow_any_original_host=False,
214214
allow_plain_http=False,
215215
check_allowlist=True,
216+
allow_local_resolved_hosts=False,
216217
)
217218

218219
assert_public_host.assert_called_once_with("files.example")
@@ -225,6 +226,7 @@ def test_validate_remote_url_rejects_plain_http_by_default(self) -> None:
225226
allow_any_original_host=False,
226227
allow_plain_http=False,
227228
check_allowlist=True,
229+
allow_local_resolved_hosts=False,
228230
)
229231

230232
def test_validate_remote_url_rejects_embedded_credentials(self) -> None:
@@ -235,6 +237,7 @@ def test_validate_remote_url_rejects_embedded_credentials(self) -> None:
235237
allow_any_original_host=False,
236238
allow_plain_http=False,
237239
check_allowlist=True,
240+
allow_local_resolved_hosts=False,
238241
)
239242

240243
def test_validate_remote_url_skips_allowlist_for_redirect_targets(self) -> None:
@@ -245,10 +248,24 @@ def test_validate_remote_url_skips_allowlist_for_redirect_targets(self) -> None:
245248
allow_any_original_host=False,
246249
allow_plain_http=False,
247250
check_allowlist=False,
251+
allow_local_resolved_hosts=False,
248252
)
249253

250254
assert_public_host.assert_called_once_with("cdn.example")
251255

256+
def test_validate_remote_url_can_allow_local_resolved_hosts(self) -> None:
257+
with patch("slicer_uri_bridge.handler.assert_public_host") as assert_public_host:
258+
validate_remote_url(
259+
"https://localhost/model.3mf",
260+
allowed_hosts={"localhost"},
261+
allow_any_original_host=False,
262+
allow_plain_http=False,
263+
check_allowlist=True,
264+
allow_local_resolved_hosts=True,
265+
)
266+
267+
assert_public_host.assert_not_called()
268+
252269

253270
class FileValidationTests(unittest.TestCase):
254271
def test_validate_downloaded_file_accepts_non_empty_model_payload(self) -> None:
@@ -402,6 +419,7 @@ def test_load_config_defaults_post_process_action_to_warn(self) -> None:
402419
config = load_config()
403420

404421
self.assertEqual(config["security"]["post_process_action"], "warn")
422+
self.assertFalse(config["security"].get("allow_local_resolved_hosts", False))
405423

406424

407425
class ProtocolFileTests(unittest.TestCase):

0 commit comments

Comments
 (0)