-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: None
-
None
Description
ConnectionString.ExtractOptions percent-decodes the entire option value before ParseReadPreferenceTagSets splits it on "," and then ":". Any escaping of the separator characters is undone before the separators are used, so a tag name or value containing "," or ":" cannot survive a round-trip through the connection string. Depending on the characters involved the result is either an exception or a silently different tag set.
The emitted form is correct and unambiguous. dc:x%2Cy%3Az has exactly one reading: a single tag whose value contains a comma and a colon. The defect is on the parse side.
Steps to reproduce
Round-tripping a single tag through MongoUrlBuilder:
Tag("dc", "x,y:z") -> emits dc:x%2Cy%3Az -> parses back as TWO tags: dc=x, y=z Tag("dc", "ny,sf") -> emits dc:ny%2Csf -> throws MongoConfigurationException Tag("dc", "ny:1") -> emits dc:ny%3A1 -> throws MongoConfigurationException
Reserved characters that are not separators round-trip correctly: "&", "?" and "%" all survive.
The behaviour is also not self-consistent. A comma alone throws, while a comma plus a colon silently reshapes the tag set with no error signalled.
Root cause
ExtractOptions calls Uri.UnescapeDataString on the whole option value for every option except authmechanismproperties, then hands the decoded string to ParseOption. ParseReadPreferenceTagSets then splits that already-decoded string on "," and ":", so a percent-encoded separator has become a real separator by the time the split happens.
authMechanismProperties is the option that handles this correctly, and is the model to follow:
- ExtractOptions skips the wholesale decode for it.
- GetAuthMechanismProperties splits on "," first, then decodes each pair, then splits on the first ":".
- EscapeOptionValue skips re-encoding it on the way back out.
readPreferenceTags has none of that structure, but MongoUrlBuilder.ToString() now escapes its key and value halves as though it did, added in CSHARP-6171.
To dos
- readpreferencetags is added to the carve-out in ConnectionString.ExtractOptions so the raw value reaches the tag parser.
- ParseReadPreferenceTagSets splits on "," then on the first ":", then percent-decodes each half, mirroring GetAuthMechanismProperties.
- readpreferencetags is added to the carve-out in EscapeOptionValue so BuildResolvedConnectionString stays symmetric.
- A decision is recorded on whether Tag should reject names or values containing "," or ":" at construction. Tag currently only null-checks its arguments, so the driver accepts values it cannot represent. If rejection is chosen it replaces the round-trip requirement for those characters. Silently reshaping the tag set is not an acceptable outcome either way.
- Round-trip tests cover ",", ":", "&", "?" and "%" in both tag names and tag values.
Notes
- Not a regression from
CSHARP-6171. The raw pre-escaping forms produce identical results, so this predates that change. - Not a security issue.
CSHARP-6171closed the injection path, so a tag value can no longer escape into other connection options and the damage is confined to the tag grammar. Read preference tags also come from deployment configuration rather than untrusted input. - The silent reshape is the reason this is worth fixing rather than leaving. The caller gets back a different tag set than the one they built, with no error.