diff --git a/d2compiler/compile.go b/d2compiler/compile.go index caee67967..6ad065ce0 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -841,8 +841,15 @@ func (c *compiler) compileEdgeLinks(edge *d2graph.Edge, m *d2ir.Map) { return } for _, field := range m.Fields { - if field.Name == "link" && edge.Label.Value == "" { - edge.Label.Value = field.Primary_.String() + if field.Name == "link" && field.Primary_ != nil { + if edge.Label.Value == "" { + edge.Label.Value = field.Primary_.String() + } else if edge.Label.Value != "" { + u, err := url.ParseRequestURI(edge.Label.Value) + if err == nil && u.Host != "" { + c.errorf(field.Primary_.Value, "Label 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 9f4e04e49..ba49c8781 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -1639,6 +1639,32 @@ a.style.fill: null } }, }, + { + name: "no_url_link_and_path_url_label_concurrently", + text: `x -> y: https://google.com {link: https://not-google.com }`, + expErr: `d2/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.d2:1:35: Label cannot be set to URL when link is also set (for security)`, + }, + { + name: "url_link_and_path_url_label_ok", + text: `x -> y note: {link: https://not-google.com}`, + expErr: ``, + }, + { + name: "url_link_and_path_url_label_concurrently", + text: `x -> y: hello world {link: https://google.com}`, + assertions: func(t *testing.T, g *d2graph.Graph) { + if len(g.Edges) != 1 { + t.Fatal(len(g.Edges)) + } + if g.Edges[0].Link.Value != "https://google.com" { + t.Fatal(g.Edges[0].Link.Value) + } + + if g.Edges[0].Label.Value != "hello world" { + t.Fatal(g.Edges[0].Label.Value) + } + }, + }, { name: "nil_scope_obj_regression", diff --git a/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.exp.json b/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.exp.json new file mode 100644 index 000000000..be462dfa3 --- /dev/null +++ b/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.exp.json @@ -0,0 +1,11 @@ +{ + "graph": null, + "err": { + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.d2,0:34:34-0:56:56", + "errmsg": "d2/testdata/d2compiler/TestCompile/no_url_link_and_path_url_label_concurrently.d2:1:35: Label cannot be set to URL when link is also set (for security)" + } + ] + } +} diff --git a/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.exp.json b/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.exp.json new file mode 100644 index 000000000..ae1d35226 --- /dev/null +++ b/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.exp.json @@ -0,0 +1,257 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:46:46", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:46:46", + "edges": [ + { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:6:6", + "src": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "src_arrow": "", + "dst": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:5:5-0:6:6", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:5:5-0:6:6", + "value": [ + { + "string": "y", + "raw_string": "y" + } + ] + } + } + ] + }, + "dst_arrow": ">" + } + ], + "primary": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:8:8-0:19:19", + "value": [ + { + "string": "hello world", + "raw_string": "hello world" + } + ] + } + }, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:20:20-0:46:46", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:21:21-0:45:45", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:21:21-0:25:25", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:21:21-0:25:25", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:27:27-0:45:45", + "value": [ + { + "string": "https://google.com", + "raw_string": "https://google.com" + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "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": [ + { + "index": 0, + "isCurve": false, + "src_arrow": false, + "dst_arrow": true, + "references": [ + { + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "hello world" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "https://google.com" + }, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ], + "objects": [ + { + "id": "x", + "id_val": "x", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "x" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "id": "y", + "id_val": "y", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:5:5-0:6:6", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_concurrently.d2,0:5:5-0:6:6", + "value": [ + { + "string": "y", + "raw_string": "y" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "y" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +} diff --git a/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.exp.json b/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.exp.json new file mode 100644 index 000000000..249fd2694 --- /dev/null +++ b/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.exp.json @@ -0,0 +1,247 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:43:43", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:43:43", + "edges": [ + { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:11:11", + "src": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "src_arrow": "", + "dst": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:5:5-0:11:11", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:5:5-0:11:11", + "value": [ + { + "string": "y note", + "raw_string": "y note" + } + ] + } + } + ] + }, + "dst_arrow": ">" + } + ], + "primary": {}, + "value": { + "map": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:13:13-0:43:43", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:14:14-0:42:42", + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:14:14-0:18:18", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:14:14-0:18:18", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:20:20-0:42:42", + "value": [ + { + "string": "https://not-google.com", + "raw_string": "https://not-google.com" + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "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": [ + { + "index": 0, + "isCurve": false, + "src_arrow": false, + "dst_arrow": true, + "references": [ + { + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "https://not-google.com" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "https://not-google.com" + }, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ], + "objects": [ + { + "id": "x", + "id_val": "x", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "x" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "id": "y note", + "id_val": "y note", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:5:5-0:11:11", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/url_link_and_path_url_label_ok.d2,0:5:5-0:11:11", + "value": [ + { + "string": "y note", + "raw_string": "y note" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "y note" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +}