diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 843d8c331..1ede35ee1 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -21,6 +21,7 @@ - Compiler: - `link`s can be set to root path, e.g. `/xyz`. [#2357](https://github.com/terrastruct/d2/issues/2357) - When importing a file, attempt resolving substitutions at the imported file scope first [#2482](https://github.com/terrastruct/d2/pull/2482) + - validate gradient color stops. [#2492](https://github.com/terrastruct/d2/pull/2492) - Parser: - impose max key length. It's almost certainly a mistake if an ID gets too long, e.g. missing quotes [#2465](https://github.com/terrastruct/d2/pull/2465) - Render: diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 61123712b..8a3224aab 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -3956,6 +3956,14 @@ svc_1.t2 -> b: do with B tassert.Equal(t, "d2/testdata/d2compiler/TestCompile/meow.d2", g.Layers[0].Layers[0].AST.Range.Path) }, }, + { + name: "invalid_gradient_color_stop", + text: ` + x + x.style.fill: "linear-gradient(#ggg, #000)" + `, + expErr: `d2/testdata/d2compiler/TestCompile/invalid_gradient_color_stop.d2:3:19: expected "fill" to be a valid named color ("orange"), a hex code ("#f0ff3a"), or a gradient ("linear-gradient(red, blue)")`, + }, } for _, tc := range testCases { diff --git a/lib/color/color.go b/lib/color/color.go index 7417a2b9d..701cc0b3d 100644 --- a/lib/color/color.go +++ b/lib/color/color.go @@ -512,7 +512,18 @@ var NamedColors = []string{ var ColorHexRegex = regexp.MustCompile(`^#(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$`) func ValidColor(color string) bool { - if !go2.Contains(NamedColors, strings.ToLower(color)) && !ColorHexRegex.MatchString(color) && !IsGradient(color) { + if IsGradient(color) { + gradient, err := ParseGradient(color) + if err != nil { + return false + } + for _, colorStop := range gradient.ColorStops { + _, err = csscolorparser.Parse(colorStop.Color) + if err != nil { + return false + } + } + } else if !go2.Contains(NamedColors, strings.ToLower(color)) && !ColorHexRegex.MatchString(color) { return false } diff --git a/testdata/d2compiler/TestCompile/invalid_gradient_color_stop.exp.json b/testdata/d2compiler/TestCompile/invalid_gradient_color_stop.exp.json new file mode 100644 index 000000000..49f1fffea --- /dev/null +++ b/testdata/d2compiler/TestCompile/invalid_gradient_color_stop.exp.json @@ -0,0 +1,11 @@ +{ + "graph": null, + "err": { + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile/invalid_gradient_color_stop.d2,2:18:25-2:47:54", + "errmsg": "d2/testdata/d2compiler/TestCompile/invalid_gradient_color_stop.d2:3:19: expected \"fill\" to be a valid named color (\"orange\"), a hex code (\"#f0ff3a\"), or a gradient (\"linear-gradient(red, blue)\")" + } + ] + } +}