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
Android refuses to load a marker image from a private-network host. iOS loads any URL it can
parse. The two providers disagree about what a marker image is allowed to be, and neither
behaviour is documented.
Android, MarkerIconFactory.kt:
scheme must be http/https (L419-429)
userInfo is rejected (L431-433)
the host is resolved and rejected when loopback, any-local, link-local, site-local,
multicast, or IPv6 unique-local (L443-500)
iOS, MarkerImageLoader.swift:
privatestaticfunc loadRemote(
uri:String,
cacheKey:NSString,
image:MarkerImage,
completion:@escaping(UIImage?)->Void){guardlet url =URL(string: uri)else{ // L81 — the only check
completion(nil)return}
session.dataTask(with: url){ data, _, _ in // L86
No host check, no userInfo check. grep -rn "allowlist\|isSiteLocal\|loopback" package/ios/
returns nothing.
Why this matters
The asymmetry makes the policy close to worthless in its current form:
The threat it guards against is a URL arriving as data — image={{ uri: apiResponse.iconUrl }} pointing at http://192.168.1.1/admin or a cloud
metadata endpoint. That is a cross-platform concern; an app shipping on both platforms is
protected on one of them.
Either the policy is worth having — in which case iOS needs it — or it is not, in which case
Android should stop paying for it. What it should not stay is per-platform.
Decision needed
Keep and mirror it. Port the Android rules to MarkerImageLoader.loadRemote.
Straightforward: URLComponents for the scheme/user check, then resolve the host and
apply the same address classes. Note CFHost / getaddrinfo resolution must not run on
the main thread, and the existing code already hops through decodeQueue / session, so
there is a natural place for it.
Drop it. Remove the host check on Android, document that a remote marker URL is
fetched as given, and tell users to validate URLs that come from untrusted data. This also
makes the linked Android bug disappear without a spec change.
Narrow it. Keep a scheme and userInfo check on both platforms, drop the private-address
classification. Cheaper, cross-platform, and stops breaking development builds — but the
SSRF surface stays open.
My read: (1) if the policy is meant seriously, (3) if it was defensive boilerplate. (2) only
if there is a reason to believe consumers rely on private-network image hosts.
Whichever is chosen, the result belongs in the README next to the marker image source table
(L339-343), which currently says nothing about any of this.
Tests
Once the rule is settled, XCTest in package/iosTests/ mirroring the Kotlin cases: loopback,
site-local, link-local, a public host, and a URL carrying userInfo.
Notes
Companion issue: #129 — the Android side of the same policy, where it breaks require()
images in development builds. Neither blocks the other: this one is the policy decision, #129 is the bug it causes.
Good first issue after the decision in this thread is made: options (1) and (3) are a
narrow, mechanical port of code that already exists in Kotlin.
Problem
Android refuses to load a marker image from a private-network host. iOS loads any URL it can
parse. The two providers disagree about what a marker image is allowed to be, and neither
behaviour is documented.
Android,
MarkerIconFactory.kt:http/https(L419-429)userInfois rejected (L431-433)multicast, or IPv6 unique-local (L443-500)
iOS,
MarkerImageLoader.swift:No host check, no
userInfocheck.grep -rn "allowlist\|isSiteLocal\|loopback" package/ios/returns nothing.
Why this matters
The asymmetry makes the policy close to worthless in its current form:
image={{ uri: apiResponse.iconUrl }}pointing athttp://192.168.1.1/adminor a cloudmetadata endpoint. That is a cross-platform concern; an app shipping on both platforms is
protected on one of them.
(Marker images from require() never render on Android development builds #129), so today it costs real developer experience and buys partial protection.
Either the policy is worth having — in which case iOS needs it — or it is not, in which case
Android should stop paying for it. What it should not stay is per-platform.
Decision needed
MarkerImageLoader.loadRemote.Straightforward:
URLComponentsfor the scheme/usercheck, then resolve the host andapply the same address classes. Note
CFHost/getaddrinforesolution must not run onthe main thread, and the existing code already hops through
decodeQueue/session, sothere is a natural place for it.
fetched as given, and tell users to validate URLs that come from untrusted data. This also
makes the linked Android bug disappear without a spec change.
userInfocheck on both platforms, drop the private-addressclassification. Cheaper, cross-platform, and stops breaking development builds — but the
SSRF surface stays open.
My read: (1) if the policy is meant seriously, (3) if it was defensive boilerplate. (2) only
if there is a reason to believe consumers rely on private-network image hosts.
Whichever is chosen, the result belongs in the README next to the marker image source table
(L339-343), which currently says nothing about any of this.
Tests
Once the rule is settled, XCTest in
package/iosTests/mirroring the Kotlin cases: loopback,site-local, link-local, a public host, and a URL carrying
userInfo.Notes
Companion issue: #129 — the Android side of the same policy, where it breaks
require()images in development builds. Neither blocks the other: this one is the policy decision,
#129 is the bug it causes.
Good first issue after the decision in this thread is made: options (1) and (3) are a
narrow, mechanical port of code that already exists in Kotlin.