huggingface_hub v1.26.0──revision解決を1回に集約するresolve_revisionと、UNCパスでNetNTLMv2ハッシュが盗まれるCVE-2026-15717の修正
Pythonライブラリhuggingface_hubの2026-07-30リリースv1.26.0は、ファイルごとに異なるコミットを掴んでしまう事故を防ぐ`resolve_revision`APIと、悪意あるリポジトリのファイル名がWindowsクライアント上で任意ファイル書き込み・NetNTLMv2ハッシュ漏洩につながるCVE-2026-15717の修正、そしてSandbox.createがHFトークンをサンドボックスへ渡さなくなる変更を同時に含んでいる。

目次
3行まとめ
- huggingface_hub v1.26.0(2026-07-30公開)は、
config.json・model.safetensorsのようにファイルを1つずつダウンロードするライブラリが、revision="main"を毎回HTTPリクエストで解決していたことで生じていた「2回のダウンロードが別々のコミットを掴む」リスクを、HfApi.resolve_revisionという新APIで1回の解決に集約して解消した。- 同リリースはCVE-2026-15717の修正も含む。
--local-dirへのダウンロード・アップロードで絶対パス・ドライブ相対パス・UNCパス・..トラバーサルを弾いていなかった問題で、悪意あるリポジトリがWindowsクライアント上に任意ファイルを書き込めるだけでなく、UNCパス経由でNetNTLMv2ハッシュを漏洩させられる可能性があった。Sandbox.createがsbx-serverバイナリ取得のためにHFトークンをジョブ環境へ注入していた問題も同時に修正され、forward_hf_token=Trueを明示しない限りトークンがサンドボックスに渡らなくなった。
resolve_revision──ファイルごとの二重解決を1回に集約
GitHub公式リリースノートによれば、config・weights・tokenizer・processorなど複数ファイルを1つずつダウンロードするライブラリは、これまで呼び出しのたびにrevision="main"をコミットハッシュへ解決していた。これはファイルごとに1回のHTTPリクエストというコストがかかるだけでなく、「その間にリポジトリが更新されると、2つの呼び出しが別々のコミットを掴んでしまう」リスクを伴っていた。
新しいHfApi.resolve_revisionは、revisionを1回だけ解決してResolvedRevisionというオブジェクトを返す。これはstrのサブクラスで、値自体はユーザー向けの表記(たとえば"main"のまま)を保ちながら、.resolved属性に実際のコミットハッシュを持つ。
>>> from huggingface_hub import resolve_revision, hf_hub_download
>>> revision = resolve_revision("openai-community/gpt2")
>>> revision
ResolvedRevision(initial=None, resolved='607a30d783dfa663caf39e06633721c8d4cfcd7e')
>>> revision == "main" # readable error messages
True
>>> config = hf_hub_download("openai-community/gpt2", "config.json", revision=revision)
>>> weights = hf_hub_download("openai-community/gpt2", "model.safetensors", revision=revision)
hf_hub_download・snapshot_download・get_cached_repo_treeといったダウンロード用ヘルパーがこのResolvedRevisionを検知して、コミットハッシュを直接使うようになり、「全ファイルが同じコミットから来る」ことを保証する。このマッピングはキャッシュのrefs/フォルダにも書き込まれるため、後続の実行がオフラインモードの場合はキャッシュ済みの値へ透過的にフォールバックする、とリリースノートは説明している。実装はPR #4604。
この問題はvLLM・Transformersでも独立に見つかっていた
PR #4604の提案コメント(作成者Wauplin氏)を読むと、resolve_revisionが生まれた背景に、単発の思いつきではない経緯があったことが分かる。
I recently worked on vllm-project/vllm#49990 and huggingface/transformers#47611 to tackle an annoying problem regarding revision resolution. In a complex ML library, revision must be resolved first so that all methods/download/listing etc. are done with the exact same commit hash.
(最近、vLLMとTransformersで、revision解決にまつわる厄介な問題に取り組んだ。複雑なMLライブラリでは、すべてのメソッド・ダウンロード・一覧取得などが同一のコミットハッシュで行われるよう、まずrevisionを解決しておく必要がある)
つまり「revisionを1回だけ解決してハッシュを使い回す」という設計は、huggingface_hub単体の発明ではなく、vLLM(vllm-project/vllm#49990)・Transformers(huggingface/transformers#47611)で個別に対処されてきた同じ問題を、huggingface_hub本体に一般化して持ち込んだものだった。実際、この機能は後にsentence-transformers(#3890、v6.1向け)やdiffusers(#14340、from_pretrainedの冒頭でrevisionを1回だけ解決)にも波及している。
8月27日、resolve_revision自体のバグが見つかり修正された
さらに興味深いのは、この新機能自体が実運用で不具合を起こし、本記事執筆日と同じ2026年8月27日にフォローアップ修正(PR #4767)がマージされていたことだ。
Problem: doing
resolve_revision(revision)twice for the same revision (e.g. "main") but on two different repos returns the same commit oid because repo type / id are not checked. [...] It already bit downstream integrations twice: [Bugfix] Skip fetching revision for model when model and weights_model are different vllm-project/vllm#51260 — a GGUF model reads its config from the base repo and its weights from the GGUF repo; the base repo's commit was handed to the weights repo => RevisionNotFoundError.
(問題:同じrevision(例:"main")に対してresolve_revision(revision)を、異なる2つのリポジトリで呼び出すと、リポジトリの種別・IDがチェックされていないため、同じコミットIDが返ってしまう。[中略]この問題は既に2つの下流統合で実害を起こしていた:vLLMのバグ修正——GGUFモデルは設定をベースリポジトリから、重みを別のGGUFリポジトリから読むが、ベースリポジトリのコミットが誤って重みリポジトリに渡され、RevisionNotFoundErrorが発生していた)
修正内容は、ResolvedRevisionが自分がどのリポジトリ(repo_id+repo_type)に対して解決されたかを内部的に覚えておき、別のリポジトリに対して使われた場合は、渡された値をそのまま使うのではなく、通常のrevision="main"と同じように改めて解決し直す、というものだ。vLLM側では、このバグを避けるための回避策(vllm-project/vllm#51260)が既に入っており、huggingface_hub側の修正が正式にリリースされ次第、その回避策は取り消される予定だとPR本文に書かれている。
つまりv1.26.0で導入されたresolve_revisionは、「複数ファイルを同一コミットから取得する」という当初の目的は達成しつつも、「同じResolvedRevisionオブジェクトを別のリポジトリに使い回すと壊れる」という新しい種類の不具合を生み、それが実際に他プロジェクトのバグ報告という形で表面化してから約1ヶ月で修正された、という経緯があったことになる。
CVE-2026-15717──UNCパスでNetNTLMv2ハッシュが盗まれる仕組み
修正PR #4540の本文は、脆弱性の中身を次のように説明している。従来の_local_folder.pyは--local-dirへのダウンロード・アップロード時に「Windowsのみを対象にした..\の文字列チェック」しか持っておらず、パスの「封じ込め(containment)」チェックが無かった。絶対パス(C:\...)、ドライブ相対パス(D:foo)、ルート相対パス(\foo)、UNCパス(\\host\share\...)はいずれも..\という文字列ではないため、このチェックを通過してしまう。local_dir / filenameという結合は、右側がアンカーされたパス(絶対パス等)だった場合に左側のlocal_dirを単純に捨ててしまうため、悪意あるリポジトリをpullしたWindowsクライアント上で任意ファイル書き込みが起きる。さらにUNC形式のファイル名の場合、Windowsクライアントがパス解決の過程で攻撃者のSMBサーバーに対して自動的に認証を試み、NetNTLMv2ハッシュが漏洩する、とPR本文は説明している。
修正は、local_dir経路(ダウンロード・アップロード両方)とキャッシュのダウンロード経路の両方に共通の_validate_relative_filenameヘルパーを適用し、..トラバーサルとアンカー付きパス(絶対・ドライブ・ルート・UNC)を、POSIXとWindows両方のルールで全プラットフォームに対して弾くようにした、というものだ。PR本文は挙動の変化も明記している。
>>> from huggingface_hub._local_folder import get_local_download_paths
>>> from pathlib import Path
>>> get_local_download_paths(Path("/home/me/models"), "C:\\Windows\\System32\\evil")
ValueError: Invalid filename 'C:\Windows\System32\evil': cannot be an absolute, drive-relative or UNC path. ...
>>> get_local_download_paths(Path("/home/me/models"), "\\\\attacker\\share\\evil")
ValueError: Invalid filename '\\attacker\share\evil': cannot be an absolute, drive-relative or UNC path. ...
>>> get_local_download_paths(Path("/home/me/models"), "path/in/repo.txt").file_path
PosixPath('/home/me/models/path/in/repo.txt') # normal files unaffected
正規のリポジトリのファイル名が..パスセグメントやドライブ・UNCのアンカーを持つことは無いため、通常のダウンロードには影響しない、ともPR本文は明記している。一方で、folder/..\..\..\fileのような「バックスラッシュはLinux上では単なる文字なので、以前はLinuxでダウンロードできてしまっていた」変わった名前は、全プラットフォームで拒否されるようになった。
HFトークンのサンドボックス漏洩も同時修正
リリースノートは、もう1件の修正としてSandbox.createがsbx-serverバイナリを取得するために利用者のHFトークンをジョブ環境へ注入していた問題を挙げている。修正後は、バケットが公開設定であることを踏まえてブートストラップを匿名でダウンロードするようになり、forward_hf_token=Trueを明示的に指定しない限り、HF認証情報がサンドボックスに渡らなくなった。
バージョンとPRの対応
| 項目 | 内容 |
|---|---|
| リリース日 | 2026-07-30 |
| バージョン | v1.26.0 |
| resolve_revision実装 | PR #4604(@Wauplin) |
| CVE-2026-15717修正 | PR #4540(@Wauplin) |
| HFトークン漏洩修正 | PR #4583(@Wauplin) |
| 影響対象 | --local-dirへのダウンロード・アップロードを使う全プラットフォームのクライアント(従来はWindowsのみ一部チェックあり) |
CVEの詳細について確認できていないこと
CVE-2026-15717について、GitHub Security Advisories・OSV.dev(api.osv.dev/v1/vulns/CVE-2026-15717)の両方を本記事執筆時点(2026年8月30日)で再度照会したが、依然としてどちらからも詳細(CVSSスコアや発見者名など)を取得できなかった。修正PRの本文にも「脆弱性そのものの発見日」の記載は無く、リリース日(2026-07-30)が確認できる唯一の日付情報になっている。筆者は_local_folder.pyのコード変更を手元でチェックアウトして再現実験する検証はしておらず、PR本文に書かれたコード例をそのまま引用している。resolve_revisionのクロスリポジトリ不具合(PR #4767)についても、修正後のバージョンが実際にリリースされ、vLLM側の回避策(vllm-project/vllm#51260)が本当に取り消されたかどうかまでは、本記事執筆時点では追跡していない。Zenn記事検索では「huggingface_hub」という文字列自体の該当記事はこの記事作成時点で見当たらなかった。
関連記事
出典・参照資料
- 二次資料[v1.26.0] Resolve revisions only once, security hardening, and resource groups for Jobs & Collections(GitHub公式リリースノート) ↗
- 二次資料[Download] Reject absolute/UNC/traversal filenames on local_dir & cache paths(PR #4540・CVE-2026-15717の修正本体) ↗
- 二次資料PR #4604: Add HfApi.resolve_revision and ResolvedRevision(実装本体・vLLM/Transformersとの関係) ↗
- 二次資料PR #4767: A ResolvedRevision only pins the repo it was resolved for(8/27マージのフォローアップ修正) ↗
AIニュースの解説を動画でも
YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。
コメント
まだコメントはありません。最初のコメントを書いてみませんか?
AIについて聞きたいことはありますか?
質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。
質問箱を見る →新しい記事をメールで受け取る
AIの新しい発表を、出典付きで整理して届けます。