mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-03 14:39:56 +08:00
perf(security): make image proxy signature stable to enable client caching (#5835)
This commit is contained in:
@@ -27,7 +27,8 @@ class SecurityUtilsTest(TestCase):
|
||||
|
||||
signed_url = SecurityUtils.sign_url(url)
|
||||
|
||||
self.assertIn("#mp_exp=", signed_url)
|
||||
self.assertIn("#mp_sig=", signed_url)
|
||||
self.assertIn("mp_purpose=image-proxy", signed_url)
|
||||
self.assertEqual(SecurityUtils.verify_signed_url(signed_url), url)
|
||||
self.assertEqual(SecurityUtils.strip_url_signature(signed_url), url)
|
||||
|
||||
@@ -45,19 +46,50 @@ class SecurityUtilsTest(TestCase):
|
||||
|
||||
self.assertIsNone(SecurityUtils.verify_signed_url(tampered_url))
|
||||
|
||||
def test_signed_url_rejects_expired_signature(self):
|
||||
def test_signed_url_is_deterministic_for_same_inputs(self):
|
||||
"""
|
||||
已过期签名不能继续放行私网图片代理请求。
|
||||
相同 URL 与 RESOURCE_SECRET_KEY 多次签名结果必须完全一致,
|
||||
保证浏览器 / Service Worker 缓存能稳定命中。
|
||||
"""
|
||||
with patch("app.utils.security.time.time", return_value=1000):
|
||||
signed_url = SecurityUtils.sign_url(
|
||||
"http://192.168.1.50:8096/Items/abc/Images/Primary",
|
||||
expires_in=10,
|
||||
)
|
||||
url = "http://192.168.1.50:8096/Items/abc/Images/Primary"
|
||||
|
||||
with patch("app.utils.security.time.time", return_value=1011):
|
||||
first = SecurityUtils.sign_url(url)
|
||||
second = SecurityUtils.sign_url(url)
|
||||
|
||||
self.assertEqual(first, second)
|
||||
self.assertEqual(SecurityUtils.verify_signed_url(first), url)
|
||||
|
||||
def test_signed_url_invalidated_after_secret_rotation(self):
|
||||
"""
|
||||
`RESOURCE_SECRET_KEY` 变更(进程重启或运维显式轮换)后旧签名必须作废,
|
||||
作为签名长期有效模型的失效兜底。
|
||||
"""
|
||||
url = "http://192.168.1.50:8096/Items/abc/Images/Primary"
|
||||
|
||||
with patch(
|
||||
"app.utils.security.settings.RESOURCE_SECRET_KEY",
|
||||
"old-secret-value-aaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
):
|
||||
signed_url = SecurityUtils.sign_url(url)
|
||||
self.assertEqual(SecurityUtils.verify_signed_url(signed_url), url)
|
||||
|
||||
with patch(
|
||||
"app.utils.security.settings.RESOURCE_SECRET_KEY",
|
||||
"new-secret-value-bbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
):
|
||||
self.assertIsNone(SecurityUtils.verify_signed_url(signed_url))
|
||||
|
||||
def test_signed_url_rejects_other_purpose(self):
|
||||
"""
|
||||
签名绑定 `purpose`,挪用到其它签名用途必须被拒绝。
|
||||
"""
|
||||
url = "http://192.168.1.50:8096/Items/abc/Images/Primary"
|
||||
signed_url = SecurityUtils.sign_url(url)
|
||||
|
||||
self.assertIsNone(
|
||||
SecurityUtils.verify_signed_url(signed_url, purpose="other-purpose")
|
||||
)
|
||||
|
||||
def test_is_safe_url_keeps_default_allowlist_behavior(self):
|
||||
"""
|
||||
默认 URL 校验保持历史 allowlist 行为,避免影响非代理调用方。
|
||||
|
||||
Reference in New Issue
Block a user