diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js
index 8248d215..476655e2 100644
--- a/__tests__/ExpensiMark-HTML-test.js
+++ b/__tests__/ExpensiMark-HTML-test.js
@@ -395,7 +395,7 @@ test('Test url replacements', () => {
+ 'https://expensify.cash/#/r/1234 '
+ 'https://github.com/Expensify/ReactNativeChat/pull/6.45 '
+ 'https://github.com/Expensify/Expensify/issues/143,231 '
- + 'testRareTLDs.beer '
+ + 'testRareTLDs.beer '
+ 'test@expensify.com '
+ 'test.completelyFakeTLD '
+ 'https://www.expensify.com/_devportal/tools/logSearch/#query=request_id:(%22Ufjjim%22)+AND+timestamp:[2021-01-08T03:48:10.389Z+TO+2021-01-08T05:48:10.389Z]&index=logs_expensify-008878) '
diff --git a/__tests__/Str-test.js b/__tests__/Str-test.js
index 1c843951..a8d92a2a 100644
--- a/__tests__/Str-test.js
+++ b/__tests__/Str-test.js
@@ -84,3 +84,15 @@ describe('Str.isValidMention', () => {
expect(Str.isValidMention('"@username@expensify.com"')).toBeTruthy();
});
});
+
+describe('Str.sanitizeURL', () => {
+ it('Normalize domain name to lower case and add missing https:// protocol', () => {
+ expect(Str.sanitizeURL('https://google.com')).toBe('https://google.com');
+ expect(Str.sanitizeURL('google.com')).toBe('https://google.com');
+ expect(Str.sanitizeURL('Https://google.com')).toBe('https://google.com');
+ expect(Str.sanitizeURL('https://GOOgle.com')).toBe('https://google.com');
+ expect(Str.sanitizeURL('FOO.com/blah_BLAH')).toBe('https://foo.com/blah_BLAH');
+ expect(Str.sanitizeURL('http://FOO.com/blah_BLAH')).toBe('http://foo.com/blah_BLAH');
+ expect(Str.sanitizeURL('HTtp://FOO.com/blah_BLAH')).toBe('http://foo.com/blah_BLAH');
+ });
+});
diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js
index 52b90400..84230c0e 100644
--- a/lib/ExpensiMark.js
+++ b/lib/ExpensiMark.js
@@ -113,15 +113,11 @@ export default class ExpensiMark {
return this.modifyTextForUrlLinks(regex, textToProcess, replacement);
},
- // We use a function here to check if there is already a https:// on the link.
- // If there is not, we force the link to be absolute by prepending '//' to the target.
- replacement: (match, g1, g2, g3) => {
+ replacement: (match, g1, g2) => {
if (g1.match(CONST.REG_EXP.EMOJIS) || !g1.trim()) {
return match;
}
-
- const href = g3 ? g2.replace(g3, g3.toLowerCase()) : `https://${g2}`;
- return `${g1.trim()}`;
+ return `${g1.trim()}`;
},
},
@@ -154,10 +150,8 @@ export default class ExpensiMark {
return this.modifyTextForUrlLinks(regex, textToProcess, replacement);
},
- // We use a function here to check if there is already a https:// on the link.
- // If there is not, we force the link to be absolute by prepending '//' to the target.
- replacement: (match, g1, g2, g3) => {
- const href = g3 ? g2.replace(g3, g3.toLowerCase()) : `https://${g2}`;
+ replacement: (match, g1, g2) => {
+ const href = Str.sanitizeURL(g2);
return `${g1}${g2}${g1}`;
},
},
@@ -465,7 +459,7 @@ export default class ExpensiMark {
filterRules: ['bold', 'strikethrough', 'italic'],
shouldEscapeText: false,
});
- replacedText = replacedText.concat(replacement(match[0], linkText, match[2], match[3]));
+ replacedText = replacedText.concat(replacement(match[0], linkText, match[2]));
}
startIndex = match.index + (match[0].length);
diff --git a/lib/Url.js b/lib/Url.js
index 2554d91c..495c1f38 100644
--- a/lib/Url.js
+++ b/lib/Url.js
@@ -7,7 +7,7 @@ const addEscapedChar = reg => `(?:${reg}|&(?:amp|quot|#x27);)`;
const URL_PATH_REGEX = `(?:${addEscapedChar('[.,=(+$!*]')}?\\/${addEscapedChar('[-\\w$@.+!*:(),=%~]')}*${addEscapedChar('[-\\w~@:%)]')}|\\/)*`;
const URL_PARAM_REGEX = `(?:\\?${addEscapedChar('[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]')}*)?`;
const URL_FRAGMENT_REGEX = `(?:#${addEscapedChar('[-\\w$@.+!*()[\\],=%;\\/:~]')}*)?`;
-const URL_REGEX = `(${URL_WEBSITE_REGEX}${URL_PATH_REGEX}(?:${URL_PARAM_REGEX}|${URL_FRAGMENT_REGEX})*)`;
+const URL_REGEX = `((${URL_WEBSITE_REGEX})${URL_PATH_REGEX}(?:${URL_PARAM_REGEX}|${URL_FRAGMENT_REGEX})*)`;
const URL_REGEX_WITH_REQUIRED_PROTOCOL = URL_REGEX.replace(`${URL_PROTOCOL_REGEX}?`, URL_PROTOCOL_REGEX);
diff --git a/lib/str.js b/lib/str.js
index 7cab4f9f..da76d7ef 100644
--- a/lib/str.js
+++ b/lib/str.js
@@ -3,6 +3,7 @@ import _ from 'underscore';
import {AllHtmlEntities} from 'html-entities';
import replaceAll from 'string.prototype.replaceall';
import {CONST} from './CONST';
+import {URL_REGEX} from './Url';
const REMOVE_SMS_DOMAIN_PATTERN = new RegExp(`@${CONST.SMS.DOMAIN}`, 'gi');
@@ -1021,6 +1022,22 @@ const Str = {
return (typeof url === 'string' && url.startsWith('/')) ? url : `/${url}`;
},
+ /**
+ * Formats a URL by converting the domain name to lowercase and adding the missing 'https://' protocol.
+ *
+ * @param {url} url The URL to be formatted
+ * @returns {String} The formatted URL
+ */
+ sanitizeURL(url) {
+ const regex = new RegExp(`^${URL_REGEX}$`, 'i');
+ const match = regex.exec(url);
+ if (!match) {
+ return url;
+ }
+ const website = match[3] ? match[2] : `https://${match[2]}`;
+ return website.toLowerCase() + this.cutBefore(match[1], match[2]);
+ },
+
/**
* Checks if parameter is a string or function
* if it is a function then we will call it with