September 26, 2026
CVE-2026-61704: A DNS check is not a connection guarantee
Real DNS evidence, a loopback canary and patch analysis of the link-preview-js validation-to-use gap.

By Ahmet Sadık Şahiner
5 min read
Why validating one address and fetching the original hostname left link-preview-js exposed to DNS rebinding.
Editorial explainer: a DNS check is not a connection guarantee.
Ahmet Sadık Şahiner · Independent Security Researcher & Information Technology Specialist
Research record: CVE-2026–61704 · OP-Engineering link-preview-js · CWE-918 · CVSS v3.1 7.5 High (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N). Official affected range: before 4.0.4. Preserved real-DNS test: 4.0.3. Fixed release: 4.0.4. Public references checked 26 September 2026. Official CVE record.
TL;DR
The documented resolveDNSHost mitigation validated a resolved address, but the subsequent HTTP fetch used the original hostname. A second DNS lookup could therefore supply a different destination. In the preserved Linux test, validation saw a public address while the actual request reached a loopback-only canary.
The essential invariant is simple: the destination used by the connection must be the destination that passed validation.
Product context and threat model
link-preview-js extracts information from web links. In a server-side integration, an application may accept a user-influenced URL and fetch it from a network context that can reach resources unavailable to that user. SSRF defenses must constrain the actual destination of that fetch.
The DNS-rebinding scenario assumes control of the URL's DNS answers and the opportunity for resolution to occur again between checking and connecting. The attacker does not need to submit a loopback literal to the initial check. Public reachability of a particular application's preview endpoint, authentication requirements and egress policy remain deployment-specific.
Root cause: validation and use resolved separately
In the vulnerable 4.0.3 path, the callback produced an address that was passed to throwOnLoopback. The fetch target was still derived from the original detected URL. Validating a result did not bind the later connection to it.
This is a TOCTOU-like gap: the name stayed the same, but its meaning as a network destination changed. A correct classification of the first answer could coexist with an unsafe second connection.
Proof using real DNS
The primary evidence is the 22 June 2026 Linux VM console log, supported by a readable evidence summary. The environment was Alpine Linux 3.24.1, Node.js 24.16.0 and the npm package link-preview-js@4.0.3. The package records hashes for the installed library files.
A local UDP/53 DNS server supplied a public address for the first A query and loopback for the later A query. The HTTP canary listened only on 127.0.0.1:9878. The evidence describes no monkey-patching of dns.lookup, fetch or the library for this Linux run.
The preserved log contains:
REAL DNS UDP A #1: rebind.test -> 93.184.216.34 client=127.0.0.1:45035
GUARD RESOLVED: 93.184.216.34
REAL DNS UDP A #2: rebind.test -> 127.0.0.1 client=127.0.0.1:50382
REAL DNS UDP type=28: rebind.test -> NOERROR/NODATA
CANARY HIT: remote=127.0.0.1 host="rebind.test:9878"
RESULT: title="INTERNAL-CANARY-HIT"REAL DNS UDP A #1: rebind.test -> 93.184.216.34 client=127.0.0.1:45035
GUARD RESOLVED: 93.184.216.34
REAL DNS UDP A #2: rebind.test -> 127.0.0.1 client=127.0.0.1:50382
REAL DNS UDP type=28: rebind.test -> NOERROR/NODATA
CANARY HIT: remote=127.0.0.1 host="rebind.test:9878"
RESULT: title="INTERNAL-CANARY-HIT"The canary records the request arriving, and the returned preview includes its title. Together with the two DNS answers, this supports an end-to-end validation-to-connection mismatch, rather than merely a suspicious input accepted by a parser.
Negative control and limits
The separate EVIDENCE.txt records a direct-loopback control rejected with SSRF request detected, trying to query host. That supports the distinction between an absent guard and a guard applied to an address different from the one ultimately used.
That control is preserved in the Windows validation summary, not in the real-DNS Linux console excerpt. Other summarized address-format checks are supplementary; they are not required to establish this DNS-rebinding result. The publication does not claim a new test run or a complete fixed-version retest.
Patch analysis: resolve, validate, use that address
PR #181 was merged on 23 June 2026. Its history is important. The initial 6ee25043 change used a pinned dispatcher. The later f3a3dd84 change removed Node-specific dependencies and constructed the fetch URL from the validated resolved address. Describing the final implementation as still using the earlier dispatcher would be inaccurate.
The final diff also applies the validated-address conversion to the explicitly handled redirect target and expands address normalization checks. Redirect validation is a separate decision point: approval of the initial destination does not authorize a subsequent one. This source analysis concerns the shown manual-redirect path and does not claim universal protection for arbitrary proxies or every redirect configuration.
The 4.0.4 release was published on 23 June 2026 and references the PR. Its source changes and regression assertions support the fix architecture. They are not a replacement for a fresh real-network retest in each consumer's deployment, including HTTPS identity handling, redirects and any proxy path.
CVE identity and chronology
This research followed the earlier CVE-2026–43897 mitigation. The authoritative CVE-2026–61704 record identifies the new issue and links GHSA-cpjf-6666-r8fx. The advisory page retrieved during this review still displayed CVE-2026–43897 in its CVE field, despite describing the rebinding report and 4.0.4. That display discrepancy is recorded in the verification report; this article uses the dedicated CVE-2026–61704 record as its identifier authority.
The advisory credits the reporter account ahmet-sahiner, and the PR identifies the same account as author. The CVE JSON itself does not include a finder-credit field.
Impact and defensive lessons
The controlled result demonstrates access to and parsing of a loopback HTTP resource. The CNA rates confidentiality impact as high; it does not assign integrity or availability impact in this vector. Access to cloud metadata, credential theft or code execution was not demonstrated by this test.
An SSRF boundary must cover resolution and connection together. Carry the validated address into the transport decision, reapply the contract at redirect boundaries, and account for normalized IPv4/IPv6 forms. Egress restrictions provide a separate layer of containment.
Timeline and disclosure scope
- 22 June 2026: preserved controlled DNS evidence.
- 23 June 2026: PR merge and 4.0.4 release; the retrieved advisory displays this publication date.
- 20 August 2026: CVE-2026–61704 publication in the official record.
- 26 September 2026: evidence and reference review for this write-up.
The evidence used a local canary and no third-party HTTP target. No production probing was performed during publication preparation.
References
- Official CVE-2026–61704 record
- Repository advisory
- PR #181, initial patch, and final implementation change
- 4.0.4 release
- Evidence: controlled laboratory records retained by the author; selected results are shown below.
Related research
See validation at the Pardus privilege boundary for another boundary with a distinct trust contract.
Evidence and technical diagrams
The proof panel shows the captured real-DNS test. The remaining figures explain the validation-to-use gap and final patch architecture.
Real evidence: the first DNS answer was public, the second was loopback, and the local canary was reached in the captured Linux test.
Conceptual attack chain: validation accepts the first DNS answer; a second lookup changes the network destination.
Conceptual root cause: checking a resolved address does not bind a later hostname-based connection to that address.
Conceptual patch comparison: the final 4.0.4 implementation uses a validated-address URL, including the handled redirect target.