Skip to content

fix(amber): re-validate every redirect hop in the Hugging Face media proxy - #9134

Open
PG1204 wants to merge 1 commit into
apache:mainfrom
PG1204:fix/hf-media-proxy-redirect-ssrf
Open

PG1204 wants to merge 1 commit into
apache:mainfrom
PG1204:fix/hf-media-proxy-redirect-ssrf

Conversation

@PG1204

@PG1204 PG1204 commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

/api/huggingface/media-proxy checked only the URL it was given, then let Unirest follow redirects on its own. So an allowlisted host could redirect the server anywhere, including internal addresses (#9103). This applies the fix #7013 made on the Python side:

  • Redirects are followed by hand. The proxy now has its own Unirest instance with followRedirects(false). The global config used by the Hub endpoints is unchanged. Each redirect comes back to the resource, and the next hop is checked before it is requested, up to 5 hops. A relative Location is resolved against the current URL. A missing or unparsable Location, or a sixth redirect, returns 502.
  • Every hop gets the same checks: https only (the scheme is compared case-insensitively, so HTTPS:// now works), an allowlisted host, and every address the host resolves to must be public. The address check rejects private, loopback, link-local (including 169.254.169.254), shared (100.64.0.0/10), unique-local, multicast, reserved and documentation ranges. IPv4-mapped, IPv4-compatible and NAT64 IPv6 addresses are judged by the IPv4 address inside them.
  • hf.co is added to the allowlist. Hub file URLs (https://huggingface.co/<repo>/resolve/main/<file>) redirect to us.aws.cdn.hf.co. Without hf.co, re-checking each hop would break the most common Hugging Face media URL.

Behaviour changes worth knowing:

Any related issues?

Closes #9103.

How was this PR tested?

30 new tests in HuggingFaceModelResourceSpec, 114 in total, all passing. No existing test was changed.

  • Red-green: the tests were written first, against a version that only added the test seams. 26 failed, each for the expected reason. For example, the media client followed a real 302 instead of returning it, and a redirect to an off-allowlist host came back as 302 instead of 403. With the fix, all pass. Reverting the fix makes exactly those 26 fail again.
  • The root cause is pinned with the real HTTP client against a loopback server: the media client must hand back a 302 rather than follow it. The other proxy tests use the suite's existing stub client plus a fake resolver, so nothing leaves the machine.
  • Mutation check: switching off each defence in turn (no auto-follow, per-hop check, hf.co, embedded-IPv4 handling, the empty-resolution guard, the hop limit, https-only) is caught by its own tests.
  • Live check against the real network: a Hub file URL follows its redirect to us.aws.cdn.hf.co and returns the PNG. The same URL over http:// returns 400, an off-allowlist host 403, and an unresolvable allowlisted name 502.
  • scalafmtCheck and scalafix --check are clean for both main and test.

Was this PR authored or co-authored using generative AI tooling?

Yes, this PR was co-authored with Claude in compliance with ASF policy.

@Yicong-Huang Yicong-Huang added release/v1.3 back porting to release/v1.3 release/v1.2 back porting to release/v1.2 labels Oct 25, 2026
@github-actions
github-actions Bot requested review from mengw15 and xuang7 October 25, 2026 17:31
@github-actions

Copy link
Copy Markdown
Contributor

Backport auto-label report

This fix: PR was checked against each actively-supported release branch. A release/* label nominates a backport target; the branch's release manager approving this PR is what sends the fix there. The required Backport Approvals check stays red until every label below is approved, so each manager either approves or removes their own label — which is why the labels left on a merged PR are exactly the branches it reached.

Release branch Analysis
✅ release/v1.3 Change detected on this branch — label added; this fix is queued to backport here. @mengw15 decides: approving sends the fix here, removing this label declines it. The merge waits on one or the other. Review requested.
✅ release/v1.2 Change detected on this branch — label added; this fix is queued to backport here. @xuang7 decides: approving sends the fix here, removing this label declines it. The merge waits on one or the other. Review requested.

Auto-label run.

@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @aglinxinyuan
    You can notify them by mentioning @aglinxinyuan in a comment.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 4 better · 🔴 4 worse · ⚪ 7 noise (<±5%) · 0 without baseline

Compared against main 98b875f benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 356 0.217 27,667/33,008/33,008 us 🔴 +23.3% / 🔴 +207.7%
🔴 bs=100 sw=10 sl=64 821 0.501 121,346/133,985/133,985 us 🟢 -19.0% / 🔴 +79.0%
⚪ bs=1000 sw=10 sl=64 964 0.588 1,043,052/1,087,416/1,087,416 us ⚪ within ±5% / 🔴 +55.8%
Baseline details

Latest main 98b875f from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 356 tuples/sec 400 tuples/sec 1,119 tuples/sec -11.0% -68.2%
bs=10 sw=10 sl=64 MB/s 0.217 MB/s 0.244 MB/s 0.683 MB/s -11.1% -68.2%
bs=10 sw=10 sl=64 p50 27,667 us 22,438 us 8,992 us +23.3% +207.7%
bs=10 sw=10 sl=64 p95 33,008 us 38,253 us 11,255 us -13.7% +193.3%
bs=10 sw=10 sl=64 p99 33,008 us 38,253 us 13,808 us -13.7% +139.1%
bs=100 sw=10 sl=64 throughput 821 tuples/sec 846 tuples/sec 1,518 tuples/sec -3.0% -45.9%
bs=100 sw=10 sl=64 MB/s 0.501 MB/s 0.517 MB/s 0.927 MB/s -3.1% -45.9%
bs=100 sw=10 sl=64 p50 121,346 us 114,694 us 68,504 us +5.8% +77.1%
bs=100 sw=10 sl=64 p95 133,985 us 165,363 us 74,866 us -19.0% +79.0%
bs=100 sw=10 sl=64 p99 133,985 us 165,363 us 89,939 us -19.0% +49.0%
bs=1000 sw=10 sl=64 throughput 964 tuples/sec 974 tuples/sec 1,569 tuples/sec -1.0% -38.5%
bs=1000 sw=10 sl=64 MB/s 0.588 MB/s 0.595 MB/s 0.957 MB/s -1.2% -38.6%
bs=1000 sw=10 sl=64 p50 1,043,052 us 1,023,112 us 669,501 us +1.9% +55.8%
bs=1000 sw=10 sl=64 p95 1,087,416 us 1,090,151 us 706,487 us -0.3% +53.9%
bs=1000 sw=10 sl=64 p99 1,087,416 us 1,090,151 us 737,560 us -0.3% +47.4%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,561.97,200,128000,356,0.217,27667.16,33007.92,33007.92
1,100,10,64,20,2434.85,2000,1280000,821,0.501,121346.36,133984.79,133984.79
2,1000,10,64,20,20745.27,20000,12800000,964,0.588,1043051.62,1087415.52,1087415.52

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.80460% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.80%. Comparing base (2407d79) to head (862675b).
⚠️ Report is 30 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...texera/web/resource/HuggingFaceModelResource.scala 90.80% 0 Missing and 8 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #9134      +/-   ##
============================================
- Coverage     92.33%   91.80%   -0.54%     
- Complexity     5045     5077      +32     
============================================
  Files          1261     1264       +3     
  Lines         54120    54772     +652     
  Branches       6768     6879     +111     
============================================
+ Hits          49974    50282     +308     
- Misses         2486     2785     +299     
- Partials       1660     1705      +45     
Flag Coverage Δ *Carryforward flag
access-control-service 77.38% <ø> (ø) Carriedforward from 2407d79
agent-service 99.16% <ø> (ø) Carriedforward from 2407d79
amber 85.97% <90.80%> (-1.34%) ⬇️
computing-unit-managing-service 60.48% <ø> (ø) Carriedforward from 2407d79
config-service 87.37% <ø> (ø) Carriedforward from 2407d79
file-service 82.00% <ø> (ø) Carriedforward from 2407d79
frontend 96.56% <ø> (ø) Carriedforward from 2407d79
notebook-migration-service 83.73% <ø> (ø) Carriedforward from 2407d79
pyamber 98.57% <ø> (ø) Carriedforward from 2407d79
workflow-compiling-service 72.43% <ø> (ø) Carriedforward from 2407d79

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine fix release/v1.2 back porting to release/v1.2 release/v1.3 back porting to release/v1.3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HuggingFace media proxy: host allowlist can be bypassed via HTTP redirects

3 participants