{"id":"CVE-2026-55178","aliases":[],"url":"https://o3.security/vulnerability/CVE-2026-55178","summary":"GeoLens: Cross-dataset authorization bypass discloses private dataset metadata, schema, sample values, table rows, and raster/vector tile data","details":"### Summary\n\nMultiple GeoLens read/link endpoints authorized only the resource named in the\nrequest URL (a map, a VRT, a source dataset, an AI request) and failed to\nre-authorize a **second, caller-influenced dataset** that the request reached\nthrough a relationship, layer reference, mosaic source, or request body. This\n\"authorize the URL resource, read a *different* dataset un-re-authorized\"\npattern let callers read data from datasets they have no access to.\n\nThe most severe instances require **no authentication at all** (anonymous,\nnetwork-only). Others require only the **default `editor` role** that any\nself-service signup / upload user receives.\n\nAll issues are fixed in **1.2.3**. There is no complete configuration\nworkaround — upgrading is the only full remediation.\n\n### Impact\n\nDepending on the endpoint, an attacker can read, for datasets they cannot\notherwise access:\n\n- the dataset's **vector tile data** (actual feature geometries/attributes),\n- the dataset's **raster pixels**,\n- backing-table **rows**,\n- and **metadata** — table name, column schema, feature count, extent, source\n  URL/filename, contacts, and **sampled row values**.\n\n### Affected versions\n\nAll versions **prior to 1.2.3** (includes the published 1.0.0, 1.2.0, and\n1.2.2 releases and their PyPI/npm/GHCR artifacts). Fixed in **1.2.3**.\n\n### Findings\n\n**1. Anonymous metadata + private vector-tile disclosure via public maps (PR #235)**\n`GET /maps/{id}` and `GET /maps/{id}/style.json` authorized the map but not\neach layer's backing dataset. A public map that references a private dataset\nleaked that dataset's table name, column schema, feature count, extent, and\nsampled values to anonymous callers. `style.json` additionally returned a\nvector-tile URL carrying an HMAC signature bound to **neither user nor map**,\nwhich the tile endpoint accepts for non-public datasets with no user check —\nso the signature is **replayable** to read the private dataset's actual vector\ntiles. *(Anonymous · High)*\n\n**2. Anonymous private-row disclosure via dataset relationships (PR #234)**\nThe dataset FK-relationship APIs authorized only the source dataset from the\nURL, never the relationship target. A public dataset with a relationship to a\nprivate dataset let an anonymous caller enumerate the relationship (obtaining\nthe private target's id/title and the relationship id) and then call the\nrelated-record endpoint to read **rows from the private target's backing\ntable**. *(Anonymous · High)*\n\n**3. Anonymous metadata disclosure via OGC `externalId` lookup (PR #236)**\n`GET /collections/datasets/items?externalId=<uuid>` resolved the dataset by id\nand returned the full OGC catalog record (title, summary, bbox, keywords,\ncontacts, distributions, source org) with **no visibility check** — the user\nwas never threaded into the lookup. An anonymous caller could read any\nprivate, restricted, or unpublished dataset's metadata by UUID. *(Anonymous ·\nHigh)*\n\n**4. Cross-tenant raster pixel disclosure via VRT mosaics — SEC-C (PR #237)**\nAn authenticated user with the default `editor` upload permission could mosaic\nanother user's **private raster** into a VRT they own, then read the victim's\n**pixels** back through raster tile / quicklook / COG endpoints that authorize\nonly the attacker-owned VRT. VRT member pixels are compiled into one served\nasset and cannot be filtered at read time, so the fix authorizes every source\ndataset at write/link time. *(Authenticated `editor` · High)*\n\n**5. Cross-tenant metadata/sample-data disclosure via AI metadata endpoints — SEC-D (PR #238)**\nThe `POST /ai/metadata/{summary,keywords,lineage,quality-statement}` endpoints\nwere gated only by the `use_ai_chat` permission (held by the default `editor`\nrole). The attacker-controlled `dataset_id` in the request body flowed into the\nLLM prompt context with no visibility filter, rendering **any** dataset's\ntitle, summary, source URL, filename, column schema, and **sample values** into\nthe response. *(Authenticated `editor` · High)*\n\n**6. Residual VRT member disclosure for legacy links — SEC-E (PR #237)**\nLink-time authorization (finding 4) does not re-authorize pre-existing\n`vrt_source_links`, so legacy or authorization-drift links still leaked member\nmetadata and health via the VRT source-listing/status endpoints until a\nper-member read filter was added. *(Medium)*\n\n### Patches\n\nFixed in **1.2.3** by, in order:\n\n- `31a103b9` — fix(catalog): authorize relationship targets in related-record endpoints (#234)\n- `01bc87da` — fix(maps): re-authorize each layer's dataset on anonymous map read endpoints (#235)\n- `407c0688` — fix(ogc): enforce dataset visibility on the externalId OGC item lookup (#236)\n- `2c031da8` — fix(vrt): authorize VRT source datasets at link time + filter unauthorized members on read (#237)\n- `07dfb1c6` — fix(ai): authorize the requested dataset on AI metadata endpoints (#238)\n\nThe fixes follow the codebase's established per-dataset re-authorization pattern\n(`can_access_dataset` / `check_dataset_access_or_anonymous`), filtering at read\ntime and authorizing cross-dataset references at link/write time.\n\n### Workarounds\n\nThere is **no complete configuration workaround**; the anonymous findings\nrequire only network access to the API. Operators who cannot upgrade\nimmediately should restrict network exposure of the API and avoid co-locating\nprivate datasets with public maps/relationships, but **upgrading to 1.2.3 is\nthe only full remediation**.\n\n### Remediation\n\nUpgrade to GeoLens **1.2.3**:\n- Container images: `ghcr.io/geolens-io/geolens-api:1.2.3` (+ worker/frontend)\n- Python SDK: `geolens==1.2.3` · CLI: `geolens-cli==1.2.3` · npm: `@geolens/sdk@1.2.3`","published":"2026-08-18T18:00:07Z","modified":"2026-08-18T18:15:07.946781480Z","cvss":{"score":7.5,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"npm","name":"@geolens/sdk","fixedVersion":"1.2.3"},{"ecosystem":"PyPI","name":"geolens-cli","fixedVersion":"1.2.3"},{"ecosystem":"PyPI","name":"geolens","fixedVersion":"1.2.3"}],"fix":{"url":"https://github.com/geolens-io/geolens/pull/234","label":"geolens-io/geolens#234"},"references":[{"type":"WEB","url":"https://github.com/geolens-io/geolens/security/advisories/GHSA-p23g-mvhj-jh3j"},{"type":"WEB","url":"https://github.com/geolens-io/geolens/pull/234"},{"type":"WEB","url":"https://github.com/geolens-io/geolens/pull/235"},{"type":"WEB","url":"https://github.com/geolens-io/geolens/pull/236"},{"type":"WEB","url":"https://github.com/geolens-io/geolens/pull/237"},{"type":"WEB","url":"https://github.com/geolens-io/geolens/pull/238"},{"type":"PACKAGE","url":"https://github.com/geolens-io/geolens"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-18T18:15:07.946781480Z"}}