diff --git a/app/test/gitea-client.test.js b/app/test/gitea-client.test.js index 423ad4b..6b701a6 100644 --- a/app/test/gitea-client.test.js +++ b/app/test/gitea-client.test.js @@ -112,6 +112,16 @@ test('fetchAllPages 在 fetch 因逾時拋出 AbortError 時向外拋出', async ) }) +test('fetchAllPages 在頁數超過 MAX_PAGES 時中止以避免無限迴圈', async () => { + // 永遠回傳非空陣列,模擬 API 不以空陣列結尾的異常情形 + globalThis.fetch = async () => jsonResponse([{ id: 1 }]) + const client = new GiteaClient({}) + await assert.rejects( + () => client.fetchAllPages('https://example.com/api'), + /exceeded MAX_PAGES/, + ) +}) + test('deleteResource 回傳 HTTP 狀態碼', async () => { globalThis.fetch = async (url, opts) => { assert.equal(opts.method, 'DELETE') diff --git a/app/test/releases.test.js b/app/test/releases.test.js index 6c88821..0f3e53b 100644 --- a/app/test/releases.test.js +++ b/app/test/releases.test.js @@ -93,6 +93,16 @@ test('cleanupReleases 在 deleteResource 回傳非 204 時仍繼續處理其餘 assert.deepEqual(client.deleted, ['http://x/releases/2', 'http://x/releases/1']) }) +test('cleanupReleases 對 id 進行 URL 編碼(防路徑穿越)', async () => { + const client = fakeClient([ + { id: 1, tag_name: 'v2', name: 'n2', created_at: '2024-02-01T00:00:00Z' }, + { id: '../evil', tag_name: 'v1', name: 'n1', created_at: '2024-01-01T00:00:00Z' }, + ]) + await cleanupReleases(client, { releaseApiUrl: 'http://x/releases', keepCount: 1 }) + // 保留最新(id=1),刪除較舊者(惡意 id);id 中的 ../ 應被編碼 + assert.deepEqual(client.deleted, ['http://x/releases/..%2Fevil']) +}) + test('cleanupReleases 略過沒有 id 的 release', async () => { const client = fakeClient([ { id: 1, tag_name: 'v3', name: 'n3', created_at: '2024-03-01T00:00:00Z' }, diff --git a/app/test/tags.test.js b/app/test/tags.test.js index debbc1f..93c1988 100644 --- a/app/test/tags.test.js +++ b/app/test/tags.test.js @@ -86,3 +86,13 @@ test('cleanupOrphanTags 在沒有任何 release 時刪除所有具名 tag', asyn await cleanupOrphanTags(client, config) assert.deepEqual(client.deleted, ['http://x/tags/v2.0.0', 'http://x/tags/v1.0.0']) }) + +test('cleanupOrphanTags 對含特殊字元的 tag 名稱進行 URL 編碼(防路徑穿越)', async () => { + const client = fakeClient({ + releases: [], + tagList: [{ name: '../evil' }], + }) + await cleanupOrphanTags(client, config) + // 名稱中的 ../ 應被編碼,不會形成可穿越的路徑 + assert.deepEqual(client.deleted, ['http://x/tags/..%2Fevil']) +})