Skip to content

Remote marker image host policy exists only on Android #130

Description

@jkasprzyk17

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:

  • 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:

private static func loadRemote(
  uri: String,
  cacheKey: NSString,
  image: MarkerImage,
  completion: @escaping (UIImage?) -> Void
) {
  guard let 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.
  • Meanwhile the same policy is what breaks bundled images on Android development builds
    (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

  1. 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.
  2. 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.
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingplatform: iosAffects iOSswiftThe Swift / iOS native layer (package/ios)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions