about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2023-11-22 17:20:35 -0600
committerGitHub <noreply@github.com>2023-11-22 17:20:35 -0600
commitedf3114e476aef5da67ca76b4636ac3cfbcb26d8 (patch)
tree2cdd41ad5a11aae5cf2c49bddac58c1aaae9a5af
parentec819f06cecca6a20b4c8c9cac3f4727d4464a04 (diff)
downloadvoidsky-edf3114e476aef5da67ca76b4636ac3cfbcb26d8.tar.zst
Fixes 1731, compare URLs case-insensitive (#1980)
-rw-r--r--__tests__/lib/strings/url-helpers.test.ts7
-rw-r--r--jest/jestSetup.js3
-rw-r--r--src/lib/strings/url-helpers.ts25
3 files changed, 30 insertions, 5 deletions
diff --git a/__tests__/lib/strings/url-helpers.test.ts b/__tests__/lib/strings/url-helpers.test.ts
index 8bb52ed40..6ac31aeb6 100644
--- a/__tests__/lib/strings/url-helpers.test.ts
+++ b/__tests__/lib/strings/url-helpers.test.ts
@@ -1,3 +1,5 @@
+import {it, describe, expect} from '@jest/globals'
+
 import {
   linkRequiresWarning,
   isPossiblyAUrl,
@@ -6,6 +8,7 @@ import {
 
 describe('linkRequiresWarning', () => {
   type Case = [string, string, boolean]
+
   const cases: Case[] = [
     ['http://example.com', 'http://example.com', false],
     ['http://example.com', 'example.com', false],
@@ -64,6 +67,10 @@ describe('linkRequiresWarning', () => {
     ['http://bsky.app/', 'https://google.com', true],
     ['https://bsky.app/', 'https://google.com', true],
 
+    // case insensitive
+    ['https://Example.com', 'example.com', false],
+    ['https://example.com', 'Example.com', false],
+
     // bad uri inputs, default to true
     ['', '', true],
     ['example.com', 'example.com', true],
diff --git a/jest/jestSetup.js b/jest/jestSetup.js
index 5d6bd4f1f..d8cee9bfd 100644
--- a/jest/jestSetup.js
+++ b/jest/jestSetup.js
@@ -2,6 +2,9 @@
 import {configure} from '@testing-library/react-native'
 import 'react-native-gesture-handler/jestSetup'
 
+// IMPORTANT: this is what's used in the native runtime
+import 'react-native-url-polyfill/auto'
+
 configure({asyncUtilTimeout: 20000})
 
 jest.mock('@react-native-async-storage/async-storage', () =>
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
index d06bd8028..e9bf4111d 100644
--- a/src/lib/strings/url-helpers.ts
+++ b/src/lib/strings/url-helpers.ts
@@ -168,8 +168,15 @@ export function getYoutubeVideoId(link: string): string | undefined {
   return videoId
 }
 
+/**
+ * Checks if the label in the post text matches the host of the link facet.
+ *
+ * Hosts are case-insensitive, so should be lowercase for comparison.
+ * @see https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2
+ */
 export function linkRequiresWarning(uri: string, label: string) {
   const labelDomain = labelToDomain(label)
+
   let urip
   try {
     urip = new URL(uri)
@@ -177,7 +184,9 @@ export function linkRequiresWarning(uri: string, label: string) {
     return true
   }
 
-  if (urip.hostname === 'bsky.app') {
+  const host = urip.hostname.toLowerCase()
+
+  if (host === 'bsky.app') {
     // if this is a link to internal content,
     // warn if it represents itself as a URL to another app
     if (
@@ -194,20 +203,26 @@ export function linkRequiresWarning(uri: string, label: string) {
     if (!labelDomain) {
       return true
     }
-    return labelDomain !== urip.hostname
+    return labelDomain !== host
   }
 }
 
-function labelToDomain(label: string): string | undefined {
+/**
+ * Returns a lowercase domain hostname if the label is a valid URL.
+ *
+ * Hosts are case-insensitive, so should be lowercase for comparison.
+ * @see https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2
+ */
+export function labelToDomain(label: string): string | undefined {
   // any spaces just immediately consider the label a non-url
   if (/\s/.test(label)) {
     return undefined
   }
   try {
-    return new URL(label).hostname
+    return new URL(label).hostname.toLowerCase()
   } catch {}
   try {
-    return new URL('https://' + label).hostname
+    return new URL('https://' + label).hostname.toLowerCase()
   } catch {}
   return undefined
 }