Fixes 1731, compare URLs case-insensitive (#1980)
parent
ec819f06ce
commit
edf3114e47
|
@ -1,3 +1,5 @@
|
||||||
|
import {it, describe, expect} from '@jest/globals'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
linkRequiresWarning,
|
linkRequiresWarning,
|
||||||
isPossiblyAUrl,
|
isPossiblyAUrl,
|
||||||
|
@ -6,6 +8,7 @@ import {
|
||||||
|
|
||||||
describe('linkRequiresWarning', () => {
|
describe('linkRequiresWarning', () => {
|
||||||
type Case = [string, string, boolean]
|
type Case = [string, string, boolean]
|
||||||
|
|
||||||
const cases: Case[] = [
|
const cases: Case[] = [
|
||||||
['http://example.com', 'http://example.com', false],
|
['http://example.com', 'http://example.com', false],
|
||||||
['http://example.com', 'example.com', false],
|
['http://example.com', 'example.com', false],
|
||||||
|
@ -64,6 +67,10 @@ describe('linkRequiresWarning', () => {
|
||||||
['http://bsky.app/', 'https://google.com', true],
|
['http://bsky.app/', 'https://google.com', true],
|
||||||
['https://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
|
// bad uri inputs, default to true
|
||||||
['', '', true],
|
['', '', true],
|
||||||
['example.com', 'example.com', true],
|
['example.com', 'example.com', true],
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
import {configure} from '@testing-library/react-native'
|
import {configure} from '@testing-library/react-native'
|
||||||
import 'react-native-gesture-handler/jestSetup'
|
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})
|
configure({asyncUtilTimeout: 20000})
|
||||||
|
|
||||||
jest.mock('@react-native-async-storage/async-storage', () =>
|
jest.mock('@react-native-async-storage/async-storage', () =>
|
||||||
|
|
|
@ -168,8 +168,15 @@ export function getYoutubeVideoId(link: string): string | undefined {
|
||||||
return videoId
|
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) {
|
export function linkRequiresWarning(uri: string, label: string) {
|
||||||
const labelDomain = labelToDomain(label)
|
const labelDomain = labelToDomain(label)
|
||||||
|
|
||||||
let urip
|
let urip
|
||||||
try {
|
try {
|
||||||
urip = new URL(uri)
|
urip = new URL(uri)
|
||||||
|
@ -177,7 +184,9 @@ export function linkRequiresWarning(uri: string, label: string) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urip.hostname === 'bsky.app') {
|
const host = urip.hostname.toLowerCase()
|
||||||
|
|
||||||
|
if (host === 'bsky.app') {
|
||||||
// if this is a link to internal content,
|
// if this is a link to internal content,
|
||||||
// warn if it represents itself as a URL to another app
|
// warn if it represents itself as a URL to another app
|
||||||
if (
|
if (
|
||||||
|
@ -194,20 +203,26 @@ export function linkRequiresWarning(uri: string, label: string) {
|
||||||
if (!labelDomain) {
|
if (!labelDomain) {
|
||||||
return true
|
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
|
// any spaces just immediately consider the label a non-url
|
||||||
if (/\s/.test(label)) {
|
if (/\s/.test(label)) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return new URL(label).hostname
|
return new URL(label).hostname.toLowerCase()
|
||||||
} catch {}
|
} catch {}
|
||||||
try {
|
try {
|
||||||
return new URL('https://' + label).hostname
|
return new URL('https://' + label).hostname.toLowerCase()
|
||||||
} catch {}
|
} catch {}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue