dlt: credential leak and SSRF via pagination next URL
dlt REST client - next-page URLs taken from the API response are followed to any host with the pipeline's credentials attached, enabling credential theft and SSRF (GHSA-67vw-hpjr-f78q)
Unpatched. Reported privately via GitHub Security Advisory on 2026-07-14; the 90-day coordinated-disclosure window passed, and the final 2026-11-03 deadline set in our 2026-10-20 notice expired with no response from the maintainer.
- Product
- dlt (REST API source and RESTClient) in dlt-hub/dlt
- Affected versions
- dlt through 1.32.0 and devel (checked at HEAD 75ea7f3).
- Severity
- HIGH
- Status
- Unpatched
- Weaknesses
- CWE-522Insufficiently Protected Credentials
- CWE-918Server-Side Request Forgery (SSRF)
- GitHub advisory
- GHSA-67vw-hpjr-f78q
Private; in triage, not yet published by the maintainer - CVE
- Pending (requested via MITRE CNA-LR)
- Published
- Credit
- Finder: Aaron Elijah Mars of Aeon. Tool: Aeon (https://www.aeon.fun).
What pipeline owners should do now
No upstream patch is available yet. Teams that run rest_api sources or RESTClient against APIs they do not fully trust should apply one of these mitigations until an official fix is released.
- Set an explicit paginator on every resource instead of relying on auto-detection. Where the API supports it, prefer offset, page-number or cursor pagination, which keep requests on the configured base URL.
- If you must use
JSONLinkPaginatororHeaderLinkPaginator, subclass it and overrideupdate_requestto stop pagination when the resolved next URL's scheme or host differs from the original request. - If modifying the library, patch
BaseNextUrlPaginator.update_requestindlt/sources/helpers/rest_client/paginators.pyto keep next-page URLs on the same origin unless the user opts in, and only attach auth for the configured host. - Run pipelines with network egress limited to the APIs they need, and block link-local metadata addresses (such as 169.254.169.254) from pipeline hosts or require IMDSv2.
- Use short-lived, narrowly scoped API credentials for ingestion, and rotate any credential used against an API that may have returned unexpected next-page links.
Impact and reach
Any dlt pipeline that pages through a REST API with RESTClient or the rest_api source and a configured authenticator (BearerTokenAuth, APIKeyAuth, HttpBasicAuth and similar) is exposed when the upstream API, or a party able to alter its responses, is hostile. Secrets in the default request headers travel with every page as well.
Two outcomes follow. First, credential theft: the pipeline's secret is sent to a host chosen by whoever controls the response. dlt already masks these same secrets in its logs, but not on the wire. Second, server-side request forgery: the pipeline can be made to request internal or link-local services and load the response body into the destination dataset. This is not an HTTP redirect, so the requests library's own rule that strips Authorization on cross-host redirects never runs. A working exploit is withheld.
How the next-page URL is followed
JSONLinkPaginator and HeaderLinkPaginator read the next-page reference directly from the response. BaseNextUrlPaginator.update_request then only resolves relative URLs and assigns the result to request.url, with no scheme or host check. The same request object keeps its auth, so RESTClient._send_request re-runs the authenticator against the new URL when it prepares the request:
- Next URL assigned with no host check: dlt/sources/helpers/rest_client/paginators.py#L651-L662
- Next reference taken from the Link header: dlt/sources/helpers/rest_client/paginators.py#L709-L711
- Next reference taken from the JSON body: dlt/sources/helpers/rest_client/paginators.py#L764-L767
- Pagination loop reuses the request and its auth: dlt/sources/helpers/rest_client/client.py#L271-L284
- Request prepared again with the authenticator: dlt/sources/helpers/rest_client/client.py#L141-L142
- Next-link paginators are the first auto-detectors: dlt/sources/helpers/rest_client/detector.py#L151-L172 and detector.py#L213-L220
Disclosure timeline
- Vulnerability reported privately via GitHub Security Advisory (GHSA-67vw-hpjr-f78q).
- First follow-up comment posted on the advisory (35 days in triage).
- Second follow-up comment posted on the advisory (65 days in triage).
- Coordinated-disclosure notice posted on the advisory after the 90-day window passed, setting a deadline of 2026-11-03.
- Coordinated-disclosure deadline expired with no response from the maintainer; sink verified still present at HEAD 75ea7f3 and in release 1.32.0.
- Public advisory published; CVE requested via MITRE CNA-LR.
References
- Affected repository: dlt-hub/dlt
- GitHub Security Advisory: GHSA-67vw-hpjr-f78q (private; in triage)