1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 23:33:31 +00:00

[PS-1123] Improve hostname and domain retrieval (#3168)

* Add test cases from previous PR https://github.com/bitwarden/jslib/pull/547

* Install tldts as replacement for tldjs

* Use tldts for hostname and domain retrieval/validation

* Remove usage of old tldjs.noop-implementation

* Add handling of about protocol

* Remove usage of tldEndingRegex and use tldts check instead

* Uninstall @types/tldjs and tldjs

* Updated package-lock.json

* Fix accessibility cookie check

* Rename loginUriView.spec to login-uri-view.spec

* Add test for getDomain failing file links

* getHostName - Return null when given, data, about or file links
This commit is contained in:
Daniel James Smith
2022-10-24 19:26:50 +02:00
committed by GitHub
parent 94e9744d06
commit 8c59eef257
12 changed files with 337 additions and 128 deletions

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-useless-escape */
import * as tldjs from "tldjs";
import { getHostname, parse } from "tldts";
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service";
import { CryptoService } from "../abstractions/crypto.service";
@@ -24,11 +24,10 @@ export class Utils {
static isMobileBrowser = false;
static isAppleMobileBrowser = false;
static global: typeof global = null;
static tldEndingRegex =
/.*\.(com|net|org|edu|uk|gov|ca|de|jp|fr|au|ru|ch|io|es|us|co|xyz|info|ly|mil)$/;
// Transpiled version of /\p{Emoji_Presentation}/gu using https://mothereff.in/regexpu. Used for compatability in older browsers.
static regexpEmojiPresentation =
/(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])/g;
static readonly validHosts: string[] = ["localhost"];
static init() {
if (Utils.inited) {
@@ -214,12 +213,39 @@ export class Utils {
}
static getHostname(uriString: string): string {
const url = Utils.getUrl(uriString);
if (Utils.isNullOrWhitespace(uriString)) {
return null;
}
uriString = uriString.trim();
if (uriString.startsWith("data:")) {
return null;
}
if (uriString.startsWith("about:")) {
return null;
}
if (uriString.startsWith("file:")) {
return null;
}
// Does uriString contain invalid characters
// TODO Needs to possibly be extended, although '!' is a reserved character
if (uriString.indexOf("!") > 0) {
return null;
}
try {
return url != null && url.hostname !== "" ? url.hostname : null;
const hostname = getHostname(uriString, { validHosts: this.validHosts });
if (hostname != null) {
return hostname;
}
} catch {
return null;
}
return null;
}
static getHost(uriString: string): string {
@@ -232,60 +258,35 @@ export class Utils {
}
static getDomain(uriString: string): string {
if (uriString == null) {
if (Utils.isNullOrWhitespace(uriString)) {
return null;
}
uriString = uriString.trim();
if (uriString === "") {
return null;
}
if (uriString.startsWith("data:")) {
return null;
}
let httpUrl = uriString.startsWith("http://") || uriString.startsWith("https://");
if (
!httpUrl &&
uriString.indexOf("://") < 0 &&
Utils.tldEndingRegex.test(uriString) &&
uriString.indexOf("@") < 0
) {
uriString = "http://" + uriString;
httpUrl = true;
}
if (httpUrl) {
try {
const url = Utils.getUrlObject(uriString);
const validHostname = tldjs?.isValid != null ? tldjs.isValid(url.hostname) : true;
if (!validHostname) {
return null;
}
if (url.hostname === "localhost" || Utils.validIpAddress(url.hostname)) {
return url.hostname;
}
const urlDomain =
tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(url.hostname) : null;
return urlDomain != null ? urlDomain : url.hostname;
} catch (e) {
// Invalid domain, try another approach below.
}
if (uriString.startsWith("about:")) {
return null;
}
try {
const domain = tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(uriString) : null;
const parseResult = parse(uriString, { validHosts: this.validHosts });
if (parseResult != null && parseResult.hostname != null) {
if (parseResult.hostname === "localhost" || parseResult.isIp) {
return parseResult.hostname;
}
if (domain != null) {
return domain;
if (parseResult.domain != null) {
return parseResult.domain;
}
return null;
}
} catch {
return null;
}
return null;
}
@@ -358,14 +359,11 @@ export class Utils {
}
static getUrl(uriString: string): URL {
if (uriString == null) {
if (this.isNullOrWhitespace(uriString)) {
return null;
}
uriString = uriString.trim();
if (uriString === "") {
return null;
}
let url = Utils.getUrlObject(uriString);
if (url == null) {
@@ -425,12 +423,6 @@ export class Utils {
return this.global.bitwardenContainerService;
}
private static validIpAddress(ipString: string): boolean {
const ipRegex =
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ipString);
}
private static isMobile(win: Window) {
let mobile = false;
((a) => {