Skip to content

Commit 1b9738c

Browse files
committed
fix: validate release payload shape before parsing
1 parent 7479038 commit 1b9738c

2 files changed

Lines changed: 99 additions & 16 deletions

File tree

astrbot/core/zip_updator.py

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,56 @@ def __init__(self, repo_mirror: str = "") -> None:
4848
self.repo_mirror = repo_mirror
4949
self.rm_on_error = on_error
5050

51+
@staticmethod
52+
def _normalize_release_payload(result: object, url: str) -> list:
53+
if isinstance(result, dict):
54+
releases = [result]
55+
elif isinstance(result, list):
56+
releases = result
57+
else:
58+
logger.error(
59+
f"版本信息格式异常,期望列表或字典,实际为: {type(result).__name__}, url: {url}",
60+
)
61+
raise FetchReleaseError("版本信息格式异常")
62+
63+
if not releases:
64+
return []
65+
66+
required_fields = (
67+
"name",
68+
"published_at",
69+
"body",
70+
"tag_name",
71+
"zipball_url",
72+
)
73+
normalized = []
74+
for idx, release in enumerate(releases):
75+
if not isinstance(release, dict):
76+
logger.error(
77+
f"版本信息第 {idx} 项格式异常,期望字典,实际为: {type(release).__name__}, url: {url}",
78+
)
79+
raise FetchReleaseError("版本信息格式异常")
80+
81+
missing_fields = [
82+
field for field in required_fields if field not in release
83+
]
84+
if missing_fields:
85+
logger.error(
86+
f"版本信息第 {idx} 项缺少字段: {missing_fields}, url: {url}",
87+
)
88+
raise FetchReleaseError("版本信息字段缺失")
89+
90+
normalized.append(
91+
{
92+
"version": release["name"] or release["tag_name"],
93+
"published_at": release["published_at"],
94+
"body": release["body"],
95+
"tag_name": release["tag_name"],
96+
"zipball_url": release["zipball_url"],
97+
},
98+
)
99+
return normalized
100+
51101
async def fetch_release_info(self, url: str) -> list:
52102
"""请求版本信息。
53103
返回一个列表,每个元素是一个字典,包含版本号、发布时间、更新内容、commit hash等信息。
@@ -80,22 +130,7 @@ async def fetch_release_info(self, url: str) -> list:
80130
logger.error(f"解析版本信息时发生异常: {e}")
81131
raise FetchReleaseError("解析版本信息失败") from e
82132

83-
if isinstance(result, dict):
84-
result = [result]
85-
if not result:
86-
return []
87-
ret = []
88-
for release in result:
89-
ret.append(
90-
{
91-
"version": release["name"],
92-
"published_at": release["published_at"],
93-
"body": release["body"],
94-
"tag_name": release["tag_name"],
95-
"zipball_url": release["zipball_url"],
96-
},
97-
)
98-
return ret
133+
return self._normalize_release_payload(result, url)
99134

100135
def unzip(self) -> NoReturn:
101136
raise NotImplementedError

tests/unit/test_updator.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,54 @@
44
from astrbot.core.zip_updator import FetchReleaseError, RepoZipUpdator
55

66

7+
def test_normalize_release_payload_raises_on_missing_fields():
8+
updator = RepoZipUpdator()
9+
malformed_payload = [
10+
{
11+
"name": "v1.0.0",
12+
"published_at": "2026-03-01T00:00:00Z",
13+
"tag_name": "v1.0.0",
14+
"zipball_url": "https://example.com/v1.0.0.zip",
15+
}
16+
]
17+
18+
with pytest.raises(FetchReleaseError, match="版本信息字段缺失"):
19+
updator._normalize_release_payload(
20+
malformed_payload,
21+
"https://example.invalid/releases",
22+
)
23+
24+
25+
def test_normalize_release_payload_raises_on_invalid_item_type():
26+
updator = RepoZipUpdator()
27+
28+
with pytest.raises(FetchReleaseError, match="版本信息格式异常"):
29+
updator._normalize_release_payload(
30+
["invalid-release-item"],
31+
"https://example.invalid/releases",
32+
)
33+
34+
35+
def test_normalize_release_payload_accepts_valid_payload():
36+
updator = RepoZipUpdator()
37+
payload = {
38+
"name": "v1.0.0",
39+
"published_at": "2026-03-01T00:00:00Z",
40+
"body": "release body",
41+
"tag_name": "v1.0.0",
42+
"zipball_url": "https://example.com/v1.0.0.zip",
43+
}
44+
45+
releases = updator._normalize_release_payload(
46+
payload,
47+
"https://example.invalid/releases",
48+
)
49+
50+
assert len(releases) == 1
51+
assert releases[0]["tag_name"] == "v1.0.0"
52+
assert releases[0]["version"] == "v1.0.0"
53+
54+
755
@pytest.mark.asyncio
856
async def test_update_supports_nightly_tag(monkeypatch, tmp_path):
957
updator = AstrBotUpdator()

0 commit comments

Comments
 (0)