Handle all URL.parse cases with invalid URLs

This commit is contained in:
Lim Chee Aun 2025-03-21 18:23:13 +08:00
parent ffcfc29d8c
commit a3dd8bab46
9 changed files with 388 additions and 380 deletions

View file

@ -236,7 +236,9 @@ function AccountInfo({
const accountInstance = useMemo(() => { const accountInstance = useMemo(() => {
if (!url) return null; if (!url) return null;
const domain = punycode.toUnicode(URL.parse(url).hostname); const hostname = URL.parse(url)?.hostname;
if (!hostname) return null;
const domain = punycode.toUnicode(hostname);
return domain; return domain;
}, [url]); }, [url]);
@ -1843,6 +1845,7 @@ function lightenRGB([r, g, b]) {
function niceAccountURL(url) { function niceAccountURL(url) {
if (!url) return; if (!url) return;
const urlObj = URL.parse(url); const urlObj = URL.parse(url);
if (!urlObj) return;
const { host, pathname } = urlObj; const { host, pathname } = urlObj;
const path = pathname.replace(/\/$/, '').replace(/^\//, ''); const path = pathname.replace(/\/$/, '').replace(/^\//, '');
return ( return (

View file

@ -61,19 +61,21 @@ function AccountSheet({ account, instance: propInstance, onClose }) {
return result.accounts[0]; return result.accounts[0];
} else if (/https?:\/\/[^/]+\/@/.test(account)) { } else if (/https?:\/\/[^/]+\/@/.test(account)) {
const accountURL = URL.parse(account); const accountURL = URL.parse(account);
const { hostname, pathname } = accountURL; if (accountURL) {
const acct = const { hostname, pathname } = accountURL;
pathname.replace(/^\//, '').replace(/\/$/, '') + const acct =
'@' + pathname.replace(/^\//, '').replace(/\/$/, '') +
hostname; '@' +
const result = await masto.v2.search.fetch({ hostname;
q: acct, const result = await masto.v2.search.fetch({
type: 'accounts', q: acct,
limit: 1, type: 'accounts',
resolve: authenticated, limit: 1,
}); resolve: authenticated,
if (result.accounts.length) { });
return result.accounts[0]; if (result.accounts.length) {
return result.accounts[0];
}
} }
} }
} }

View file

@ -46,6 +46,7 @@ import emojifyText from '../utils/emojify-text';
import enhanceContent from '../utils/enhance-content'; import enhanceContent from '../utils/enhance-content';
import FilterContext from '../utils/filter-context'; import FilterContext from '../utils/filter-context';
import { isFiltered } from '../utils/filters'; import { isFiltered } from '../utils/filters';
import getDomain from '../utils/get-domain';
import getTranslateTargetLanguage from '../utils/get-translate-target-language'; import getTranslateTargetLanguage from '../utils/get-translate-target-language';
import getHTMLText from '../utils/getHTMLText'; import getHTMLText from '../utils/getHTMLText';
import handleContentLinks from '../utils/handle-content-links'; import handleContentLinks from '../utils/handle-content-links';
@ -2695,14 +2696,6 @@ function MediaFirstContainer(props) {
); );
} }
function getDomain(url) {
return punycode.toUnicode(
URL.parse(url)
.hostname.replace(/^www\./, '')
.replace(/\/$/, ''),
);
}
// "Post": Quote post + card link preview combo // "Post": Quote post + card link preview combo
// Assume all links from these domains are "posts" // Assume all links from these domains are "posts"
// Mastodon links are "posts" too but they are converted to real quote posts and there's too many domains to check // Mastodon links are "posts" too but they are converted to real quote posts and there's too many domains to check
@ -3520,6 +3513,7 @@ const StatusButton = forwardRef((props, ref) => {
function nicePostURL(url) { function nicePostURL(url) {
if (!url) return; if (!url) return;
const urlObj = URL.parse(url); const urlObj = URL.parse(url);
if (!urlObj) return;
const { host, pathname } = urlObj; const { host, pathname } = urlObj;
const path = pathname.replace(/\/$/, ''); const path = pathname.replace(/\/$/, '');
// split only first slash // split only first slash

692
src/locales/en.po generated

File diff suppressed because it is too large Load diff

View file

@ -467,7 +467,7 @@ function AccountStatuses() {
const accountInstance = useMemo(() => { const accountInstance = useMemo(() => {
if (!account?.url) return null; if (!account?.url) return null;
const domain = URL.parse(account.url).hostname; const domain = URL.parse(account.url)?.hostname;
return domain; return domain;
}, [account]); }, [account]);
const sameInstance = instance === accountInstance; const sameInstance = instance === accountInstance;

View file

@ -35,6 +35,7 @@ import { oklab2rgb, rgb2oklab } from '../utils/color-utils';
import db from '../utils/db'; import db from '../utils/db';
import emojifyText from '../utils/emojify-text'; import emojifyText from '../utils/emojify-text';
import { isFiltered } from '../utils/filters'; import { isFiltered } from '../utils/filters';
import getDomain from '../utils/get-domain';
import htmlContentLength from '../utils/html-content-length'; import htmlContentLength from '../utils/html-content-length';
import mem from '../utils/mem'; import mem from '../utils/mem';
import niceDateTime from '../utils/nice-date-time'; import niceDateTime from '../utils/nice-date-time';
@ -1171,11 +1172,7 @@ function Catchup() {
height, height,
publishedAt, publishedAt,
} = card; } = card;
const domain = punycode.toUnicode( const domain = getDomain(url);
URL.parse(url)
.hostname.replace(/^www\./, '')
.replace(/\/$/, ''),
);
let accentColor; let accentColor;
if (blurhash) { if (blurhash) {
const averageColor = getBlurHashAverageColor(blurhash); const averageColor = getBlurHashAverageColor(blurhash);

View file

@ -19,6 +19,7 @@ import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
import { oklab2rgb, rgb2oklab } from '../utils/color-utils'; import { oklab2rgb, rgb2oklab } from '../utils/color-utils';
import { filteredItems } from '../utils/filters'; import { filteredItems } from '../utils/filters';
import getDomain from '../utils/get-domain';
import pmem from '../utils/pmem'; import pmem from '../utils/pmem';
import shortenNumber from '../utils/shorten-number'; import shortenNumber from '../utils/shorten-number';
import states, { saveStatus } from '../utils/states'; import states, { saveStatus } from '../utils/states';
@ -252,11 +253,7 @@ function Trending({ columnMode, ...props }) {
: null; : null;
const isShortTitle = title.length < 30; const isShortTitle = title.length < 30;
const hasAuthor = !!(authorName || author); const hasAuthor = !!(authorName || author);
const domain = punycode.toUnicode( const domain = getDomain(url);
URL.parse(url)
.hostname.replace(/^www\./, '')
.replace(/\/$/, ''),
);
let accentColor; let accentColor;
if (blurhash) { if (blurhash) {
const averageColor = getBlurHashAverageColor(blurhash); const averageColor = getBlurHashAverageColor(blurhash);

13
src/utils/get-domain.js Normal file
View file

@ -0,0 +1,13 @@
import punycode from 'punycode/';
export default function getDomain(url) {
try {
return punycode.toUnicode(
URL.parse(url)
.hostname.replace(/^www\./, '')
.replace(/\/$/, ''),
);
} catch (e) {
return ''; // just give up
}
}

View file

@ -12,7 +12,9 @@ const statusPostRegexes = [
export function getInstanceStatusObject(url) { export function getInstanceStatusObject(url) {
// Regex /:username/:id, where username = @username or @username@domain, id = anything // Regex /:username/:id, where username = @username or @username@domain, id = anything
const { hostname, pathname } = URL.parse(url); const theURL = URL.parse(url);
if (!theURL) return {};
const { hostname, pathname } = theURL;
// const [, username, domain, id] = pathname.match(statusRegex) || []; // const [, username, domain, id] = pathname.match(statusRegex) || [];
for (const regex of statusPostRegexes) { for (const regex of statusPostRegexes) {
const [, id] = pathname.match(regex) || []; const [, id] = pathname.match(regex) || [];