diff --git a/src/components/Markdown.svelte b/src/components/Markdown.svelte
index 0d7c11b74..67bcd8766 100644
--- a/src/components/Markdown.svelte
+++ b/src/components/Markdown.svelte
@@ -129,6 +129,7 @@
const url = new URL(e.href);
if (url.origin !== window.origin) {
e.target = "_blank";
+ e.rel = "noopener noreferrer";
}
} catch (e) {
console.warn("Not able to parse url", e);
diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts
index c4d0bf951..430af0b56 100644
--- a/src/lib/markdown.ts
+++ b/src/lib/markdown.ts
@@ -2,6 +2,7 @@ import type { Config } from "dompurify";
import type { MarkedExtension, Tokens } from "marked";
import type { Route } from "@app/lib/router";
+import escape from "lodash/escape";
import footnoteMarkedExtension from "marked-footnote";
import katexMarkedExtension from "marked-katex-extension";
import linkifyMarkedExtension from "marked-linkify-it";
@@ -29,6 +30,7 @@ export const sanitizeConfig: Config = {
"href",
"id",
"name",
+ "rel",
"src",
"target",
"text",
@@ -106,10 +108,8 @@ export class Renderer extends BaseRenderer {
const text = this.parser.parseInline(tokens);
if (href.startsWith("#")) {
// By lowercasing we avoid casing mismatches, between headings and links.
- return `${text}`;
- }
-
- if (this.#route.resource === "repo.source" && !isUrl(href)) {
+ href = href.toLowerCase();
+ } else if (this.#route.resource === "repo.source" && !isUrl(href)) {
href = routeToPath({
...this.#route,
path: canonicalize(href, this.#route.path || "README.md"),
@@ -117,7 +117,12 @@ export class Renderer extends BaseRenderer {
});
}
- return `${text}`;
+ const attrs = [`href="${escape(href)}"`];
+ if (title) {
+ attrs.push(`title="${escape(title)}"`);
+ }
+
+ return `${text}`;
}
}