From 6c100083c29fb0ccf0cc52e8767e540a260d9468 Mon Sep 17 00:00:00 2001
From: Gusted
Date: Mon, 19 Feb 2024 20:40:53 +0100
Subject: [PATCH] [BUG] Restrict when to make link absolute in markdown
- Backport of #2403
- In markdown, links are proccessed to be made absolute against the
relevant base in that context. Such that `./src` will be transformed
into `http://example.com/owner/repo/src/branch/main/src`.
- Don't try to make the link absolute if the link has a schema that's
defined in `[markdown].CUSTOM_URL_SCHEMES`, because they can't be made
absolute and doing so could lead to problems (see test case, double
slash was transformed to single slash).
- Adds unit test.
- Resolves https://codeberg.org/Codeberg/Community/issues/1489
(cherry picked from commit 65b9a959b8aa9cce954cdc21b70ec6e2d27cd0f9)
---
modules/markup/markdown/goldmark.go | 15 +++++++++----
modules/markup/markdown/markdown_test.go | 27 ++++++++++++++++++++++++
2 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go
index 3dc5530e00..b650854971 100644
--- a/modules/markup/markdown/goldmark.go
+++ b/modules/markup/markdown/goldmark.go
@@ -7,6 +7,7 @@ import (
"bytes"
"fmt"
"regexp"
+ "slices"
"strings"
"code.gitea.io/gitea/modules/container"
@@ -131,11 +132,17 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
case *ast.Link:
// Links need their href to munged to be a real value
link := v.Destination
- if len(link) > 0 && !markup.IsLink(link) &&
- link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
- // special case: this is not a link, a hash link or a mailto:, so it's a
- // relative URL
+ // Do not process the link if it's not a link, starts with an hashtag
+ // (indicating it's an anchor link), starts with `mailto:` or any of the
+ // custom markdown URLs.
+ processLink := len(link) > 0 && !markup.IsLink(link) &&
+ link[0] != '#' && !bytes.HasPrefix(link, byteMailto) &&
+ !slices.ContainsFunc(setting.Markdown.CustomURLSchemes, func(s string) bool {
+ return bytes.HasPrefix(link, []byte(s+":"))
+ })
+
+ if processLink {
var base string
if ctx.IsWiki {
base = ctx.Links.WikiLink()
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index ee3ec0fda5..3cb11c9482 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/markup"
. "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
@@ -960,3 +961,29 @@ space
assert.Equal(t, c.Expected, result, "Unexpected result in testcase %v", i)
}
}
+
+func TestCustomMarkdownURL(t *testing.T) {
+ defer test.MockVariableValue(&setting.Markdown.CustomURLSchemes, []string{"abp"})()
+
+ setting.AppURL = AppURL
+ setting.AppSubURL = AppSubURL
+
+ test := func(input, expected string) {
+ buffer, err := RenderString(&markup.RenderContext{
+ Ctx: git.DefaultContext,
+ Links: markup.Links{
+ Base: setting.AppSubURL,
+ BranchPath: "branch/main",
+ },
+ }, input)
+ assert.NoError(t, err)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
+ }
+
+ test("[test](abp:subscribe?location=https://codeberg.org/filters.txt&title=joy)",
+ `test
`)
+
+ // Ensure that the schema itself without `:` is still made absolute.
+ test("[test](abp)",
+ `test
`)
+}