diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index f3c0d2a77..c221c8128 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -2,4 +2,6 @@ #### Improvements 🧹 +- Improves compiler's tooltip URL check. [#1390](https://github.com/terrastruct/d2/pull/1390) + #### Bugfixes ⛑️ diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 4718636c5..0825166d6 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -533,8 +533,8 @@ func (c *compiler) compileReserved(attrs *d2graph.Attributes, f *d2ir.Field) { } if attrs.Link != nil && attrs.Tooltip != nil { - _, err := url.ParseRequestURI(attrs.Tooltip.Value) - if err == nil { + u, err := url.ParseRequestURI(attrs.Tooltip.Value) + if err == nil && u.Host != "" { c.errorf(scalar, "Tooltip cannot be set to URL when link is also set (for security)") } } diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 19f41a60b..56f5b0088 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -1505,6 +1505,11 @@ x -> y: { text: `x: {link: https://not-google.com; tooltip: https://google.com}`, expErr: `d2/testdata/d2compiler/TestCompile/no_url_link_and_url_tooltip_concurrently.d2:1:44: Tooltip cannot be set to URL when link is also set (for security)`, }, + { + name: "url_link_non_url_tooltip_ok", + text: `x: {link: https://not-google.com; tooltip: note: url.ParseRequestURI might see this as a URL}`, + expErr: ``, + }, { name: "url_link_and_not_url_tooltip_concurrently", text: `x: {link: https://google.com; tooltip: hello world}`, diff --git a/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.exp.json b/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.exp.json new file mode 100644 index 000000000..55017b2a1 --- /dev/null +++ b/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.exp.json @@ -0,0 +1,184 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:0:0-0:93:93", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:0:0-0:93:93", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:3:3-0:93:93", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:4:4-0:32:32", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:4:4-0:8:8", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:4:4-0:8:8", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:10:10-0:32:32", + "value": [ + { + "string": "https://not-google.com", + "raw_string": "https://not-google.com" + } + ] + } + } + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:34:34-0:92:92", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:34:34-0:41:41", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:34:34-0:41:41", + "value": [ + { + "string": "tooltip", + "raw_string": "tooltip" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:43:43-0:92:92", + "value": [ + { + "string": "note: url.ParseRequestURI might see this as a URL", + "raw_string": "note: url.ParseRequestURI might see this as a URL" + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "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": "x", + "id_val": "x", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_non_url_tooltip_ok.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "x" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "tooltip": { + "value": "note: url.ParseRequestURI might see this as a URL" + }, + "link": { + "value": "https://not-google.com" + }, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +}