allow setting a web instance in the url from a whitelist
as web clients can be hosted on multiple domains, allow specifying which hosted instance we want to use. as this could be phishing vector, we work with a host whitelist for now until a better solution comes to mind.
This commit is contained in:
parent
0aa9c2e766
commit
47d0db89c1
2 changed files with 39 additions and 7 deletions
33
src/Link.js
33
src/Link.js
|
@ -40,6 +40,21 @@ function asPrefix(identifierKind) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWebInstanceMap(queryParams) {
|
||||||
|
const prefix = "web-instance[";
|
||||||
|
const postfix = "]";
|
||||||
|
const webInstanceParams = queryParams.filter(([key]) => key.startsWith(prefix) && key.endsWith(postfix));
|
||||||
|
const webInstances = webInstanceParams.map(([key, value]) => {
|
||||||
|
const noPrefix = key.substr(prefix.length);
|
||||||
|
const clientId = noPrefix.substr(0, noPrefix.length - postfix.length);
|
||||||
|
return [clientId, value];
|
||||||
|
});
|
||||||
|
return webInstances.reduce((map, [clientId, host]) => {
|
||||||
|
map[clientId] = host;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
export function getLabelForLinkKind(kind) {
|
export function getLabelForLinkKind(kind) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case LinkKind.User: return "Start chat";
|
case LinkKind.User: return "Start chat";
|
||||||
|
@ -74,8 +89,12 @@ export class Link {
|
||||||
|
|
||||||
let viaServers = [];
|
let viaServers = [];
|
||||||
let clientId = null;
|
let clientId = null;
|
||||||
|
let webInstances = {};
|
||||||
if (queryParamsStr) {
|
if (queryParamsStr) {
|
||||||
const queryParams = queryParamsStr.split("&").map(pair => pair.split("="));
|
const queryParams = queryParamsStr.split("&").map(pair => {
|
||||||
|
const [key, value] = pair.split("=");
|
||||||
|
return [decodeURIComponent(key), decodeURIComponent(value)];
|
||||||
|
});
|
||||||
viaServers = queryParams
|
viaServers = queryParams
|
||||||
.filter(([key, value]) => key === "via")
|
.filter(([key, value]) => key === "via")
|
||||||
.map(([,value]) => value);
|
.map(([,value]) => value);
|
||||||
|
@ -83,6 +102,7 @@ export class Link {
|
||||||
if (clientParam) {
|
if (clientParam) {
|
||||||
clientId = clientParam[1];
|
clientId = clientParam[1];
|
||||||
}
|
}
|
||||||
|
webInstances = getWebInstanceMap(queryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (linkStr.startsWith("#/")) {
|
if (linkStr.startsWith("#/")) {
|
||||||
|
@ -96,32 +116,33 @@ export class Link {
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const server = matches[2];
|
const server = matches[2];
|
||||||
const localPart = matches[1];
|
const localPart = matches[1];
|
||||||
return new Link(clientId, viaServers, IdentifierKind.UserId, localPart, server);
|
return new Link(clientId, viaServers, IdentifierKind.UserId, localPart, server, webInstances);
|
||||||
}
|
}
|
||||||
matches = ROOMALIAS_PATTERN.exec(identifier);
|
matches = ROOMALIAS_PATTERN.exec(identifier);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const server = matches[2];
|
const server = matches[2];
|
||||||
const localPart = matches[1];
|
const localPart = matches[1];
|
||||||
return new Link(clientId, viaServers, IdentifierKind.RoomAlias, localPart, server, eventId);
|
return new Link(clientId, viaServers, IdentifierKind.RoomAlias, localPart, server, webInstances, eventId);
|
||||||
}
|
}
|
||||||
matches = ROOMID_PATTERN.exec(identifier);
|
matches = ROOMID_PATTERN.exec(identifier);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const server = matches[2];
|
const server = matches[2];
|
||||||
const localPart = matches[1];
|
const localPart = matches[1];
|
||||||
return new Link(clientId, viaServers, IdentifierKind.RoomId, localPart, server, eventId);
|
return new Link(clientId, viaServers, IdentifierKind.RoomId, localPart, server, webInstances, eventId);
|
||||||
}
|
}
|
||||||
matches = GROUPID_PATTERN.exec(identifier);
|
matches = GROUPID_PATTERN.exec(identifier);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const server = matches[2];
|
const server = matches[2];
|
||||||
const localPart = matches[1];
|
const localPart = matches[1];
|
||||||
return new Link(clientId, viaServers, IdentifierKind.GroupId, localPart, server);
|
return new Link(clientId, viaServers, IdentifierKind.GroupId, localPart, server, webInstances);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(clientId, viaServers, identifierKind, localPart, server, eventId) {
|
constructor(clientId, viaServers, identifierKind, localPart, server, webInstances, eventId) {
|
||||||
const servers = [server];
|
const servers = [server];
|
||||||
servers.push(...viaServers);
|
servers.push(...viaServers);
|
||||||
|
this.webInstances = webInstances;
|
||||||
this.servers = orderedUnique(servers);
|
this.servers = orderedUnique(servers);
|
||||||
this.identifierKind = identifierKind;
|
this.identifierKind = identifierKind;
|
||||||
this.identifier = `${asPrefix(identifierKind)}${localPart}:${server}`;
|
this.identifier = `${asPrefix(identifierKind)}${localPart}:${server}`;
|
||||||
|
|
|
@ -17,6 +17,13 @@ limitations under the License.
|
||||||
import {Maturity, Platform, LinkKind,
|
import {Maturity, Platform, LinkKind,
|
||||||
FDroidLink, AppleStoreLink, PlayStoreLink, WebsiteLink} from "../types.js";
|
FDroidLink, AppleStoreLink, PlayStoreLink, WebsiteLink} from "../types.js";
|
||||||
|
|
||||||
|
const trustedWebInstances = [
|
||||||
|
"app.element.io", // first one is the default one
|
||||||
|
"develop.element.io",
|
||||||
|
"chat.fosdem.org",
|
||||||
|
"chat.mozilla.org",
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information on how to deep link to a given matrix client.
|
* Information on how to deep link to a given matrix client.
|
||||||
*/
|
*/
|
||||||
|
@ -56,7 +63,11 @@ export class Element {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (platform === Platform.DesktopWeb || platform === Platform.MobileWeb || platform === Platform.iOS) {
|
if (platform === Platform.DesktopWeb || platform === Platform.MobileWeb || platform === Platform.iOS) {
|
||||||
return `https://app.element.io/#/${fragmentPath}`;
|
let instanceHost = trustedWebInstances[0];
|
||||||
|
if (trustedWebInstances.includes(link.webInstances[this.id])) {
|
||||||
|
instanceHost = link.webInstances[this.id];
|
||||||
|
}
|
||||||
|
return `https://${instanceHost}/#/${fragmentPath}`;
|
||||||
} else if (platform === Platform.Linux || platform === Platform.Windows || platform === Platform.macOS) {
|
} else if (platform === Platform.Linux || platform === Platform.Windows || platform === Platform.macOS) {
|
||||||
return `element://vector/webapp/#/${fragmentPath}`;
|
return `element://vector/webapp/#/${fragmentPath}`;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue