Skip to content

fix(engine): downloads refuse hosts that resolve to private addresses - #6571

Merged
miguel-heygen merged 3 commits into
mainfrom
fix/download-public-addresses
Oct 28, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
fix/download-public-addresses

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What changes

Downloads refuse hosts that resolve to private addresses. The downloader already refused private, loopback and link-local host names written in the URL and re-checked every redirect; it still connected to whatever a public name resolved to. Now every download resolves the host itself and refuses to connect when any answer is a private, loopback, link-local or otherwise reserved address (the same ranges the URL check uses, one list). The check runs on the address the connection uses, so there is no second lookup to race.

  • The transport is undici's own fetch with an Agent whose connect.lookup does the check (undici 7.25.0, exact, the version already in the lockfile; a dependency of the engine and the CLI, which keeps it external like the browser packages). It is imported on the first download, so a process that downloads nothing pays nothing.
  • The tests replace the transport through one seam (_setDownloadFetchForTests) instead of stubbing the global fetch; the existing download tests moved to it unchanged otherwise.
  • Behind an env proxy (HTTPS_PROXY with NODE_USE_ENV_PROXY=1 or --use-env-proxy) the proxy resolves the name, so downloads use Node's own fetch there and the URL and redirect checks stay the guard. fetchGoesThroughEnvProxy moved to @hyperframes/core/env-proxy, one owner for the CLI's DNS probe, the publish hint and the downloader.
  • A refused address surfaces as the existing non-retryable http_rejected failure ("Download URL is not permitted"), so it is not retried.

Tests

urlDownloader.publicAddress.test.ts resolves a host to a private IPv4, a link-local address mixed in with a public one, and a loopback IPv6 address: all three are rejected without a retry, for downloadToTemp and fetchPublicHttpsText. They fail with the address check removed.

fails without the fix: urlDownloader.publicAddress.test.ts > the private IPv4, mixed link-local and loopback IPv6 cases (each expects the HF_NON_PUBLIC_ADDRESS refusal).

Checked on a Linux box

  • Engine download, mixer and extractor tests: pass three runs in a row.
  • A real render with a remote video and image (both fetched by the producer through this downloader): same video hash on main and on this branch, three runs each. Wall time 7.72, 8.24 and 9.78 s on the branch against 9.02, 8.25 and 9.76 s on main: the same within the machine's noise.
  • The CLI built with tsup (undici external) renders the same remote-asset project to the same hash.
  • The first download in a process loads undici: about 45 ms once per process, on a render of several seconds or more.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 0 important · 0 nits · head 3ac69653

The address check itself is sound: it runs on the connect-time lookup, handles all: true for happy-eyeballs, and the new test exercises real undici. The blocker is that this transport drops env-proxy support. Inline.

— Genesis

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 137 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES — 1 resolved, 0 open, 2 new · 3ac6965 → e5856f0

The proxy fix is right. Required CI is red on this head (run 40566977216) for two reasons this PR causes:

  • Producer: unit tests. packages/producer/src/server.test.ts:229 and :238 (prepareRenderBody — previewUrl) still stub the global fetch. fetchPublicHttpsText now goes through undici, so the stub is never called: 0 calls, and no prepared. This is the same move the engine tests got. Switch them to _setDownloadFetchForTests, which may need exporting from the engine index.
  • CLI: npx shim (all 3 OSes). See inline.

— Genesis

@@ -1,13 +1,8 @@
import { promises as dns } from "node:dns";
import { fetchGoesThroughEnvProxy } from "@hyperframes/core/env-proxy";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker (CI): the npx-shim job doesn't build core, so under Node the node condition resolves @hyperframes/core/env-proxy to dist/envProxy.js, which doesn't exist. skills.test.ts fails 40 tests with ERR_MODULE_NOT_FOUND (via updateCheck.ts → hostAnswers.ts). Either build core in that job or keep this import out of the shim's module graph.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Fixed in d5d455e — npx shim green on all 3 OSes.

@miguel-heygen
miguel-heygen force-pushed the fix/download-public-addresses branch from e5856f0 to 7cdf401 Compare October 28, 2026 18:04

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES — 2 resolved, 0 open, 2 new · e5856f0 → d5d455e

  • Required CI red (run 40744720190), caused by this PR: 16 producer tests still stub the global fetch, so the undici transport never calls the stub and the downloads fail with ENOTFOUND. These are 15 in producer/src/services/htmlCompiler.test.ts (localizeRemoteMediaSources / ImageSources / FontFaces, stubs at :1952, :1980, :2008 …) and distributed/plan.test.ts:712 ("maps an open-ended remote video HTTP 404…", integration lane). Main is green. Move them to _setDownloadFetchForTests, like server.test.ts.
  • Body is missing a fails without the fix: line naming the test, e.g. fails without the fix: urlDownloader.publicAddress.test.ts > ….

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 2 resolved, 0 open, 0 new · d5d455e → 84bb4f3

The compiler and plan 404 tests now stub _setDownloadFetchForTests. CI and Windows render verification are green on this head, and the body carries the fails without the fix: line.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 9830059 Oct 28, 2026
83 checks passed
@miguel-heygen
miguel-heygen deleted the fix/download-public-addresses branch October 28, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant