Fix protocol-aware urls, add better link validation in previews

This commit is contained in:
Pavel Djundik 2018-04-27 14:11:54 +03:00
parent 15c14c6dea
commit 629ae8bfa4
5 changed files with 78 additions and 9 deletions

View File

@ -25,11 +25,18 @@ function findLinks(text) {
return [];
}
return matches.map((url) => ({
return matches.map((url) => {
// Prefix protocol to protocol-aware urls
if (url.schema === "//") {
url.url = `http:${url.url}`;
}
return {
start: url.index,
end: url.lastIndex,
link: url.url,
}));
};
});
}
module.exports = findLinks;

View File

@ -11,7 +11,6 @@ const findLinks = require("../../../client/js/libs/handlebars/ircmessageparser/f
const storage = require("../storage");
const mediaTypeRegex = /^(audio|video)\/.+/;
const linkRegex = /^https?:\/\//;
// Fix ECDH curve client compatibility in Node v8/v9
// This is fixed in Node 10, but The Lounge supports LTS versions
@ -34,7 +33,7 @@ module.exports = function(client, chan, msg) {
const cleanText = cleanIrcMessage(msg.text);
// We will only try to prefetch http(s) links
const links = findLinks(cleanText).filter((w) => linkRegex.test(w.link));
const links = findLinks(cleanText).filter((w) => isValidLink(w.link));
if (links.length === 0) {
return;
@ -99,7 +98,7 @@ function parseHtml(preview, res, client) {
}
// Make sure thumbnail is a valid url
if (!linkRegex.test(preview.thumb)) {
if (!isValidLink(preview.thumb)) {
preview.thumb = "";
}
@ -364,3 +363,24 @@ function fetch(uri, headers, cb) {
function normalizeURL(header) {
return URI(header).normalize().toString();
}
function isValidLink(link) {
try {
const uri = URI(link);
const protocol = uri.protocol();
// Only fetch http and https links
if (protocol !== "http" && protocol !== "https") {
return false;
}
// Do not fetch links without hostname or ones that contain authorization
if (!uri.hostname() || uri.username() || uri.password()) {
return false;
}
} catch (e) {
return false;
}
return true;
}

View File

@ -290,4 +290,17 @@ describe("findLinks", () => {
expect(actual).to.deep.equal(expected);
});
it("should add protocol to protocol-aware urls", () => {
const input = "//example.com";
const expected = [{
link: "http://example.com",
start: 0,
end: 13,
}];
const actual = findLinks(input);
expect(actual).to.deep.equal(expected);
});
});

View File

@ -7,7 +7,7 @@ describe("parse Handlebars helper", () => {
it("should not introduce xss", () => {
const testCases = [{
input: "<img onerror='location.href=\"//youtube.com\"'>",
expected: "&lt;img onerror&#x3D;&#x27;location.href&#x3D;&quot;<a href=\"//youtube.com\" target=\"_blank\" rel=\"noopener\">//youtube.com</a>&quot;&#x27;&gt;",
expected: "&lt;img onerror&#x3D;&#x27;location.href&#x3D;&quot;<a href=\"http://youtube.com\" target=\"_blank\" rel=\"noopener\">//youtube.com</a>&quot;&#x27;&gt;",
}, {
input: '#&">bug',
expected: '<span class="inline-channel" role="button" tabindex="0" data-chan="#&amp;&quot;&gt;bug">#&amp;&quot;&gt;bug</span>',

View File

@ -371,4 +371,33 @@ describe("Link plugin", function() {
}
});
});
it("should fetch protocol-aware links", function(done) {
const message = this.irc.createMessage({
text: "//localhost:9002",
});
link(this.irc, this.network.channels[0], message);
this.irc.once("msg:preview", function(data) {
expect(data.preview.link).to.equal("http://localhost:9002");
done();
});
});
it("should not try to fetch links with wrong protocol", function() {
const message = this.irc.createMessage({
text: "ssh://example.com ftp://example.com irc://example.com http:////////example.com",
});
expect(message.previews).to.be.empty;
});
it("should not try to fetch links with username or password", function() {
const message = this.irc.createMessage({
text: "http://root:'some%pass'@hostname/database http://a:%p@c http://a:%p@example.com http://test@example.com",
});
expect(message.previews).to.be.empty;
});
});