markdown: sanitize links

This commit is contained in:
Alexander Wang 2024-11-18 08:00:57 -08:00
parent ad629356cb
commit b4382a6793
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
5 changed files with 153 additions and 1 deletions

View file

@ -9,3 +9,4 @@
#### Bugfixes ⛑️
- Imports: fixes using substitutions in `icon` values [#2207](https://github.com/terrastruct/d2/pull/2207)
- Markdown: fixes ampersands in URLs in markdown [#2219](https://github.com/terrastruct/d2/pull/2219)

View file

@ -935,6 +935,13 @@ b.(x -> y)[0]: two
}
},
},
{
name: "markdown_ampersand",
text: `memo: |md
<a href="https://www.google.com/search?q=d2&newwindow=1&amp;bar">d2</a>
|
`,
},
{
name: "unsemantic_markdown",

26
lib/textmeasure/links.go Normal file
View file

@ -0,0 +1,26 @@
package textmeasure
import (
"fmt"
"regexp"
"strings"
)
func sanitizeLinks(input string) (string, error) {
re := regexp.MustCompile(`href="([^"]*)"`)
return re.ReplaceAllStringFunc(input, func(href string) string {
matches := re.FindStringSubmatch(href)
if len(matches) < 2 {
return href
}
value := matches[1]
value = strings.ReplaceAll(value, "&amp;", "TEMP_AMP")
value = strings.ReplaceAll(value, "&", "&amp;")
value = strings.ReplaceAll(value, "TEMP_AMP", "&amp;")
return fmt.Sprintf(`href="%s"`, value)
}), nil
}

View file

@ -83,7 +83,11 @@ func RenderMarkdown(m string) (string, error) {
if err := markdownRenderer.Convert([]byte(m), &output); err != nil {
return "", err
}
return output.String(), nil
sanitized, err := sanitizeLinks(output.String())
if err != nil {
return "", err
}
return sanitized, nil
}
func init() {

View file

@ -0,0 +1,114 @@
{
"graph": {
"name": "",
"isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:0:0-3:0:86",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:0:0-2:1:85",
"key": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:0:0-0:4:4",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:0:0-0:4:4",
"value": [
{
"string": "memo",
"raw_string": "memo"
}
]
}
}
]
},
"primary": {},
"value": {
"block_string": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:6:6-2:1:85",
"quote": "",
"tag": "md",
"value": "<a href=\"https://www.google.com/search?q=d2&newwindow=1&amp;bar\">d2</a>"
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
"edges": null,
"objects": [
{
"id": "memo",
"id_val": "memo",
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:0:0-0:4:4",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/markdown_ampersand.d2,0:0:0-0:4:4",
"value": [
{
"string": "memo",
"raw_string": "memo"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "<a href=\"https://www.google.com/search?q=d2&newwindow=1&amp;bar\">d2</a>"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"language": "markdown",
"shape": {
"value": "text"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
]
},
"err": null
}