diff --git a/shared/utils/urls.test.ts b/shared/utils/urls.test.ts
index f8b27ea7c..62de14b0c 100644
--- a/shared/utils/urls.test.ts
+++ b/shared/utils/urls.test.ts
@@ -1,5 +1,100 @@
+import * as urlsUtils from "./urls";
import { urlRegex } from "./urls";
+describe("IsUrl Method", () => {
+ describe("invalid urls", () => {
+ it("should return false", () => {
+ expect(urlsUtils.isUrl("")).toBe(false);
+ expect(urlsUtils.isUrl("#invalidurl")).toBe(false);
+ expect(urlsUtils.isUrl("mailto:")).toBe(false);
+ expect(urlsUtils.isUrl("://")).toBe(false);
+ });
+ });
+});
+
+describe("isInternalUrl Method", () => {
+ it("should return false if empty string", () => {
+ expect(urlsUtils.isInternalUrl("")).toBe(false);
+ });
+
+ it("should return true if starting with relative path", () => {
+ expect(urlsUtils.isInternalUrl("/drafts")).toEqual(true);
+ });
+});
+
+describe("isExternalUrl Method", () => {
+ it("should return false if empty url", () => {
+ expect(urlsUtils.isExternalUrl("")).toBe(false);
+ });
+
+ it("should return false if internal url", () => {
+ expect(urlsUtils.isExternalUrl("/drafts")).toBe(false);
+ });
+});
+
+describe("sanitizeUrl Method", () => {
+ it("should return undefined if not url", () => {
+ expect(urlsUtils.sanitizeUrl(undefined)).toBeUndefined();
+ expect(urlsUtils.sanitizeUrl(null)).toBeUndefined();
+ expect(urlsUtils.sanitizeUrl("")).toBeUndefined();
+ });
+
+ it("should append https:// to non-special urls", () => {
+ expect(urlsUtils.sanitizeUrl("www.google.com")).toEqual(
+ "https://www.google.com"
+ );
+ });
+
+ describe("special urls", () => {
+ it("should return the url as it's if starting with /", () => {
+ expect(urlsUtils.sanitizeUrl("/drafts")).toEqual("/drafts");
+ });
+ it("should return the url as it's if starting with #", () => {
+ expect(urlsUtils.sanitizeUrl("#home")).toEqual("#home");
+ });
+ it("should return the url as it's if it's mailto:", () => {
+ expect(urlsUtils.sanitizeUrl("mailto:outline@getoutline.com")).toEqual(
+ "mailto:outline@getoutline.com"
+ );
+ });
+ it("should return the url as it's if it's mailto:", () => {
+ expect(urlsUtils.sanitizeUrl("mailto:outline@getoutline.com")).toEqual(
+ "mailto:outline@getoutline.com"
+ );
+ });
+ it("should return the url as it's if it's sms:, fax:, tel:", () => {
+ expect(urlsUtils.sanitizeUrl("mailto:outline@getoutline.com")).toEqual(
+ "mailto:outline@getoutline.com"
+ );
+ expect(urlsUtils.sanitizeUrl("tel:0123456789")).toEqual("tel:0123456789");
+ expect(urlsUtils.sanitizeUrl("fax:0123456789")).toEqual("fax:0123456789");
+ expect(urlsUtils.sanitizeUrl("sms:0123456789")).toEqual("sms:0123456789");
+ });
+ it("should return the url as it's if it's a special domain", () => {
+ expect(urlsUtils.sanitizeUrl("mqtt://getoutline.com")).toEqual(
+ "mqtt://getoutline.com"
+ );
+ });
+ });
+
+ describe("Blocked protocols", () => {
+ it("should be sanitized", () => {
+ expect(urlsUtils.sanitizeUrl("file://localhost.com/outline.txt")).toEqual(
+ "https://file://localhost.com/outline.txt"
+ );
+ expect(urlsUtils.sanitizeUrl("javascript:whatever")).toEqual(
+ "https://javascript:whatever"
+ );
+ expect(
+ urlsUtils.sanitizeUrl("data:text/html,")
+ ).toEqual("https://data:text/html,");
+ expect(urlsUtils.sanitizeUrl("vbscript:whatever")).toEqual(
+ "https://vbscript:whatever"
+ );
+ });
+ });
+});
+
describe("#urlRegex", () => {
it("should return undefined for invalid urls", () => {
expect(urlRegex(undefined)).toBeUndefined();
diff --git a/shared/utils/urls.ts b/shared/utils/urls.ts
index 337d47725..7981ae9d3 100644
--- a/shared/utils/urls.ts
+++ b/shared/utils/urls.ts
@@ -55,7 +55,9 @@ export function isUrl(text: string) {
try {
const url = new URL(text);
- return url.hostname !== "";
+ const blockedProtocols = ["javascript:", "file:", "vbscript:", "data:"];
+
+ return url.hostname !== "" && !blockedProtocols.includes(url.protocol);
} catch (err) {
return false;
}
@@ -68,7 +70,7 @@ export function isUrl(text: string) {
* @returns True if the url is external, false otherwise.
*/
export function isExternalUrl(url: string) {
- return !isInternalUrl(url);
+ return !!url && !isInternalUrl(url);
}
/**
@@ -87,7 +89,10 @@ export function sanitizeUrl(url: string | null | undefined) {
!isUrl(url) &&
!url.startsWith("/") &&
!url.startsWith("#") &&
- !url.startsWith("mailto:")
+ !url.startsWith("mailto:") &&
+ !url.startsWith("sms:") &&
+ !url.startsWith("fax:") &&
+ !url.startsWith("tel:")
) {
return `https://${url}`;
}