Skip to content

feat: restrict dataset urls to configured prefixes - #169

Merged
maxrjones merged 7 commits into
mainfrom
feat/url-allowlist
Oct 2, 2026
Merged

maxrjones merged 7 commits into
mainfrom
feat/url-allowlist

Conversation

@maxrjones

Copy link
Copy Markdown
Member

This feature allows restricting what dataset urls a titiler-multidim deployment can be used for. The default stays the same (any valid URL if TITILER_MULTIDIM_ALLOWED_URL_PREFIXES is unset. If TITILER_MULTIDIM_ALLOWED_URL_PREFIXES, all URLs that don't validate against those prefixes will raise a HTTPException.

Testing

  • Ran new tests

PR checks

  • Standard CI runs automatically on each push.
  • To run the CDK synth check, add the run-cdk-checks label to this PR.
  • If you push more commits after that run completes, remove and re-add the label to run it again.
  • To trigger a dev deployment, add the deploy-dev label. It smoke-tests tiles from the native MUR, virtual MUR, and virtual NLDAS Icechunk stores after deployment.

maxrjones and others added 6 commits October 1, 2026 15:58
The ?url= parameter was an open trust boundary: any s3:// prefix the
reader role can read, or file:// path in the container, could be opened.

ApiSettings.allowed_url_prefixes (TITILER_MULTIDIM_ALLOWED_URL_PREFIXES,
comma-separated). When set, DatasetPathParams rejects any url not
starting with one of the prefixes with a 400; every data endpoint and
the metadata/validate extensions route through that dependency. Empty
keeps today's open behaviour so nothing changes until a deploy opts in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GDAL >= 3.12 already defaults GDAL_VRT_RAWRASTERBAND_ALLOWED_SOURCE to
SIBLING_OR_CHILD_OF_VRT_PATH; set it and GDAL_VRT_ENABLE_RAWRASTERBAND=NO
explicitly so a base-image or GDAL version change cannot silently relax
them. GDAL only encodes output here, so this is defence in depth.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The prefix check was a raw str.startswith, so:
- "s3://bucket/a" also admitted "s3://bucket/abc/..."; prefixes now match
  whole path segments (a trailing "/" is implied).
- "<prefix>/../elsewhere" passed textually; urls with "." or ".." path
  segments (percent-encoded included) are now refused outright rather
  than resolved, because S3 keeps such keys literally while HTTP servers
  resolve them, so no single resolution is safe.
- "HTTPS://Host/" did not match "https://host/"; scheme and host are now
  compared case-insensitively. Paths stay case-sensitive.

The original url is still what gets opened; normalisation is only used
for the comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- type allowed_url_prefixes as list[str] (NoDecode + CSV validator)
  instead of a str field whose validator returns a list
- _normalize_url raises on "."/".." segments instead of returning None;
  a bad configured prefix now fails startup rather than being silently
  dropped (which rejected every request)
- append the trailing "/" to the path, not after the query string, and
  do it in one place for both prefixes and urls
- reuse reader.api_settings instead of a third ApiSettings instance
- move the matching into _is_url_allowed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reader opens a scheme-less url as a local path, but the allowlist
compared it as a raw string, so a "file:///data/" prefix denied
"/data/x" (and a bare prefix denied file:// urls). _normalize_url now
turns bare and relative paths into absolute file:// urls (relative to
the working directory, as the reader opens them) after the dot-segment
check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@maxrjones
maxrjones requested a review from hrodmn October 1, 2026 21:54
@github-actions github-actions Bot added the feat label Oct 1, 2026
@maxrjones
maxrjones merged commit bcdbd1a into main Oct 2, 2026
10 checks passed
@maxrjones
maxrjones deleted the feat/url-allowlist branch October 2, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants