Backport #4531 to 0.5.x#4635
Conversation
There was a problem hiding this comment.
Pull request overview
Backports the adapter-id escaping fix for Slice1 service address encoding/decoding and locator resolution so adapter IDs with URI-sensitive characters are represented safely in ServiceAddress.Params.
Changes:
- Escapes Slice1-decoded adapter IDs before storing them in service address params.
- Unescapes
adapter-idparams when encoding Slice1 and when resolving locator locations. - Adds Slice proxy round-trip cases for escaped adapter-id values.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tests/IceRpc.Slice.Tests/ProxyTests.cs |
Adds adapter-id round-trip test cases with escaped URI characters. |
src/IceRpc.Slice/ServiceAddressSliceEncoderExtensions.cs |
Unescapes stored adapter-id params before writing Slice1 wire strings. |
src/IceRpc.Slice/ServiceAddressSliceDecoderExtensions.cs |
Escapes Slice1 wire adapter-id strings before adding them to URI params. |
src/IceRpc.Locator/LocatorInterceptor.cs |
Unescapes adapter-id params before constructing locator requests. |
src/IceRpc.Locator/Internal/LocationResolver.cs |
Unescapes adapter-id params during recursive locator resolution. |
Comments suppressed due to low confidence (1)
src/IceRpc.Locator/Internal/LocationResolver.cs:132
- This cached recursive resolution path also lacks coverage for escaped adapter IDs. The existing LocationResolver tests use 'bar', so they would pass even if this unescape call were removed; add a test that resolves a well-known service address with an escaped adapter-id and verifies the recursive lookup uses the raw adapter ID.
if (serviceAddress is not null && serviceAddress.Params.TryGetValue("adapter-id", out string? escapedAdapterId))
{
try
{
// Resolves adapter ID recursively, by checking first the cache. If we resolved the well-known
// service address, we request a cache refresh for the adapter ID.
(serviceAddress, _) = await PerformResolveAsync(
new Location { IsAdapterId = true, Value = Uri.UnescapeDataString(escapedAdapterId) },
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| [TestCase("ice:/hello?adapter-id=foo%23bar", null, SliceEncoding.Slice1)] // '#' | ||
| [TestCase("ice:/hello?adapter-id=foo%25bar", null, SliceEncoding.Slice1)] // '%' | ||
| [TestCase("ice:/hello?adapter-id=foo%20bar", null, SliceEncoding.Slice1)] // ' ' | ||
| [TestCase("ice:/hello?adapter-id=foo%23bar%25baz%20qux%26quux", null, SliceEncoding.Slice1)] // '#', '%', ' ', '&' |
| location = request.ServiceAddress.Params.TryGetValue("adapter-id", out string? escapedAdapterId) ? | ||
| new Location { IsAdapterId = true, Value = Uri.UnescapeDataString(escapedAdapterId) } : |
| if (serviceAddress is not null && serviceAddress.Params.TryGetValue("adapter-id", out string? escapedAdapterId)) | ||
| { | ||
| (serviceAddress, _) = await ResolveAsync( | ||
| new Location { IsAdapterId = true, Value = adapterId }, | ||
| new Location { IsAdapterId = true, Value = Uri.UnescapeDataString(escapedAdapterId) }, |
| if (decoder.DecodeString() is string adapterId && adapterId.Length > 0) | ||
| { | ||
| serviceAddressParams = serviceAddressParams.Add("adapter-id", adapterId); | ||
| serviceAddressParams = serviceAddressParams.Add("adapter-id", Uri.EscapeDataString(adapterId)); |
There was a problem hiding this comment.
Copilot is right here, we over-escape.
Putting this over-escaping bug aside, I am wondering if this bug-fix is truly semver-compatible.
Say your 0.5.1 application receives an adapter ID with an "invalid" adapter ID (what we're fixing here). It's still a proper string that you can use and compare with a hard-coded string literal safely. It's undesirable for a patch release to change this decoded string.
Backport of #4531 — escape adapter-id value in
ServiceAddressparams.Summary
The
adapter-idvalue decoded from a wire-format ice service address was inserted into the URI parameter string unescaped, allowing URI-component injection when the adapter-id contains#,&,%, or space (extra params, broken fragment, locator-resolution surprises). The fix:Uri.EscapeDataString(adapterId)when adding toParams.Uri.UnescapeDataString(escapedAdapterId)when reading back out.LocationResolver,LocatorInterceptor): unescape on read so theLocation.Valueis the raw adapter-id string the locator sees.Test plan
dotnet test --filter "FullyQualifiedName~ProxyTests"(IceRpc.Slice.Tests) — 38/38 pass (4 new adapter-id round-trip cases).dotnet test tests/IceRpc.Locator.Tests— 44/44 pass.Backport notes
Cherry-pick of 2385211. Adaptations:
src/IceRpc.Ice/Operations/IceProxyIceDecoderExtensions.cs/IceProxyIceEncoderExtensions.cson main — both live undersrc/IceRpc.Slice/ServiceAddressSliceDecoderExtensions.cs/ServiceAddressSliceEncoderExtensions.cson 0.5.x. Ported by hand to the two equivalent call sites.LocationResolver.cs,LocatorInterceptor.cs) merged cleanly.tests/IceRpc.Ice.Tests/ProxyTests.cs(a file that doesn't exist on 0.5.x). Added them totests/IceRpc.Slice.Tests/ProxyTests.cs:Decode_proxyinstead, withSliceEncoding.Slice1to exercise the same code path.Sibling Tier B PR sharing this file: #4634 (backport of #4548, wrap URI decode errors) — whichever lands first, the other will need a trivial rebase.