Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default defineConfig({
"./src/mermaid.css",
"./src/table.css",
"./src/tailwind.css",
"./src/tooltips.css",
],
pagination: false,
plugins: runLinkCheck
Expand Down
33 changes: 0 additions & 33 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
"he": "^1.2.0",
"instantsearch.css": "^8.5.0",
"instantsearch.js": "^4.74.0",
"littlefoot": "^4.1.1",
"lz-string": "^1.5.0",
"marked": "^14.1.1",
"mermaid": "^11.3.0",
Expand Down
21 changes: 4 additions & 17 deletions src/components/GlossaryTooltip.astro
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
import { z } from "astro:schema";
import { getGlossaryEntry } from "~/util/glossary";
import "tippy.js/dist/tippy.css";
import { marked } from "marked";

type Props = z.infer<typeof props>;
Expand Down Expand Up @@ -31,31 +30,19 @@ definition = definition.split(/\r?\n/)[0];
data-tooltip
data-content={marked.parse(definition)}
class="border-b-2 border-dashed border-accent-600"
tabindex="0"
>{
link ? (
<a href={link}><slot /></a>
) : (
<slot />
)
}</span><script>
import tippy from "tippy.js";
import { addTooltip } from "~/util/tippy";

const tooltips = document.querySelectorAll<HTMLSpanElement>("[data-tooltip]");

for (const tooltip of tooltips) {
tippy(`#${CSS.escape(tooltip.id)}`, {
content: tooltip.dataset.content,
allowHTML: true,
interactive: true,
placement: "auto",
arrow: false,
});
addTooltip(tooltip, tooltip.dataset.content as string);
}
</script><style is:global>
.tippy-box {
background-color: var(--sl-color-bg-nav);
border-color: var(--sl-color-text);
border: 0.1em solid;
color: var(--sl-color-white);
}
</style>
</script>
50 changes: 3 additions & 47 deletions src/components/overrides/Head.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { Props } from "@astrojs/starlight/props";
import Default from "@astrojs/starlight/components/Head.astro";
import { differenceInCalendarDays } from "date-fns";
import "littlefoot/dist/littlefoot.css";
import "tippy.js/dist/tippy.css";

import { getEntry } from "astro:content";
import { getPageDescription } from "~/util/description";
Expand Down Expand Up @@ -164,51 +164,7 @@ if (Astro.props.entry.data.external_link) {
}
---

<script>
import littlefoot from "littlefoot";

littlefoot();
document
.querySelectorAll("span.littlefoot")
.forEach((x) => x.classList.add("not-content"));
</script>
<script>
import mermaid from "mermaid";

const diagrams = document.querySelectorAll<HTMLPreElement>("pre.mermaid");

let init = false;

async function render() {
const theme =
document.documentElement.getAttribute("data-theme") === "light"
? "neutral"
: "dark";

for (const diagram of diagrams) {
if (!init) {
diagram.setAttribute("data-diagram", diagram.textContent as string);
}

const def = diagram.getAttribute("data-diagram") as string;

mermaid.initialize({ startOnLoad: false, theme });
await mermaid
.render(`mermaid-${crypto.randomUUID()}`, def)
.then(({ svg }) => (diagram.innerHTML = svg));

diagram.setAttribute("data-processed", "true");
}

init = true;
}

const obs = new MutationObserver(() => render());

obs.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
</script>
<script src="src/scripts/footnotes.ts"></script>
<script src="src/scripts/mermaid.ts"></script>
<script src="src/scripts/analytics.ts"></script>
<Default {...Astro.props}><slot /></Default>
2 changes: 2 additions & 0 deletions src/components/overrides/PageSidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ if (Astro.props.toc) {
];

for (const header of headers) {
if (header.id === "footnote-label") continue;

const depth = headerDepth(header);

const title = he.decode(header.innerText);
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/style-guide/components/footnotes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ To create footnotes, use the following syntax:
```mdx live
You can use GitHub Flavoured Markdown footnotes![^1]

[^1]: Powered by [littlefoot.js](https://littlefoot.js.org)
[^1]: Powered by [tippy.js](https://atomiks.github.io/tippyjs/)
```

Refer to [Footnotes](/style-guide/formatting/footnotes/) to learn when to use them.
26 changes: 26 additions & 0 deletions src/scripts/footnotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { addTooltip } from "~/util/tippy";

const footnotes = document.querySelector("section.footnotes");

if (footnotes) {
const notes = footnotes.querySelectorAll("li");

for (const note of notes) {
const content = note.querySelector("p") as HTMLParagraphElement;

const fnrefs = document.querySelectorAll<HTMLAnchorElement>(
`#${note.id.replace("fn", "fnref")}`,
);

for (const fnref of fnrefs) {
addTooltip(fnref, content.innerHTML);

fnref.classList.add("footnote");

fnref.setAttribute("tabindex", "0");
fnref.removeAttribute("href");
}
}

footnotes.remove();
}
36 changes: 36 additions & 0 deletions src/scripts/mermaid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import mermaid from "mermaid";

const diagrams = document.querySelectorAll<HTMLPreElement>("pre.mermaid");

let init = false;

async function render() {
const theme =
document.documentElement.getAttribute("data-theme") === "light"
? "neutral"
: "dark";

for (const diagram of diagrams) {
if (!init) {
diagram.setAttribute("data-diagram", diagram.textContent as string);
}

const def = diagram.getAttribute("data-diagram") as string;

mermaid.initialize({ startOnLoad: false, theme });
await mermaid
.render(`mermaid-${crypto.randomUUID()}`, def)
.then(({ svg }) => (diagram.innerHTML = svg));

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML

[DOM text](1) is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI about 1 year ago

To fix the problem, we need to ensure that the content being inserted into innerHTML is properly sanitized. Since the mermaid.render function is used to generate the SVG content, we should ensure that this content is safe. If mermaid does not guarantee the safety of its output, we should sanitize the SVG content before inserting it into the DOM.

The best way to fix this without changing existing functionality is to use a library like DOMPurify to sanitize the SVG content before setting it as innerHTML. This ensures that any potentially harmful content is removed.

Suggested changeset 2
src/scripts/mermaid.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/scripts/mermaid.ts b/src/scripts/mermaid.ts
--- a/src/scripts/mermaid.ts
+++ b/src/scripts/mermaid.ts
@@ -1,2 +1,3 @@
 import mermaid from "mermaid";
+import DOMPurify from "dompurify";
 
@@ -22,3 +23,3 @@
 			.render(`mermaid-${crypto.randomUUID()}`, def)
-			.then(({ svg }) => (diagram.innerHTML = svg));
+			.then(({ svg }) => (diagram.innerHTML = DOMPurify.sanitize(svg)));
 
EOF
@@ -1,2 +1,3 @@
import mermaid from "mermaid";
import DOMPurify from "dompurify";

@@ -22,3 +23,3 @@
.render(`mermaid-${crypto.randomUUID()}`, def)
.then(({ svg }) => (diagram.innerHTML = svg));
.then(({ svg }) => (diagram.innerHTML = DOMPurify.sanitize(svg)));

package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -85,3 +85,6 @@
 		"node": "22.8.0"
-	}
+	},
+	"dependencies": {
+    "dompurify": "^3.1.7"
+  }
 }
EOF
@@ -85,3 +85,6 @@
"node": "22.8.0"
}
},
"dependencies": {
"dompurify": "^3.1.7"
}
}
This fix introduces these dependencies
Package Version Security advisories
dompurify (npm) 3.1.7 None
Copilot is powered by AI and may make mistakes. Always verify output.

diagram.setAttribute("data-processed", "true");
}

init = true;
}

const obs = new MutationObserver(() => render());

obs.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
12 changes: 12 additions & 0 deletions src/tooltips.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@tailwind utilities;

.footnote {
@apply text-xs !text-[--sl-color-accent] font-semibold p-1 -m-1;
}

.tippy-box {
background-color: var(--sl-color-bg-nav);
border-color: var(--sl-color-text);
border: 0.1em solid;
color: var(--sl-color-white);
}
18 changes: 18 additions & 0 deletions src/util/tippy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import tippy from "tippy.js";

export function addTooltip(element: HTMLElement, content: string) {
const id = "#" + CSS.escape(element.id);

tippy(id, {
content,
allowHTML: true,
interactive: true,
placement: "auto",
arrow: false,
// This is imperfect as it stops you from tabbing into
// links inside the tooltip, but stops tooltips being
// cutoff by the sidebar
// https://atomiks.github.io/tippyjs/v6/faq/#my-tooltip-appears-cut-off-or-is-not-showing-at-all
appendTo: document.body,
});
}