@@ -7,6 +7,39 @@ const kShadyLinkRegExps = [
77 / ( h t t p [ s ] ? : \/ \/ .* \. ( l i n k | x y z | t k | m l | g a | c f | g q | p w | t o p | c l u b | m w | b d | k e | a m | s b s | d a t e | q u e s t | c d | b i d | w s | i c u | c a m | u n o | e m a i l | s t r e a m ) ) $ /
88] ;
99
10+ // List of known URI schemes (IANA registered + common ones)
11+ // See: https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
12+ const kKnownProtocols = new Set ( [
13+ // Web
14+ "http:" , "https:" ,
15+ // File & Data
16+ "file:" , "data:" , "blob:" ,
17+ // FTP
18+ "ftp:" , "ftps:" , "sftp:" , "tftp:" ,
19+ // Mail & Messaging
20+ "mailto:" , "xmpp:" , "irc:" , "ircs:" , "sip:" , "sips:" , "tel:" , "sms:" , "mms:" ,
21+ // Remote access
22+ "ssh:" , "telnet:" , "vnc:" , "rdp:" ,
23+ // Version control
24+ "git:" , "svn:" , "cvs:" , "hg:" ,
25+ // P2P & Torrents
26+ "magnet:" , "ed2k:" , "torrent:" ,
27+ // Crypto & Blockchain
28+ "bitcoin:" , "ethereum:" , "ipfs:" , "ipns:" ,
29+ // App-specific
30+ "slack:" , "discord:" , "spotify:" , "steam:" , "skype:" , "zoommtg:" , "msteams:" ,
31+ "vscode:" , "vscode-insiders:" , "jetbrains:" ,
32+ // Mobile & Desktop deep links
33+ "intent:" , "market:" , "itms:" , "itms-apps:" , "fb:" , "twitter:" , "instagram:" , "whatsapp:" , "tg:" ,
34+ // Other common protocols
35+ "ws:" , "wss:" , "ldap:" , "ldaps:" , "nntp:" , "news:" , "rtsp:" , "rtspu:" , "rtsps:" ,
36+ "webcal:" , "feed:" , "podcast:" ,
37+ // eslint-disable-next-line no-script-url
38+ "javascript:" , "about:" , "view-source:" ,
39+ // Security related
40+ "acap:" , "cap:" , "cid:" , "mid:" , "urn:" , "tag:" , "dns:" , "geo:" , "ni:" , "nih:"
41+ ] ) ;
42+
1043export class ShadyURL {
1144 static isSafe (
1245 input : string
@@ -16,6 +49,11 @@ export class ShadyURL {
1649 }
1750
1851 const parsedUrl = new URL ( input ) ;
52+ // Unknown protocol, not a real URL
53+ if ( ! kKnownProtocols . has ( parsedUrl . protocol ) ) {
54+ return true ;
55+ }
56+
1957 const hostname = parsedUrl . hostname ;
2058 if ( ipaddress . isValid ( hostname ) ) {
2159 if ( this . #isPrivateIPAddress( hostname ) ) {
0 commit comments