summaryrefslogtreecommitdiff
path: root/layouts/_markup/render-link.html
diff options
context:
space:
mode:
authorNicholas Johnson <>2026-09-18 00:00:00 +0000
committerNicholas Johnson <>2026-09-19 16:34:32 -0400
commit61d07d87a9b54cd102699e065a349d5c3fc4a06fbe7a96c255e43aa182c02be7 (patch)
tree381fa4edb4bc9a5ac37749dcc26faa19f16b8c8a49a544caa13d239f7b730d03 /layouts/_markup/render-link.html
parent424afe0f8fed829758cc4d09722b4cfd98268734f82b799962369478389cf5dc (diff)
downloadhugo-theme-journal-61d07d87a9b54cd102699e065a349d5c3fc4a06fbe7a96c255e43aa182c02be7.tar.gz
hugo-theme-journal-61d07d87a9b54cd102699e065a349d5c3fc4a06fbe7a96c255e43aa182c02be7.zip
Drop scripting URL schemes from links and images
Both render hooks passed the destination through safeURL, which bypasses the sanitising that html/template would otherwise apply, so a javascript: or data:text/html destination in Markdown became a live scripting URL in the page. That matters for any site built from content its author did not write. safeURL cannot simply be dropped: it is what allows gemini:// and other schemes html/template does not recognise to survive, which this theme depends on. Reject the scripting schemes explicitly instead, render the link text on its own, and warn during the build so the author can see what was dropped. data: is still allowed for images, where it is a legitimate way to inline one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'layouts/_markup/render-link.html')
-rw-r--r--layouts/_markup/render-link.html11
1 files changed, 10 insertions, 1 deletions
diff --git a/layouts/_markup/render-link.html b/layouts/_markup/render-link.html
index 8fb3c26..bb507d2 100644
--- a/layouts/_markup/render-link.html
+++ b/layouts/_markup/render-link.html
@@ -1,3 +1,12 @@
{{ $emoji := cond (and (not (.Page.Params.makerefs | default true)) (.Page.Params.showlinkemoji | default true)) (emojify ":link: ") "" -}}
{{ $isExternalLink := (urls.Parse .Destination).IsAbs -}}
-{{ $emoji }}<a class="link {{ if $isExternalLink }}link--external{{ else }}link--internal{{ end }}" href="{{ .Destination | safeURL }}"{{ with .Title }} title="{{ . }}"{{ end }} rel="{{ if $isExternalLink }}external {{ end }}noreferrer">{{ .Text }}</a> \ No newline at end of file
+
+{{/* .Destination is marked safe below so that schemes the web knows nothing */ -}}
+{{/* about, such as gemini://, survive; scripting schemes are dropped instead */ -}}
+{{ $scheme := lower (urls.Parse .Destination).Scheme -}}
+{{ if in (slice "javascript" "vbscript" "data") $scheme -}}
+{{ warnidf "unsafe-link-scheme" "dropped %s: link in %s" $scheme .Page.Path -}}
+{{ .Text -}}
+{{ else -}}
+{{ $emoji }}<a class="link {{ if $isExternalLink }}link--external{{ else }}link--internal{{ end }}" href="{{ .Destination | safeURL }}"{{ with .Title }} title="{{ . }}"{{ end }} rel="{{ if $isExternalLink }}external {{ end }}noreferrer">{{ .Text }}</a>
+{{- end -}}