What's wrong
Semantics.Color/Color.Conversions.cs FromHex (L49-L70) checks only the string's length, then parses each channel with ParseByte (L99-L100): Convert.ToByte(hex.Substring(index, 2), 16). Convert.ToByte(string, 16) has two quirks that leak through:
- A leading
+ is accepted, so Color.FromHex("#+F+F+F").ToHex() returns "#0F0F0F" and invalid input is accepted without complaint. The same applies to "+F" inside any 6- or 8-digit string.
- A non-hex character throws
FormatException, so FromHex("#GGGGGG") and FromHex("#0x0x0x") both throw it. FromHex("#-1-1-1") throws ArgumentException instead. The exception type therefore depends on which bad character appears.
The XML docs for FromHex list only ArgumentNullException and ArgumentException, and CLAUDE.md says to throw ArgumentException for validation failures, not FormatException. A caller that validates user or theme input with catch (ArgumentException) crashes on #GGGGGG and silently accepts #+F+F+F.
All of these were reproduced by compiling Semantics.Color into a scratch console app.
Suggested fix
- After length normalisation, check that every character is
[0-9A-Fa-f], and throw ArgumentException(..., nameof(hex)) if one is not.
- Parse each channel with
byte.Parse(span, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), which rejects signs.
- Add tests:
#+F+F+F, #GGGGGG, #0x0x0x and #-1-1-1 all throw ArgumentException, and valid 3/6/8-digit inputs in either case still round-trip.
What's wrong
Semantics.Color/Color.Conversions.csFromHex(L49-L70) checks only the string's length, then parses each channel withParseByte(L99-L100):Convert.ToByte(hex.Substring(index, 2), 16).Convert.ToByte(string, 16)has two quirks that leak through:+is accepted, soColor.FromHex("#+F+F+F").ToHex()returns"#0F0F0F"and invalid input is accepted without complaint. The same applies to"+F"inside any 6- or 8-digit string.FormatException, soFromHex("#GGGGGG")andFromHex("#0x0x0x")both throw it.FromHex("#-1-1-1")throwsArgumentExceptioninstead. The exception type therefore depends on which bad character appears.The XML docs for
FromHexlist onlyArgumentNullExceptionandArgumentException, and CLAUDE.md says to throwArgumentExceptionfor validation failures, notFormatException. A caller that validates user or theme input withcatch (ArgumentException)crashes on#GGGGGGand silently accepts#+F+F+F.All of these were reproduced by compiling
Semantics.Colorinto a scratch console app.Suggested fix
[0-9A-Fa-f], and throwArgumentException(..., nameof(hex))if one is not.byte.Parse(span, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), which rejects signs.#+F+F+F,#GGGGGG,#0x0x0xand#-1-1-1all throwArgumentException, and valid 3/6/8-digit inputs in either case still round-trip.