fix(post form): block temporary media links

This commit is contained in:
Tommaso Casaburi
2026-05-16 16:11:22 +07:00
parent 70cf337895
commit a86c523d17
43 changed files with 164 additions and 38 deletions
+10
View File
@@ -10,6 +10,7 @@ vi.mock('../clipboard-utils', () => ({
import {
copyShareLinkToClipboard,
getExpiringMediaLinkHostname,
getHostname,
getPublishURLFilename,
is5chanLink,
@@ -62,6 +63,15 @@ describe('url-utils', () => {
expect(getPublishURLFilename('not-a-url')).toBeNull();
});
it('detects publish media hosts with temporary links', () => {
expect(getExpiringMediaLinkHostname('https://i.4cdn.org/gif/1712345678900.jpg')).toBe('i.4cdn.org');
expect(getExpiringMediaLinkHostname('http://litterbox.catbox.moe/u/example.png')).toBe('litterbox.catbox.moe');
expect(getExpiringMediaLinkHostname('https://www.tmpfiles.org/dl/123/file.mp4')).toBe('tmpfiles.org');
expect(getExpiringMediaLinkHostname('https://cdn.file.kiwi/example')).toBe('file.kiwi');
expect(getExpiringMediaLinkHostname('https://example.com/file.png')).toBeNull();
expect(getExpiringMediaLinkHostname('not-a-url')).toBeNull();
});
it('copies share links for threads and catalog pages using the production fallback base url', async () => {
await copyShareLinkToClipboard('music.eth', 'thread', 'cid-123');
expect(testState.copyToClipboardMock).toHaveBeenCalledWith('https://5chan.app/#/music.eth/thread/cid-123');
@@ -0,0 +1,8 @@
import { getExpiringMediaLinkHostname } from './url-utils';
type TranslateFn = (key: string, options?: Record<string, unknown>) => string;
export const getExpiringMediaLinkAlert = (url: string, t: TranslateFn): string | null => {
const expiringMediaLinkHostname = getExpiringMediaLinkHostname(url);
return expiringMediaLinkHostname ? `${t('error')}: ${t('expiring_media_link_alert', { domain: expiringMediaLinkHostname })}` : null;
};
+35
View File
@@ -67,6 +67,41 @@ export const normalizePublishURL = (url: string) => {
return trimmedUrl;
};
const EXPIRING_MEDIA_LINK_HOSTNAMES = [
// 4chan CDN media disappears when threads are pruned, usually after a few hours or days.
'i.4cdn.org',
// Litterbox temporary uploads can expire after 1 hour, 12 hours, 1 day, or 3 days.
'litterbox.catbox.moe',
// tmpfiles.org uploads expire after 60 minutes, 6 hours, 12 hours, or 24 hours.
'tmpfiles.org',
// Filebin uploads are deleted automatically after about 6 days.
'filebin.net',
// temp.sh files expire after 3 days.
'temp.sh',
// Termbin pastes are automatically deleted after 1 week.
'termbin.com',
// Uguu files expire after 3 hours.
'uguu.se',
// file.kiwi encrypted files are deleted after about 4 days by default.
'file.kiwi',
] as const;
const normalizeHostnameForMatching = (hostname: string) => hostname.toLowerCase().replace(/^www\./, '');
export const getExpiringMediaLinkHostname = (url: string): string | null => {
try {
const parsedUrl = new URL(normalizePublishURL(url));
if (parsedUrl.protocol !== 'https:') {
return null;
}
const hostname = normalizeHostnameForMatching(parsedUrl.hostname);
return EXPIRING_MEDIA_LINK_HOSTNAMES.find((expiringHostname) => hostname === expiringHostname || hostname.endsWith(`.${expiringHostname}`)) || null;
} catch {
return null;
}
};
export const isValidPublishURL = (url: string) => {
try {
return new URL(normalizePublishURL(url)).protocol === 'https:';