DEV: Redo `DiscourseURL.isInternal()` (#26504)

Simplify the implementation and make it return `true` for prefix-less relative urls, like `docs`
This commit is contained in:
Jarek Radosz 2024-04-04 15:39:51 +02:00 committed by GitHub
parent 39ae85b0e7
commit c117f6de04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 38 deletions

View File

@ -286,27 +286,32 @@ const DiscourseURL = EmberObject.extend({
// Determines whether a URL is internal or not
isInternal(url) {
if (url && url.length) {
if (!url?.length) {
return false;
}
if (url.startsWith("//")) {
url = "http:" + url;
}
if (url.startsWith("#")) {
return true;
}
if (url.startsWith("/")) {
return true;
}
if (url.startsWith(this.origin())) {
return true;
}
if (url.replace(/^http/, "https").startsWith(this.origin())) {
return true;
}
if (url.replace(/^https/, "http").startsWith(this.origin())) {
return true;
}
if (url.startsWith("http://") || url.startsWith("https://")) {
return (
url.startsWith(this.origin) ||
url.replace(/^http/, "https").startsWith(this.origin) ||
url.replace(/^https/, "http").startsWith(this.origin)
);
}
try {
const parsedUrl = new URL(url, this.origin);
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
return false;
}
} catch {
return false;
}
return true;
},
/**
@ -388,8 +393,8 @@ const DiscourseURL = EmberObject.extend({
},
// This has been extracted so it can be tested.
origin() {
let prefix = getURL("/");
get origin() {
const prefix = getURL("/");
return window.location.origin + (prefix === "/" ? "" : prefix);
},

View File

@ -81,7 +81,7 @@ module("Unit | Utility | click-track", function (hooks) {
assert.true(false, "should not request a csrf token");
});
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
sinon.stub(DiscourseURL, "origin").get(() => "http://discuss.domain.com");
const done = assert.async();
pretender.post("/clicks/track", (request) => {
@ -101,7 +101,7 @@ module("Unit | Utility | click-track", function (hooks) {
});
test("does not track attachments", async function (assert) {
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
sinon.stub(DiscourseURL, "origin").get(() => "http://discuss.domain.com");
pretender.post("/clicks/track", () => assert.true(false));

View File

@ -14,54 +14,77 @@ module("Unit | Utility | url", function (hooks) {
setupTest(hooks);
test("isInternal with a HTTP url", function (assert) {
sinon.stub(DiscourseURL, "origin").returns("http://eviltrout.com");
sinon.stub(DiscourseURL, "origin").get(() => "http://eviltrout.com");
assert.notOk(DiscourseURL.isInternal(null), "a blank URL is not internal");
assert.ok(DiscourseURL.isInternal("/test"), "relative URLs are internal");
assert.ok(
assert.false(DiscourseURL.isInternal(null), "a blank URL is not internal");
assert.true(DiscourseURL.isInternal("/test"), "relative URLs are internal");
assert.true(
DiscourseURL.isInternal("docs"),
"non-prefixed relative URLs are internal"
);
assert.true(DiscourseURL.isInternal("#foo"), "anchor URLs are internal");
assert.true(
DiscourseURL.isInternal("//eviltrout.com"),
"a url on the same host is internal (protocol-less)"
);
assert.ok(
assert.true(
DiscourseURL.isInternal("http://eviltrout.com/tophat"),
"a url on the same host is internal"
);
assert.ok(
assert.true(
DiscourseURL.isInternal("https://eviltrout.com/moustache"),
"a url on a HTTPS of the same host is internal"
);
assert.notOk(
assert.false(
DiscourseURL.isInternal("//twitter.com.com"),
"a different host is not internal (protocol-less)"
);
assert.notOk(
assert.false(
DiscourseURL.isInternal("http://twitter.com"),
"a different host is not internal"
);
assert.false(
DiscourseURL.isInternal("ftp://eviltrout.com"),
"a different protocol is not internal"
);
assert.false(
DiscourseURL.isInternal("ftp::/eviltrout.com"),
"an invalid URL is not internal"
);
});
test("isInternal with a HTTPS url", function (assert) {
sinon.stub(DiscourseURL, "origin").returns("https://eviltrout.com");
assert.ok(
sinon.stub(DiscourseURL, "origin").get(() => "https://eviltrout.com");
assert.true(
DiscourseURL.isInternal("http://eviltrout.com/monocle"),
"HTTPS urls match HTTP urls"
);
assert.true(
DiscourseURL.isInternal("https://eviltrout.com/monocle"),
"HTTPS urls match HTTPS urls"
);
});
test("isInternal on subfolder install", function (assert) {
sinon.stub(DiscourseURL, "origin").returns("http://eviltrout.com/forum");
assert.notOk(
sinon.stub(DiscourseURL, "origin").get(() => "http://eviltrout.com/forum");
assert.false(
DiscourseURL.isInternal("http://eviltrout.com"),
"the host root is not internal"
);
assert.notOk(
assert.false(
DiscourseURL.isInternal("http://eviltrout.com/tophat"),
"a url on the same host but on a different folder is not internal"
);
assert.ok(
assert.true(
DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"),
"a url on the same host and on the same folder is internal"
);
assert.true(DiscourseURL.isInternal("/test"), "relative URLs are internal");
assert.true(
DiscourseURL.isInternal("docs"),
"non-prefixed relative URLs are internal"
);
assert.true(DiscourseURL.isInternal("#foo"), "anchor URLs are internal");
});
test("userPath", function (assert) {