mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-01 17:51:49 +02:00
130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
import zipfile
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from services import geopolitics, updater
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_gdelt_caches():
|
|
geopolitics._article_title_cache.clear()
|
|
geopolitics._article_url_safety_cache.clear()
|
|
yield
|
|
geopolitics._article_title_cache.clear()
|
|
geopolitics._article_url_safety_cache.clear()
|
|
|
|
|
|
class TestGdeltArticleUrlSafety:
|
|
def test_safe_public_article_url_allows_public_dns(self, monkeypatch):
|
|
monkeypatch.setattr(
|
|
geopolitics.socket,
|
|
"getaddrinfo",
|
|
lambda *args, **kwargs: [(0, 0, 0, "", ("93.184.216.34", 443))],
|
|
)
|
|
|
|
allowed, reason = geopolitics._is_safe_public_article_url("https://example.com/story")
|
|
|
|
assert allowed is True
|
|
assert reason == ""
|
|
|
|
def test_safe_public_article_url_blocks_private_dns(self, monkeypatch):
|
|
monkeypatch.setattr(
|
|
geopolitics.socket,
|
|
"getaddrinfo",
|
|
lambda *args, **kwargs: [(0, 0, 0, "", ("10.0.0.7", 443))],
|
|
)
|
|
|
|
allowed, reason = geopolitics._is_safe_public_article_url("https://example.com/story")
|
|
|
|
assert allowed is False
|
|
assert reason == "private_dns"
|
|
|
|
def test_fetch_article_title_refuses_private_ip_without_request(self):
|
|
with patch("services.geopolitics.requests.get") as mock_get:
|
|
title = geopolitics._fetch_article_title("http://127.0.0.1/story")
|
|
|
|
assert title is None
|
|
mock_get.assert_not_called()
|
|
|
|
|
|
class TestUpdaterHardening:
|
|
def test_validate_update_url_allows_github_codeload(self):
|
|
url = "https://codeload.github.com/BigBodyCobain/Shadowbroker/zip/refs/tags/v1.2.3"
|
|
|
|
assert updater._validate_update_url(url) == url
|
|
|
|
def test_validate_update_url_rejects_untrusted_host(self):
|
|
with pytest.raises(RuntimeError, match="untrusted release host"):
|
|
updater._validate_update_url("https://evil.example.com/update.zip")
|
|
|
|
def test_extract_and_copy_rejects_zip_path_traversal(self, tmp_path):
|
|
zip_path = tmp_path / "bad.zip"
|
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
zf.writestr("../escape.txt", "nope")
|
|
|
|
with pytest.raises(RuntimeError, match="path traversal entry"):
|
|
updater._extract_and_copy(str(zip_path), str(tmp_path / "project"), str(tmp_path / "work"))
|
|
|
|
def test_perform_update_returns_manual_url_on_failure(self, monkeypatch, tmp_path):
|
|
def _boom(_temp_dir):
|
|
raise RuntimeError("update exploded")
|
|
|
|
monkeypatch.setattr(updater, "_download_release", _boom)
|
|
|
|
result = updater.perform_update(str(tmp_path))
|
|
|
|
assert result["status"] == "error"
|
|
assert result["manual_url"] == updater.GITHUB_RELEASES_PAGE_URL
|
|
assert "update exploded" in result["message"]
|
|
|
|
def test_perform_update_surfaces_release_metadata(self, monkeypatch, tmp_path):
|
|
release_url = "https://github.com/BigBodyCobain/Shadowbroker/releases/tag/v1.2.3"
|
|
download_url = (
|
|
"https://api.github.com/repos/BigBodyCobain/Shadowbroker/zipball/v1.2.3"
|
|
)
|
|
backup_path = tmp_path / "backup.zip"
|
|
|
|
(tmp_path / "frontend").mkdir()
|
|
(tmp_path / "backend").mkdir()
|
|
|
|
monkeypatch.setattr(
|
|
updater,
|
|
"_download_release",
|
|
lambda _temp_dir: ("dummy.zip", "v1.2.3", download_url, release_url),
|
|
)
|
|
monkeypatch.setattr(updater, "_validate_zip_hash", lambda _zip_path: None)
|
|
monkeypatch.setattr(updater, "_backup_current", lambda *_args: str(backup_path))
|
|
monkeypatch.setattr(updater, "_extract_and_copy", lambda *_args: 42)
|
|
|
|
result = updater.perform_update(str(tmp_path))
|
|
|
|
assert result["status"] == "ok"
|
|
assert result["version"] == "v1.2.3"
|
|
assert result["files_updated"] == 42
|
|
assert result["backup_path"] == str(backup_path)
|
|
assert result["manual_url"] == release_url
|
|
assert result["release_url"] == release_url
|
|
assert result["download_url"] == download_url
|
|
|
|
def test_perform_update_returns_manual_for_non_source_runtime(self, monkeypatch, tmp_path):
|
|
release_url = "https://github.com/BigBodyCobain/Shadowbroker/releases/tag/v1.2.3"
|
|
download_url = (
|
|
"https://api.github.com/repos/BigBodyCobain/Shadowbroker/zipball/v1.2.3"
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
updater,
|
|
"_download_release",
|
|
lambda _temp_dir: ("dummy.zip", "v1.2.3", download_url, release_url),
|
|
)
|
|
|
|
result = updater.perform_update(str(tmp_path))
|
|
|
|
assert result["status"] == "manual"
|
|
assert result["version"] == "v1.2.3"
|
|
assert result["manual_url"] == release_url
|
|
assert result["release_url"] == release_url
|
|
assert result["download_url"] == download_url
|
|
assert "does not support in-place source updates" in result["message"]
|