Share never hands over a private feed's file (#455)

Share on a paid episode's file gave the file's own address, which for
Patreon is patreon.com/api/rss/u/<your id>/e/<n>.mp3?sig=..., signed for
the subscriber: whoever got it could download the episode as them. A
private feed's episode is now shared by its page alone, from the file
too, and with no page there is nothing to share. The access warning
matched any patreon.com address, so it warned on a post's page, which
asks for a sign-in and carries nothing of yours; it now matches
Patreon's feed and file addresses only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqkrVA8TdTxe556JB3sR1n
This commit is contained in:
2026-11-13 17:34:17 +00:00
parent 45ef1e83f2
commit 757ce5fbb3
5 changed files with 35 additions and 7 deletions

View File

@@ -343,3 +343,12 @@ test('Share says why an episode gets no page anyone can play, and warns of a key
// The request failed outright: no reason known, but the key is still warned of.
assert.match(note(null, paid), /own access/);
});
test('a Patreon feed or file carries your access, a post page does not (#455)', () => {
const carries = (url: string) => run(`carriesAccess(${JSON.stringify(url)})`) as boolean;
assert.equal(carries('https://www.patreon.com/rss/glasscannon?auth=abc&show=1'), true);
assert.equal(carries('https://www.patreon.com/api/rss/u/H0Pm/e/174604106.mp3?sig=abc'), true);
assert.equal(carries('https://c10.patreonusercdn.com/ep.mp3?token-hash=abc'), true);
assert.equal(carries('https://www.patreon.com/glasscannon/posts/music-from-room-174604106'), false);
assert.equal(carries('https://example.com/ep.mp3'), false);
});

View File

@@ -2271,6 +2271,17 @@ test('Share hands the feed, and an episode as a link to its page on ipx-play (#4
await expect(page.locator('#shat')).toHaveCount(0);
await page.locator('#shlink').click();
await expect.poll(() => page.evaluate(() => window.shared.at(-1)?.url)).toBe('https://www.patreon.com/posts/1?auth=secret123');
// From its file too, the page alone: a paid file's address is signed for you (#455). A post's
// page carries nothing of yours, so no warning.
await page.evaluate(async () => {
const e = (await api('/api/feeds/paid-show/entries') as ListPage).entries[0];
const file = { id: 99, url: 'https://www.patreon.com/api/rss/u/H0Pm/e/1.mp3?sig=abc' } as Enclosure;
await shareEpisode({ ...e, link: 'https://www.patreon.com/glasscannon/posts/1', enclosures: [file] }, 99);
});
await expect(page.locator('#modalCard')).toContainText('its file is never shared');
await expect(page.locator('#modalCard')).not.toContainText('own access');
await page.locator('#shlink').click();
await expect.poll(() => page.evaluate(() => window.shared.at(-1)?.url)).toBe('https://www.patreon.com/glasscannon/posts/1');
});
test('a refresh button turns from the press to the end of the scan it asked for', async ({ page }) => {