From b56fa83fe3036c40b1e9ff000cef1646ec52ba3c Mon Sep 17 00:00:00 2001 From: Stacy Olivas Date: Sun, 5 Apr 2026 19:38:42 -0700 Subject: [PATCH] fix: remove cross-branch security test classes from radio-reliability PR --- tests/test_web_viewer.py | 160 --------------------------------------- 1 file changed, 160 deletions(-) diff --git a/tests/test_web_viewer.py b/tests/test_web_viewer.py index bd3b535..8a6359e 100644 --- a/tests/test_web_viewer.py +++ b/tests/test_web_viewer.py @@ -2920,166 +2920,6 @@ class TestDbPathResolutionFromConfigDir: ) -# =========================================================================== -# Security: Restore endpoint path traversal prevention (GAP W1) -# =========================================================================== - - -class TestRestoreEndpointSecurity: - """POST /api/maintenance/restore must reject path traversal attacks. - - Covers GAP W1: validate_safe_path() added before shutil.copy2(). - Dangerous system directories and traversal patterns must return 400. - """ - - def test_etc_passwd_path_rejected(self, client): - """/etc/passwd supplied as db_file must be blocked.""" - resp = client.post( - "/api/maintenance/restore", - json={"db_file": "/etc/passwd"}, - content_type="application/json", - ) - assert resp.status_code == 400 - data = resp.get_json() - assert "error" in data - - def test_etc_shadow_path_rejected(self, client): - """/etc/shadow must be blocked (credential file).""" - resp = client.post( - "/api/maintenance/restore", - json={"db_file": "/etc/shadow"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_proc_self_environ_rejected(self, client): - """/proc/self/environ must be blocked (process secrets).""" - resp = client.post( - "/api/maintenance/restore", - json={"db_file": "/proc/self/environ"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_empty_db_file_rejected(self, client): - """Empty db_file must return 400.""" - resp = client.post( - "/api/maintenance/restore", - json={"db_file": ""}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_missing_db_file_key_rejected(self, client): - """Missing db_file key must return 400.""" - resp = client.post( - "/api/maintenance/restore", - json={}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_validate_safe_path_called_for_restore(self, client): - """validate_safe_path is invoked — not bypassed — in the restore handler.""" - with patch("modules.web_viewer.app.validate_safe_path", return_value=None) as mock_vsp: - resp = client.post( - "/api/maintenance/restore", - json={"db_file": "/tmp/test.db"}, - content_type="application/json", - ) - mock_vsp.assert_called_once() - assert resp.status_code == 400 # None returned → path rejected - - -# =========================================================================== -# Security: Feed preview/test SSRF prevention (GAP W2) -# =========================================================================== - - -class TestFeedPreviewSecurity: - """POST /api/feeds/preview and /api/feeds/test must reject SSRF-able URLs. - - Covers GAP W2: validate_external_url() added before any outbound fetch. - Private IPs, loopback, and file:// schemes must return 400. - """ - - def test_metadata_endpoint_blocked_in_preview(self, client): - """169.254.169.254 (cloud metadata) must be blocked in feed preview.""" - resp = client.post( - "/api/feeds/preview", - json={"feed_url": "http://169.254.169.254/latest/meta-data/", "feed_type": "rss"}, - content_type="application/json", - ) - assert resp.status_code == 400 - data = resp.get_json() - assert "error" in data - - def test_loopback_blocked_in_preview(self, client): - """127.0.0.1 loopback must be blocked in feed preview.""" - resp = client.post( - "/api/feeds/preview", - json={"feed_url": "http://127.0.0.1/internal", "feed_type": "rss"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_file_scheme_blocked_in_preview(self, client): - """file:// scheme must be blocked in feed preview.""" - resp = client.post( - "/api/feeds/preview", - json={"feed_url": "file:///etc/passwd", "feed_type": "rss"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_private_network_blocked_in_preview(self, client): - """10.x.x.x (private RFC1918) must be blocked in feed preview.""" - resp = client.post( - "/api/feeds/preview", - json={"feed_url": "http://10.0.0.1/feed.xml", "feed_type": "rss"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_missing_feed_url_rejected(self, client): - """Missing feed_url key must return 400.""" - resp = client.post( - "/api/feeds/preview", - json={"feed_type": "rss"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_metadata_endpoint_blocked_in_test_route(self, client): - """169.254.169.254 must be blocked in /api/feeds/test as well.""" - resp = client.post( - "/api/feeds/test", - json={"url": "http://169.254.169.254/metadata"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_loopback_blocked_in_test_route(self, client): - """127.0.0.1 must be blocked in /api/feeds/test.""" - resp = client.post( - "/api/feeds/test", - json={"url": "http://127.0.0.1/internal"}, - content_type="application/json", - ) - assert resp.status_code == 400 - - def test_validate_external_url_called_for_preview(self, client): - """validate_external_url is invoked — not bypassed — in the preview handler.""" - with patch("modules.web_viewer.app.validate_external_url", return_value=False) as mock_veu: - resp = client.post( - "/api/feeds/preview", - json={"feed_url": "http://192.168.1.1/feed.rss", "feed_type": "rss"}, - content_type="application/json", - ) - mock_veu.assert_called() - assert resp.status_code == 400 - - class TestRadioDebugConfig: """Tests for GET/POST /api/config/radio-debug endpoints."""