0
packages/pl-fe/LICENSE-GPL-3.0
Normal file
0
packages/pl-fe/LICENSE-GPL-3.0
Normal file
@ -55,6 +55,7 @@
|
||||
"@lexical/utils": "^0.23.1",
|
||||
"@mkljczk/lexical-remark": "^0.4.0",
|
||||
"@mkljczk/react-hotkeys": "^1.2.2",
|
||||
"@mkljczk/url-purify": "^0.0.1",
|
||||
"@reach/combobox": "^0.18.0",
|
||||
"@reach/rect": "^0.18.0",
|
||||
"@reach/tabs": "^0.18.0",
|
||||
|
||||
@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
|
||||
|
||||
import Emojify from 'pl-fe/features/emoji/emojify';
|
||||
import { makeEmojiMap } from 'pl-fe/utils/normalizers';
|
||||
import Purify from 'pl-fe/utils/url-purify';
|
||||
|
||||
import HashtagLink from './hashtag-link';
|
||||
import HoverAccountWrapper from './hover-account-wrapper';
|
||||
@ -62,12 +63,12 @@ const uniqueHashtagsWithCaseHandling = (hashtags: string[]) => {
|
||||
};
|
||||
|
||||
function parseContent(props: IParsedContent): ReturnType<typeof domToReact>;
|
||||
function parseContent(props: IParsedContent, extractHashtags: true, greentext: boolean): {
|
||||
function parseContent(props: IParsedContent, extractHashtags: true, cleanUrls: boolean, greentext: boolean): {
|
||||
hashtags: Array<string>;
|
||||
content: ReturnType<typeof domToReact>;
|
||||
};
|
||||
|
||||
function parseContent({ html, mentions, hasQuote, emojis }: IParsedContent, extractHashtags = false, greentext = false) {
|
||||
function parseContent({ html, mentions, hasQuote, emojis }: IParsedContent, extractHashtags = false, cleanUrls = false, greentext = false) {
|
||||
if (html.length === 0) {
|
||||
return extractHashtags ? { content: null, hashtags: [] } : null;
|
||||
}
|
||||
@ -108,8 +109,9 @@ function parseContent({ html, mentions, hasQuote, emojis }: IParsedContent, extr
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<a
|
||||
{...domNode.attribs}
|
||||
href={cleanUrls ? Purify.clearUrl(domNode.attribs.href) : domNode.attribs.href}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
rel='nofollow noopener'
|
||||
rel='nofollow noopener noreferrer'
|
||||
target='_blank'
|
||||
title={domNode.attribs.href}
|
||||
>
|
||||
|
||||
@ -82,7 +82,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
||||
preview,
|
||||
withMedia,
|
||||
}) => {
|
||||
const { displaySpoilers } = useSettings();
|
||||
const { cleanUrls, displaySpoilers } = useSettings();
|
||||
const { greentext } = usePlFeConfig();
|
||||
|
||||
const [collapsed, setCollapsed] = useState<boolean | null>(null);
|
||||
@ -146,7 +146,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
||||
mentions: status.mentions,
|
||||
hasQuote: !!status.quote_id,
|
||||
emojis: status.emojis,
|
||||
}, true, greentext), [content]);
|
||||
}, true, cleanUrls, greentext), [content]);
|
||||
|
||||
useEffect(() => {
|
||||
setLineClamp(!spoilerNode.current || spoilerNode.current.clientHeight >= 96);
|
||||
|
||||
@ -37,6 +37,7 @@ const settingsSchema = v.object({
|
||||
autoTranslate: v.fallback(v.boolean(), false),
|
||||
knownLanguages: v.fallback(v.array(v.string()), []),
|
||||
showWrenchButton: v.fallback(v.boolean(), false),
|
||||
cleanUrls: v.fallback(v.boolean(), false),
|
||||
|
||||
theme: v.fallback(v.optional(v.object({
|
||||
brandColor: v.fallback(v.string(), ''),
|
||||
|
||||
93
packages/pl-fe/src/utils/url-purify.ts
Normal file
93
packages/pl-fe/src/utils/url-purify.ts
Normal file
@ -0,0 +1,93 @@
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// I hope I got this relicensing stuff right xD
|
||||
import { URLPurify, type SerializedRules } from '@mkljczk/url-purify';
|
||||
|
||||
// Adapted from ClearURLs Rules
|
||||
// https://github.com/ClearURLs/Rules/blob/master/data.min.json
|
||||
// Licensed under the LGPL-3.0 license.
|
||||
const DEFAULT_RULESET: SerializedRules = {
|
||||
providers: {
|
||||
globalRules: {
|
||||
urlPattern: '.*',
|
||||
rules: [
|
||||
'(?:%3F)?utm(?:_[a-z_]*)?',
|
||||
'(?:%3F)?ga_[a-z_]+',
|
||||
'(?:%3F)?yclid',
|
||||
'(?:%3F)?_openstat',
|
||||
'(?:%3F)?fb_action_(?:types|ids)',
|
||||
'(?:%3F)?fb_(?:source|ref)',
|
||||
'(?:%3F)?fbclid',
|
||||
'(?:%3F)?action_(?:object|type|ref)_map',
|
||||
'(?:%3F)?gs_l',
|
||||
'(?:%3F)?mkt_tok',
|
||||
'(?:%3F)?hmb_(?:campaign|medium|source)',
|
||||
'(?:%3F)?gclid',
|
||||
'(?:%3F)?srsltid',
|
||||
'(?:%3F)?otm_[a-z_]*',
|
||||
'(?:%3F)?cmpid',
|
||||
'(?:%3F)?os_ehash',
|
||||
'(?:%3F)?_ga',
|
||||
'(?:%3F)?_gl',
|
||||
'(?:%3F)?__twitter_impression',
|
||||
'(?:%3F)?wt_?z?mc',
|
||||
'(?:%3F)?wtrid',
|
||||
'(?:%3F)?[a-z]?mc',
|
||||
'(?:%3F)?dclid',
|
||||
'Echobox',
|
||||
'(?:%3F)?spm',
|
||||
'(?:%3F)?vn(?:_[a-z]*)+',
|
||||
'(?:%3F)?tracking_source',
|
||||
'(?:%3F)?ceneo_spo',
|
||||
'(?:%3F)?itm_(?:campaign|medium|source)',
|
||||
'(?:%3F)?__hsfp',
|
||||
'(?:%3F)?__hssc',
|
||||
'(?:%3F)?__hstc',
|
||||
'(?:%3F)?_hsenc',
|
||||
'(?:%3F)?__s',
|
||||
'(?:%3F)?hsCtaTracking',
|
||||
'(?:%3F)?mc_(?:eid|cid|tc)',
|
||||
'(?:%3F)?ml_subscriber',
|
||||
'(?:%3F)?ml_subscriber_hash',
|
||||
'(?:%3F)?msclkid',
|
||||
'(?:%3F)?oly_anon_id',
|
||||
'(?:%3F)?oly_enc_id',
|
||||
'(?:%3F)?rb_clickid',
|
||||
'(?:%3F)?s_cid',
|
||||
'(?:%3F)?vero_conv',
|
||||
'(?:%3F)?vero_id',
|
||||
'(?:%3F)?wickedid',
|
||||
'(?:%3F)?twclid',
|
||||
'(?:%3F)?ref_?',
|
||||
'(?:%3F)?referrer',
|
||||
],
|
||||
},
|
||||
youtube: {
|
||||
urlPattern: '^https?:\\/\\/(?:[a-z0-9-]+\\.)*?(youtube\\.com|youtu\\.be)',
|
||||
rules: ['feature', 'gclid', 'kw', 'si', 'pp'],
|
||||
exceptions: [
|
||||
'^https?:\\/\\/(?:[a-z0-9-]+\\.)*?youtube\\.com\\/signin\\?.*?',
|
||||
],
|
||||
redirections: [
|
||||
'^https?:\\/\\/(?:[a-z0-9-]+\\.)*?youtube\\.com\\/redirect?.*?q=([^&]*)',
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Purify = new URLPurify({
|
||||
rulesFromMemory: DEFAULT_RULESET,
|
||||
});
|
||||
|
||||
export default Purify;
|
||||
@ -1727,6 +1727,11 @@
|
||||
lodash "^4.17.21"
|
||||
mousetrap "^1.6.5"
|
||||
|
||||
"@mkljczk/url-purify@^0.0.1":
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@mkljczk/url-purify/-/url-purify-0.0.1.tgz#4406b43e9cc054c67ca48fd6d684c700e119c85c"
|
||||
integrity sha512-7QmSb6+d5aGVBZqBP8XkQQ3Z97YQXo8tHW9hq99aZWW7M7ugIxvb6arNjRqA/CKgiTc3tQGfvAS0SGfu5zedOw==
|
||||
|
||||
"@nexusmods/eslint-plugin-nexusmods@^0.0.7":
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@nexusmods/eslint-plugin-nexusmods/-/eslint-plugin-nexusmods-0.0.7.tgz#2db1e8e50096480fd725d9978560c4c6d26c782d"
|
||||
|
||||
Reference in New Issue
Block a user