You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Remote marker images: a redirect bypasses the host policy, and the response size is unbounded #170
Remote marker images are checked against a host policy that rejects private, loopback and
link-local addresses (Android today; iOS after #166). The check runs once, on the URL the app
passed, and then the request is handed to a client that follows redirects:
HttpURLConnection follows same-protocol redirects by default (instanceFollowRedirects is not
set anywhere), and on iOS URLSession.shared follows them too
(MarkerImageLoader.swift:86). #166 already states this:
Neither platform re-checks a redirect. URLSession and HttpURLConnection both follow 302 by
default, so a public host can redirect to 169.254.169.254. […] it should be fixed on both
sides at once — worth its own issue rather than a one-platform patch here.
Two more holes in the same code:
DNS rebinding / TOCTOU: the host is resolved for the check (MarkerIconFactory.kt:460),
and resolved again by the connection. The second answer is never checked.
Unbounded read:stream.readBytes() (Android) and dataTask (iOS) read the whole body
into memory before decoding. A multi-hundred-megabyte response is an OOM.
Impact
https://allowed.example/pin.png → 302 Location: http://169.254.169.254/… (or any private IP)
is fetched despite the policy. The policy exists to stop exactly that (see the options in #130),
so today it is only a speed bump. The size problem is a crash vector for any app that shows
images from user-controlled URLs.
(connection as HttpURLConnection).instanceFollowRedirects = false; follow up to N (e.g. 3)
redirects by hand, running remoteMarkerUriRejectReason on every Location.
Connect to the address that passed the check (or re-check connection.url.host after connect).
Reject when contentLengthLong exceeds a limit (e.g. 5 MB) and stop reading at the same limit
when the header is absent.
A dedicated URLSession with a delegate: urlSession(_:task:willPerformHTTPRedirection:newRequest:)
runs the policy on the new request and cancels on rejection.
Enforce the size limit through expectedContentLength and a streaming data delegate.
Document the limit and the redirect behaviour next to the host policy in the README.
Acceptance criteria
A redirect to a rejected host is not followed on either platform, and is logged like a direct rejection
A redirect chain longer than the limit is rejected
Responses above the size limit are rejected without buffering them whole
Ordinary redirects between public hosts (CDNs) still work
Testing
JVM tests with a local MockWebServer (redirect to 127.0.0.1, oversized body, chunked body
without Content-Length); on iOS a URLProtocol stub in the example app or a unit target.
Problem
Remote marker images are checked against a host policy that rejects private, loopback and
link-local addresses (Android today; iOS after #166). The check runs once, on the URL the app
passed, and then the request is handed to a client that follows redirects:
HttpURLConnectionfollows same-protocol redirects by default (instanceFollowRedirectsis notset anywhere), and on iOS
URLSession.sharedfollows them too(
MarkerImageLoader.swift:86). #166 already states this:Two more holes in the same code:
MarkerIconFactory.kt:460),and resolved again by the connection. The second answer is never checked.
stream.readBytes()(Android) anddataTask(iOS) read the whole bodyinto memory before decoding. A multi-hundred-megabyte response is an OOM.
Impact
https://allowed.example/pin.png→302 Location: http://169.254.169.254/…(or any private IP)is fetched despite the policy. The policy exists to stop exactly that (see the options in #130),
so today it is only a speed bump. The size problem is a crash vector for any app that shows
images from user-controlled URLs.
Where
package/android/src/main/java/com/margelo/nitro/nitromaps/MarkerIconFactory.kt:243-264—loadRemoteIconpackage/android/src/main/java/com/margelo/nitro/nitromaps/MarkerIconFactory.kt:419-500— policypackage/ios/MarkerImageLoader.swift:80-101—loadRemoteviaURLSession.sharedSuggested fix
Android
(connection as HttpURLConnection).instanceFollowRedirects = false; follow up to N (e.g. 3)redirects by hand, running
remoteMarkerUriRejectReasonon everyLocation.connection.url.hostafter connect).contentLengthLongexceeds a limit (e.g. 5 MB) and stop reading at the same limitwhen the header is absent.
iOS (on top of #166)
URLSessionwith a delegate:urlSession(_:task:willPerformHTTPRedirection:newRequest:)runs the policy on the new request and cancels on rejection.
expectedContentLengthand a streaming data delegate.Document the limit and the redirect behaviour next to the host policy in the README.
Acceptance criteria
Testing
JVM tests with a local
MockWebServer(redirect to127.0.0.1, oversized body, chunked bodywithout
Content-Length); on iOS aURLProtocolstub in the example app or a unit target.