6 月 Townhall 更新:94 個 Bug 修正、OCR + Realtime 已採用 Rust,以及零回歸承諾
感謝所有參加我們 6 月 town hall 的人。
三個數字概括了這個月:24 個安全性修正、94 個 bug 修正,以及78 個功能提交。以下各節將逐一拆解,並說明我們對零已回報回歸的公開承諾,以及 LiteLLM gateway 逐步遷移到 Rust 的進程。
感謝所有參加我們 6 月 town hall 的人。
三個數字概括了這個月:24 個安全性修正、94 個 bug 修正,以及78 個功能提交。以下各節將逐一拆解,並說明我們對零已回報回歸的公開承諾,以及 LiteLLM gateway 逐步遷移到 Rust 的進程。
針對 LiteLLM proxy 這個 Host-header 認證繞過的更新已於 v1.84.0 發布,後續的路徑處理強化則已完成,並在 v1.84.3、v1.85.2、v1.86.2 與 v1.83.10-stable.patch.3 中回補到所有維護中的發行線。繞過的潛在風險僅限於同時具備下列三項特定條件的部署。此繞過由 Le The Thang(KCSC)與 Kim Ngoc Chung(One Mount Group)回報。
在 proxy listener 可透過任意 Host 標頭存取時,這些條件可能允許未經驗證的存取進入受保護的管理路由。
沒有任何 LiteLLM Cloud 客戶受到影響。此次更新已在本公告發表前,部署到所有 LiteLLM Cloud 環境中——並回補到使用中的發行線。
v1.84.0v1.84.3、v1.85.2 與 v1.86.2v1.84.0 或更新版本。無需變更設定。關於此公告的更多資訊請見這裡:https://github.com/BerriAI/litellm/security/advisories/GHSA-4xpc-pv4p-pm3w. CVE:https://www.cve.org/CVERecord?id=CVE-2026-48710.
感謝所有參加我們 5 月 town hall 的人。
我們涵蓋了安全強化、版本發布編號變更、新產品發布(MCP 工具集、on-behalf-of OAuth)、效能提升,以及我們迄今最大的一個押注——LiteLLM Agent Platform。
2026 年 5 月 11 日,來自 Aikido Security 的資安研究人員發現了一起名為 "Mini Shai-Hulud" 的協同供應鏈攻擊,該攻擊發布了超過 170 個 npm 套件與 2 個 PyPI 套件的惡意版本,其中包括 mistralai==2.4.6。
LiteLLM 不受影響。 我們透過 httpx 直接經由 HTTP 呼叫 Mistral 的 API,且在程式碼庫中的任何地方都不會匯入 mistralai Python SDK。
mistralai 套件。 我們呼叫 Mistral 的 API 方式與呼叫其他所有提供者相同(透過 httpx)。這個遭入侵的套件在任何 LiteLLM 環境中都不會被執行。import mistralai 時執行。由於 LiteLLM 從未觸發該匯入,因此負載永遠不會執行。mistralai==2.4.6,請立即遵循 Mistral AI 的指引。我們最近針對 LiteLLM Proxy 發布了一則安全公告。
我們透過 bug bounty program 收到一份關於 LiteLLM Proxy 的 API key 驗證路徑中存在 SQL injection 漏洞的回報,該漏洞追蹤編號為 CVE-2026-42208。
此問題已由我們的團隊審查、在穩定版本中修正,並以 GitHub Security Advisory 的形式發布。
v1.81.16 至 v1.83.6v1.83.7 及之後版本v1.83.10-stable穩定版本:https://github.com/BerriAI/litellm/releases/tag/v1.83.10-stable
公告:https://github.com/BerriAI/litellm/security/advisories/GHSA-r75f-5x8p-qvmc
2026 年 4 月 15 日,OX Security 發布了一則公告,說明 Anthropic 的 MCP SDK 的 stdio 傳輸中的命令注入(StdioServerParameters 會執行它拿到的任何 command)。LiteLLM 自 v1.83.6-nightly 起已修復此問題。
此修補已在 commit 7b7f304(PR #25343)中完成,且自 v1.83.6-nightly 起的每個版本都已包含。v1.83.7-stable 也包含此修補。
/mcp-rest/test/* 預覽端點)都位於 LiteLLM 的驗證之後。攻擊者必須先具備有效的 LiteLLM API 金鑰——而且在套用修補後,還需要 PROXY_ADMIN 角色——才能 पहुँच पहुँच到這段程式路徑。v1.83.6-nightly 起已上線。 含有修補的第一個 stable 版本是 v1.83.7-stable。完整的已修補版本清單請見下方。
Akto 現已原生在 LiteLLM proxy 中以串接式防護欄運作。
感謝所有參加我們 4 月 town hall 的朋友。
我們在這次會議中分享了 CI/CD v2 的改進、產品穩定性工作,以及接下來在可靠性與產品藍圖上的優先事項。
在 3 月的 供應鏈事件 之後,我們請 Veria Labs 針對 LiteLLM proxy 進行稽核,並修正了來自獨立研究人員的多項漏洞回報。以下所有問題都已在 v1.83.0 中修正。如果您受到影響,特別是如果您已啟用 JWT 驗證,我們建議升級。
我們也已推出 漏洞賞金計畫,而 Veria Labs 也持續對 proxy 進行稽核。更多修正將在後續版本中推出。
這兩個高嚴重性問題(CVE-2026-35029 和 GHSA-69x8-hrgq-fjj8)都要求攻擊者已經持有 proxy 的有效 API 金鑰。未經驗證的使用者無法利用這些問題。
這個關鍵嚴重性問題(CVE-2026-35030)是驗證繞過,但只有在明確啟用 enable_jwt_auth 的部署中才會受影響,而該功能預設為關閉。預設的 LiteLLM 設定不受影響,且沒有 LiteLLM Cloud 客戶啟用此功能。
CI/CD v2 現已於 LiteLLM 上線。
承接我們的 安全事件 路線圖,CI/CD v2 為 LiteLLM 導入了隔離環境、更強的安全防護欄,以及更安全的發佈分離。
從 v1.83.0-nightly 開始,所有發布到 GHCR 的 LiteLLM Docker 映像都會使用 cosign 簽署。每個發佈都使用相同的金鑰簽署,該金鑰在 commit 0112e53 中引入。
使用固定的 commit hash 驗證(建議):
commit hash 在密碼學上不可變,因此這是確保您使用原始簽署金鑰的最強方式:
cosign verify \
--key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \
ghcr.io/berriai/litellm:<release-tag>
使用 release tag 驗證(方便):
本儲存庫中的標籤受到保護,並解析為相同的金鑰。此選項較容易閱讀,但仰賴標籤保護規則:
cosign verify \
--key https://raw.githubusercontent.com/BerriAI/litellm/<release-tag>/cosign.pub \
ghcr.io/berriai/litellm:<release-tag>
請將 <release-tag> 替換為您要部署的版本(例如 v1.83.0-stable)。
預期輸出:
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
接下來,我們計劃:
採用 OpenSSF(這是一組專案應遵守的安全標準,用來展現強健的安全態勢——了解更多)
將 SLSA Build Provenance 加入我們的 CI/CD pipeline——這表示我們允許使用者獨立驗證發佈確實來自我們,並防止發佈在公開後遭到靜默修改。
我們希望這將意味著您可以放心,您所使用的發佈是安全且確實來自我們的。
新的 CI/CD pipeline 反映了以下列出的原則,並設計得更安全且更可靠:
協助我們規劃 4 月的穩定性衝刺——https://github.com/BerriAI/litellm/issues/24825

我們正與 Vanta 合作,為 LiteLLM 的 SOC 2 Type 2 與 ISO 27001 合規性進行重新認證。
作為此流程的一部分,我們也在尋找獨立稽核員,以驗證並確認我們的合規狀態。
這是我們致力於成為最安全且最透明的 AI Gateway 的一環。
感謝所有參加我們 town hall 的人。
我們想利用那段時間說明我們目前所知、到目前為止已採取的行動,以及未來如何改進 LiteLLM 的發布與安全流程。這篇貼文是該更新的書面版本。 投影片請見此處
狀態: 調查進行中 最後更新: 2026 年 3 月 27 日
更新(3 月 30 日): LiteLLM 的全新安全版本現已可用(v1.83.0)。這是由我們新的 CI/CD v2 管線發佈,該管線加入了隔離環境、更強的安全閘道,以及更安全的 LiteLLM 發佈分離機制。
更新(3 月 27 日): 請查看 Townhall 更新,包括事件說明、我們已採取的措施,以及後續內容。了解更多
更新(3 月 27 日): 新增 已驗證的安全版本 區段,提供所有經審核的 PyPI 與 Docker 發佈版的 SHA-256 檢查碼。
更新(3 月 26 日): 新增
checkmarx[.]zone至 入侵指標
更新(3 月 25 日): 新增社群貢獻的腳本,用於掃描 GitHub Actions 與 GitLab CI 管線中是否使用了受影響版本。請參閱 如何檢查您是否受影響。感謝 @Zach Fury 提供這些腳本。
main。LiteLLM AI Gateway 正在調查一起疑似供應鏈攻擊,涉及未經授權的 PyPI 套件發佈。目前證據顯示,一名維護者的 PyPI 帳戶可能已遭入侵,並被用來散布惡意程式碼。
目前我們認為此事件可能與更廣泛的 Trivy 安全入侵 有關;據報導,遭竊的憑證被用來未經授權存取 LiteLLM 發佈管線。
此調查仍在進行中。以下細節在我們確認更多發現後可能會變更。
以下發佈到 PyPI 的 LiteLLM 版本受到影響:
proxy_server.py 中包含惡意負載litellm_init.pth,以及在 LiteLLM AI Gateway proxy_server.py 中包含惡意負載如果您安裝或執行了這兩個版本中的任一版本,請立即查看以下建議。
注意:這些版本已從 PyPI 移除。
初步證據顯示,攻擊者繞過官方 CI/CD 工作流程,直接將惡意套件上傳到 PyPI。
這些受影響版本似乎包含一個憑證竊取程式,設計目的為:
POST 的 models.litellm.cloud 請求加密並外洩資料,該網域不是官方 BerriAI / LiteLLM 網域如果以下任何情況為真,您可能受影響:
pip 安裝或升級 LiteLLMpip install litellm 時未固定版本,並收到 v1.82.7 或 v1.82.8pip install litellm 但未固定版本的 Docker 映像檔如果以下任何情況為真,您不受影響:
LiteLLM AI Gateway/Proxy 使用者: 使用官方 LiteLLM Proxy Docker 映像檔的客戶未受影響。該部署路徑在 requirements.txt 中固定依賴版本,且不依賴受影響的 PyPI 套件。
ghcr.io/berriai/litellmpip show litellm
前往 Proxy base url,並檢查已安裝 LiteLLM 的版本。

掃描 GitHub 組織中的所有儲存庫,尋找安裝了受影響版本的工作流程作業。
需求: Python 3 與 requests(pip install requests)。
設定:
export GITHUB_TOKEN="your-github-pat"
執行:
python find_litellm_github.py
將腳本中的 ORG 變數設定為您的 GitHub 組織名稱。
兩個腳本預設都只掃描 今天 的作業。若在其他日期執行,請調整 WINDOW_START 與 WINDOW_END 常數,以涵蓋 2026 年 3 月 24 日(事件日期)。查看完整腳本(find_litellm_github.py)
#!/usr/bin/env python3
"""
Scan all GitHub Actions jobs in a GitHub org that ran between
0800-1244 UTC today and identify any that installed litellm 1.82.7 or 1.82.8.
Adjust WINDOW_START / WINDOW_END to cover March 24, 2026 if running later.
"""
import io
import os
import re
import sys
import zipfile
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone
import requests
GITHUB_URL = "https://api.github.com"
ORG = "your-org" # <-- set to your GitHub organization
TOKEN = os.environ.get("GITHUB_TOKEN", "")
TODAY = datetime.now(timezone.utc).date()
WINDOW_START = datetime(TODAY.year, TODAY.month, TODAY.day, 8, 0, 0, tzinfo=timezone.utc)
WINDOW_END = datetime(TODAY.year, TODAY.month, TODAY.day, 12, 44, 0, tzinfo=timezone.utc)
TARGET_VERSIONS = {"1.82.7", "1.82.8"}
VERSION_PATTERN = re.compile(r"litellm[=\-](\d+\.\d+\.\d+)", re.IGNORECASE)
SESSION = requests.Session()
SESSION.headers.update({
"Authorization": f"Bearer {TOKEN}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
})
def get_paginated(url, params=None):
params = dict(params or {})
params.setdefault("per_page", 100)
page = 1
while True:
params["page"] = page
resp = SESSION.get(url, params=params, timeout=30)
if resp.status_code == 404:
return
resp.raise_for_status()
data = resp.json()
if isinstance(data, dict):
items = next((v for v in data.values() if isinstance(v, list)), [])
else:
items = data
if not items:
break
yield from items
if len(items) < params["per_page"]:
break
page += 1
def parse_ts(ts_str):
if not ts_str:
return None
return datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
def get_repos():
repos = []
for r in get_paginated(f"{GITHUB_URL}/orgs/{ORG}/repos", {"type": "all"}):
repos.append({"id": r["id"], "name": r["name"], "full_name": r["full_name"]})
return repos
def get_runs_in_window(repo_full_name):
created_filter = (
f"{WINDOW_START.strftime('%Y-%m-%dT%H:%M:%SZ')}"
f"..{WINDOW_END.strftime('%Y-%m-%dT%H:%M:%SZ')}"
)
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/runs"
runs = []
for run in get_paginated(url, {"created": created_filter, "per_page": 100}):
ts = parse_ts(run.get("run_started_at") or run.get("created_at"))
if ts and WINDOW_START <= ts <= WINDOW_END:
runs.append(run)
return runs
def get_jobs_for_run(repo_full_name, run_id):
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/runs/{run_id}/jobs"
jobs = []
for job in get_paginated(url, {"filter": "all"}):
ts = parse_ts(job.get("started_at"))
if ts and WINDOW_START <= ts <= WINDOW_END:
jobs.append(job)
return jobs
def fetch_job_log(repo_full_name, job_id):
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/jobs/{job_id}/logs"
resp = SESSION.get(url, timeout=60, allow_redirects=True)
if resp.status_code in (403, 404, 410):
return ""
resp.raise_for_status()
content_type = resp.headers.get("Content-Type", "")
if "zip" in content_type or resp.content[:2] == b"PK":
try:
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
parts = []
for name in sorted(zf.namelist()):
with zf.open(name) as f:
parts.append(f.read().decode("utf-8", errors="replace"))
return "\n".join(parts)
except zipfile.BadZipFile:
pass
return resp.text
def check_job(repo_full_name, job):
job_id = job["id"]
job_name = job["name"]
run_id = job["run_id"]
started = job.get("started_at", "")
log_text = fetch_job_log(repo_full_name, job_id)
if not log_text:
return None
found_versions = set()
context_lines = []
for line in log_text.splitlines():
m = VERSION_PATTERN.search(line)
if m:
ver = m.group(1)
if ver in TARGET_VERSIONS:
found_versions.add(ver)
context_lines.append(line.strip())
if not found_versions:
return None
return {
"repo": repo_full_name,
"run_id": run_id,
"job_id": job_id,
"job_name": job_name,
"started_at": started,
"versions": sorted(found_versions),
"context": context_lines[:10],
"job_url": job.get("html_url", f"https://github.com/{repo_full_name}/actions/runs/{run_id}"),
}
def main():
if not TOKEN:
print("ERROR: Set GITHUB_TOKEN environment variable.", file=sys.stderr)
sys.exit(1)
print(f"Time window : {WINDOW_START.isoformat()} -> {WINDOW_END.isoformat()}")
print(f"Hunting for : litellm {', '.join(sorted(TARGET_VERSIONS))}")
print()
print(f"Fetching repositories for org '{ORG}'...")
repos = get_repos()
print(f" Found {len(repos)} repositories")
print()
jobs_to_check = []
print("Scanning workflow runs for time window...")
for repo in repos:
full_name = repo["full_name"]
try:
runs = get_runs_in_window(full_name)
except requests.HTTPError as e:
print(f" WARN: {full_name} - {e}", file=sys.stderr)
continue
if not runs:
continue
print(f" {full_name}: {len(runs)} run(s) in window")
for run in runs:
try:
jobs = get_jobs_for_run(full_name, run["id"])
except requests.HTTPError as e:
print(f" WARN: run {run['id']} - {e}", file=sys.stderr)
continue
for job in jobs:
jobs_to_check.append((full_name, job))
total = len(jobs_to_check)
print(f"\nFetching logs for {total} job(s)...")
print()
hits = []
with ThreadPoolExecutor(max_workers=8) as pool:
futures = {
pool.submit(check_job, full_name, job): (full_name, job["id"])
for full_name, job in jobs_to_check
}
done = 0
for future in as_completed(futures):
done += 1
full_name, jid = futures[future]
try:
result = future.result()
except Exception as e:
print(f" ERROR {full_name} job {jid}: {e}", file=sys.stderr)
continue
if result:
hits.append(result)
print(
f" [{done}/{total}] {full_name} job {jid}" +
(f" *** HIT: litellm {result['versions']} ***" if result else ""),
flush=True,
)
print()
print("=" * 72)
print(f"RESULTS: {len(hits)} job(s) installed litellm {' or '.join(sorted(TARGET_VERSIONS))}")
print("=" * 72)
if not hits:
print("No matches found.")
return
for h in sorted(hits, key=lambda x: x["started_at"]):
print()
print(f" Repo : {h['repo']}")
print(f" Job : {h['job_name']} (#{h['job_id']})")
print(f" Run ID : {h['run_id']}")
print(f" Started : {h['started_at']}")
print(f" Versions : litellm {', '.join(h['versions'])}")
print(f" URL : {h['job_url']}")
print(f" Log lines :")
for line in h["context"]:
print(f" {line}")
if __name__ == "__main__":
main()
掃描 GitLab 群組(包含子群組)中的所有專案,尋找安裝了受影響版本的 CI/CD 作業。
需求: Python 3 與 requests(pip install requests)。
設定:
export GITLAB_TOKEN="your-gitlab-pat"
執行:
python find_litellm_jobs.py
將腳本中的 GROUP_NAME 變數設定為您的 GitLab 群組名稱。
兩個腳本預設都只掃描 今天 的作業。若在其他日期執行,請調整 WINDOW_START 與 WINDOW_END 常數,以涵蓋 2026 年 3 月 24 日(事件日期)。查看完整腳本(find_litellm_jobs.py)
#!/usr/bin/env python3
"""
Scan all GitLab CI/CD jobs in a GitLab group that ran between
0800-1244 UTC today and identify any that installed litellm 1.82.7 or 1.82.8.
Adjust WINDOW_START / WINDOW_END to cover March 24, 2026 if running later.
"""
import os
import re
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone
import requests
GITLAB_URL = "https://gitlab.com"
GROUP_NAME = "YourGroup" # <-- set to your GitLab group name
TOKEN = os.environ.get("GITLAB_TOKEN", "")
TODAY = datetime.now(timezone.utc).date()
WINDOW_START = datetime(TODAY.year, TODAY.month, TODAY.day, 8, 0, 0, tzinfo=timezone.utc)
WINDOW_END = datetime(TODAY.year, TODAY.month, TODAY.day, 12, 44, 0, tzinfo=timezone.utc)
TARGET_VERSIONS = {"1.82.7", "1.82.8"}
VERSION_PATTERN = re.compile(r"litellm[=\-](\d+\.\d+\.\d+)", re.IGNORECASE)
HEADERS = {"PRIVATE-TOKEN": TOKEN}
SESSION = requests.Session()
SESSION.headers.update(HEADERS)
def get_paginated(url, params=None):
params = dict(params or {})
params.setdefault("per_page", 100)
page = 1
while True:
params["page"] = page
resp = SESSION.get(url, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
if not data:
break
yield from data
if len(data) < params["per_page"]:
break
page += 1
def get_group_id(group_name):
resp = SESSION.get(f"{GITLAB_URL}/api/v4/groups/{group_name}", timeout=30)
resp.raise_for_status()
return resp.json()["id"]
def get_all_projects(group_id):
projects = []
for p in get_paginated(
f"{GITLAB_URL}/api/v4/groups/{group_id}/projects",
{"include_subgroups": "true", "archived": "false"},
):
projects.append({"id": p["id"], "name": p["path_with_namespace"]})
return projects
def parse_ts(ts_str):
if not ts_str:
return None
ts_str = ts_str.replace("Z", "+00:00")
return datetime.fromisoformat(ts_str)
def jobs_in_window(project_id):
matching = []
url = f"{GITLAB_URL}/api/v4/projects/{project_id}/jobs"
params = {"per_page": 100, "scope[]": ["success", "failed", "canceled", "running"]}
page = 1
while True:
params["page"] = page
resp = SESSION.get(url, params=params, timeout=30)
if resp.status_code == 403:
return matching
resp.raise_for_status()
jobs = resp.json()
if not jobs:
break
stop_early = False
for job in jobs:
ts = parse_ts(job.get("started_at") or job.get("created_at"))
if ts is None:
continue
if ts > WINDOW_END:
continue
if ts < WINDOW_START:
stop_early = True
continue
matching.append(job)
if stop_early or len(jobs) < 100:
break
page += 1
return matching
def fetch_trace(project_id, job_id):
url = f"{GITLAB_URL}/api/v4/projects/{project_id}/jobs/{job_id}/trace"
resp = SESSION.get(url, timeout=60)
if resp.status_code in (403, 404):
return ""
resp.raise_for_status()
return resp.text
def check_job(project_name, project_id, job):
job_id = job["id"]
job_name = job["name"]
ref = job.get("ref", "")
started = job.get("started_at", job.get("created_at", ""))
trace = fetch_trace(project_id, job_id)
if not trace:
return None
found_versions = set()
for match in VERSION_PATTERN.finditer(trace):
ver = match.group(1)
if ver in TARGET_VERSIONS:
found_versions.add(ver)
if not found_versions:
return None
context_lines = []
for line in trace.splitlines():
if VERSION_PATTERN.search(line):
ver_match = VERSION_PATTERN.search(line)
if ver_match and ver_match.group(1) in TARGET_VERSIONS:
context_lines.append(line.strip())
return {
"project": project_name,
"project_id": project_id,
"job_id": job_id,
"job_name": job_name,
"ref": ref,
"started_at": started,
"versions": sorted(found_versions),
"context": context_lines[:10],
"job_url": f"{GITLAB_URL}/{project_name}/-/jobs/{job_id}",
}
def main():
if not TOKEN:
print("ERROR: Set GITLAB_TOKEN environment variable.", file=sys.stderr)
sys.exit(1)
print(f"Time window : {WINDOW_START.isoformat()} -> {WINDOW_END.isoformat()}")
print(f"Hunting for : litellm {', '.join(sorted(TARGET_VERSIONS))}")
print()
print(f"Resolving group '{GROUP_NAME}'...")
group_id = get_group_id(GROUP_NAME)
print("Fetching projects...")
projects = get_all_projects(group_id)
print(f" Found {len(projects)} projects")
print()
all_jobs_to_check = []
print("Scanning job listings for time window...")
for proj in projects:
try:
jobs = jobs_in_window(proj["id"])
except requests.HTTPError as e:
print(f" WARN: {proj['name']} - {e}", file=sys.stderr)
continue
if jobs:
print(f" {proj['name']}: {len(jobs)} job(s) in window")
for j in jobs:
all_jobs_to_check.append((proj["name"], proj["id"], j))
total = len(all_jobs_to_check)
print(f"\nFetching traces for {total} job(s)...")
print()
hits = []
with ThreadPoolExecutor(max_workers=10) as pool:
futures = {
pool.submit(check_job, pname, pid, job): (pname, job["id"])
for pname, pid, job in all_jobs_to_check
}
done = 0
for future in as_completed(futures):
done += 1
pname, jid = futures[future]
try:
result = future.result()
except Exception as e:
print(f" ERROR checking {pname} job {jid}: {e}", file=sys.stderr)
continue
if result:
hits.append(result)
print(f" [{done}/{total}] checked {pname} job {jid}" +
(f" *** HIT: litellm {result['versions']} ***" if result else ""),
flush=True)
print()
print("=" * 72)
print(f"RESULTS: {len(hits)} job(s) installed litellm {' or '.join(sorted(TARGET_VERSIONS))}")
print("=" * 72)
if not hits:
print("No matches found.")
return
for h in sorted(hits, key=lambda x: x["started_at"]):
print()
print(f" Project : {h['project']}")
print(f" Job : {h['job_name']} (#{h['job_id']})")
print(f" Branch/tag: {h['ref']}")
print(f" Started : {h['started_at']}")
print(f" Versions : litellm {', '.join(h['versions'])}")
print(f" URL : {h['job_url']}")
print(f" Log lines :")
for line in h["context"]:
print(f" {line}")
if __name__ == "__main__":
main()
由社群貢獻的 CI/CD 腳本(原始 gist)。執行前請先審查。
請檢查受影響系統是否有以下指標:
site-packages 中存在 litellm_init.pthmodels.litellm[.]cloud 的對外流量或請求
此網域不隸屬於 LiteLLMcheckmarx[.]zone 的對外流量或請求
此網域不隸屬於 LiteLLM如果您安裝或執行了 v1.82.7 或 v1.82.8,請立即採取以下行動。
將受影響系統中存在的任何憑證視為已遭入侵,包括:
檢查您的 site-packages 目錄中是否有名為 litellm_init.pth 的檔案:
find /usr/lib/python3.13/site-packages/ -name "litellm_init.pth"
如果存在:
檢視您的:
確認是否在任何地方安裝了 v1.82.7 或 v1.82.8。
將 LiteLLM 固定到已知安全的版本,例如 v1.82.6 或更早版本,或在稍後公告的已驗證版本。
LiteLLM AI Gateway 團隊已採取以下步驟:
自 v1.83.0-nightly 起,所有發布到 GHCR 的 LiteLLM Docker 映像都已使用 cosign 進行簽署。每個版本都使用在 commit 0112e53 中引入的相同金鑰簽署。
使用固定的 commit hash 驗證(建議):
commit hash 在密碼學上不可變,因此這是確認您使用的是原始簽署金鑰的最強方式:
cosign verify \
--key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \
ghcr.io/berriai/litellm:<release-tag>
使用 release 標籤驗證(方便):
本儲存庫中的標籤受到保護,並會解析為相同的金鑰。此選項較容易閱讀,但仰賴標籤保護規則:
cosign verify \
--key https://raw.githubusercontent.com/BerriAI/litellm/<release-tag>/cosign.pub \
ghcr.io/berriai/litellm:<release-tag>
請將 <release-tag> 替換為您正在部署的版本(例如 v1.83.0-stable)。
預期輸出:
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
我們已審查在 v1.78.0 到 v1.82.6 之間發布的每個 LiteLLM 版本,涵蓋 PyPI 與 Docker。每個構件都已透過以下方式驗證:
下列列出的所有版本都已確認乾淨。
| 版本 | SHA-256 | 未發現入侵指標 | 符合 Git | Git 提交 | 狀態 |
|---|---|---|---|---|---|
| 1.82.6 | 164a3ef3e19f309e… | ✔ 乾淨 | ✔ 是 | 38d477507dad | ✔ 乾淨 |
| 1.82.5 | e1012ab816352215… | ✔ 乾淨 | ✔ 是 | 1998c4f3703f | ✔ 乾淨 |
| 1.82.4 | d37c34a847e7952a… | ✔ 乾淨 | ✔ 是 | cfeafbe38811 | ✔ 乾淨 |
| 1.82.3 | 609901f6c5a5cf8c… | ✔ 乾淨 | ✔ 是 | 61409275c8d8 | ✔ 乾淨 |
| 1.82.2 | 641ed024774fa3d5… | ✔ 乾淨 | ✔ 是 | f351bbdb3683 | ✔ 乾淨 |
| 1.82.1 | a9ec3fe42eccb161… | ✔ 乾淨 | ✔ 是 | 94b002066e3a | ✔ 乾淨 |
| 1.82.0 | 5496b5d4532cccdc… | ✔ 乾淨 | ✔ 是 | 6c6585af568e | ✔ 乾淨 |
| 1.81.16 | d6bcc13acbd26719… | ✔ 乾淨 | ✔ 是 | 678200ee4887 | ✔ 乾淨 |
| 1.81.15 | 2fa253658702509c… | ✔ 乾淨 | ✔ 是 | 2e819656cee9 | ✔ 乾淨 |
| 1.81.14 | 6394e61bbdef7121… | ✔ 乾淨 | ✔ 是 | 96bcee0b0af7 | ✔ 乾淨 |
| 1.81.13 | ae4aea2a55e85993… | ✔ 乾淨 | ✔ 是 | cc957a19a560 | ✔ 乾淨 |
| 1.81.12 | 219cf9729e5ea30c… | ✔ 乾淨 | ✔ 是 | ba0d541b1982 | ✔ 乾淨 |
| 1.81.11 | 06a66c24742e082d… | ✔ 乾淨 | ✔ 是 | 231aedeeff7e | ✔ 乾淨 |
| 1.81.10 | 9efa1cbe61ac051f… | ✔ 乾淨 | ✔ 是 | 7488abece8e7 | ✔ 乾淨 |
| 1.81.9 | 24ee273bc8a62299… | ✔ 乾淨 | ✔ 是 | a09d3e9162eb | ✔ 乾淨 |
| 1.81.8 | 78cca92f36bc6c26… | ✔ 乾淨 | ✔ 是 | 4fea649f519b | ✔ 乾淨 |
| 1.81.7 | 58466c88c3289c6a… | ✔ 乾淨 | ✔ 是 | 3f6a281d0f7a | ✔ 乾淨 |
| 1.81.6 | 573206ba194d49a1… | ✔ 乾淨 | ✔ 是 | 8da3a93e6e63 | ✔ 乾淨 |
| 1.81.5 | 206505c5a0c6503e… | ✔ 乾淨 | ✔ 是 | 2cc3778761d4 | ✔ 乾淨 |
| 1.81.3 | 3f60fd8b72758795… | ✔ 乾淨 | ✔ 是 | f30742fe6e8e | ✔ 乾淨 |
| 版本 | SHA-256 | 未發現入侵指標 | 符合 Git | Git 提交 | 狀態 |
|---|---|---|---|---|---|
| 1.82.3 | 0a571da849db5f9c… | ✔ 乾淨 | ✔ 是 | 61409275c8d8 | ✔ 乾淨 |
| 1.82.3-stable | 0c2b2a0ad3e50af1… | ✔ 乾淨 | ✔ 是 | 61409275c8d8 | ✔ 乾淨 |
| 1.82.0-stable | 71bf7283767ca436… | ✔ 乾淨 | ✔ 是 | 97947c254252 | ✔ 乾淨 |
| 1.81.15 | 303c31af87e7915e… | ✔ 乾淨 | ✔ 是 | 20bf3aa8070a | ✔ 乾淨 |
| 1.81.14-stable | a34f975804823181… | ✔ 乾淨 | ✔ 是 | 0435375b1271 | ✔ 乾淨 |
| 1.81.13 | a876f3f22f9b6fd4… | ✔ 乾淨 | ✔ 是 | cc957a19a560 | ✔ 乾淨 |
| 1.81.12-stable | e24022878ccc87f5… | ✔ 乾淨 | ✔ 是 | ba0d541b1982 | ✔ 乾淨 |
| 1.81.9-stable | 262e53d7702ed825… | ✔ 乾淨 | ✔ 是 | a09d3e9162eb | ✔ 乾淨 |
| 1.81.3-stable | dff82ccc32fb6489… | ✔ 乾淨 | ✔ 是 | 61ed8f9e0355 | ✔ 乾淨 |
| 1.81.0-stable | f4913297d1bb3dc3… | ✔ 乾淨 | ✔ 是 | 790a5ce0b323 | ✔ 乾淨 |
| 1.80.15-stable | 0b4ec3861e978b4a… | ✔ 乾淨 | ✔ 是 | 17c8d8d109b5 | ✔ 乾淨 |
| 1.80.11-stable | 4068108d9101cd2a… | ✔ 乾淨 | ✔ 是 | 57e07bddd341 | ✔ 乾淨 |
| 1.80.8-stable | 0304c2eb1f3cf542… | ✔ 乾淨 | ✔ 是 | 3381d63152f8 | ✔ 乾淨 |
| 1.80.5-stable | a89e173135fff96a… | ✔ 乾淨 | ✔ 是 | 6c49b95a4ab7 | ✔ 乾淨 |
| 1.80.0-stable | a3416f4cd0c896c9… | ✔ 乾淨 | ✔ 是 | 98365205acd0 | ✔ 乾淨 |
| 1.79.3-stable | 27aae83d6ab6cb0b… | ✔ 乾淨 | ✔ 是 | c0548542d4a9 | ✔ 乾淨 |
| 1.79.1-stable | 7780d29a9543c4ce… | ✔ 乾淨 | ✔ 是 | c217bddb59ba | ✔ 乾淨 |
| 1.79.0-stable | 32bf6ac059a56641… | ✔ 乾淨 | ✔ 是 | 8d495f56a9cc | ✔ 乾淨 |
| 1.78.5-stable | d5e607648eafa15e… | ✔ 乾淨 | ✔ 是 | c471bf1f16c2 | ✔ 乾淨 |
| 1.78.0-stable | 7a56b32dc7153763… | ✔ 乾淨 | ✔ 是 | 5fde83d9f154 | ✔ 乾淨 |
如果您認為您的系統可能受到影響,請立即聯絡我們:
security@berri.aisupport@berri.ai如需即時更新,請追蹤 X 上的 LiteLLM (YC W23)。
日期: 2026 年 3 月 18 日 持續時間: 不明 嚴重性: 高 狀態: 已修復
當自訂 guardrail 傳回完整的 LiteLLM request/data dictionary 時,LiteLLM 記錄的 guardrail 回應可能包含 secret_fields.raw_headers,其中包括含有 API 金鑰或其他憑證的明文 Authorization 標頭。
這些資訊接著可能傳播到會消耗 guardrail 中繼資料的記錄與可觀測性表面,包括:
LLM 呼叫、proxy 路由,以及提供者執行都不會因這個 bug 而被阻擋。影響是敏感請求標頭在可觀測性與記錄路徑中被暴露。