Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 62 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -825,36 +825,74 @@ jobs:
# auditing first keeps the gate honest. Do NOT fold the scanner
# install back into the step above.
#
# filelock CVE-2026-112940 / -112913 / -112907 (ignored 2026-10-26,
# founder-approved). All three are in filelock's SQLite-backed
# ReadWriteLock / AsyncReadWriteLock, and that API is UNREACHABLE in
# our closure. Verified, not assumed: no package in a resolved
# `.[dev,local]` env references ReadWriteLock outside filelock itself;
# fluid_build never imports filelock; and the six packages that do
# (cachecontrol, huggingface_hub, litellm, mypy, mypyc, pip) all use
# the plain FileLock. All three are also AV:L/PR:L -- they need a
# second local user able to create or rename entries in the lock
# database's own directory.
# filelock: every advisory whose only fix is filelock >= 4.0 is shown as
# a warning, not a failure (founder decision 2026-10-29). Thirteen were
# published between 2026-10-26 and 2026-10-29 (seven on the 29th alone),
# all fixed only in 4.x, so ignoring them by ID chased a moving target.
# The fix cannot be resolved here: litellm is a BASE dependency (not an
# extra) and its latest release (1.106.6 on 2026-10-29) still declares
# filelock<4.0 (checked against the PyPI JSON API), so pinning the fixed
# version makes the install unsatisfiable. virtualenv is not a blocker --
# its latest allows filelock<5. Upstream tracker: BerriAI/litellm#48394.
#
# The fix is filelock 4.2.1, which CANNOT be resolved here: litellm is
# a BASE dependency (not an extra) and its latest release 1.106.4 still
# declares filelock<4.0 (checked against the PyPI JSON API), so pinning
# the fixed version makes the install unsatisfiable. virtualenv is not a
# blocker -- its latest allows filelock<5.
# What the filelock advisories touch: ReadWriteLock / StrictSoftFileLock
# are used by nothing in the closure; the plain FileLock (pip /
# cachecontrol, huggingface_hub, litellm, safety, mypy) and SoftFileLock
# (huggingface_hub's fallback where flock is missing) are, with lock
# files in the running user's own caches; all need a second local user
# (AV:L) who can rename or hard-link entries in the lock's directory.
# fluid_build never imports filelock.
#
# Upstream tracker: BerriAI/litellm#48394 (asks litellm to lift the
# cap; virtualenv already moved to filelock<5).
#
# REMOVE all three as soon as litellm lifts the cap and
# Still failing the gate: any other package, any filelock advisory with
# a fix inside 3.x or none listed, and a pip-audit run that errors.
# REMOVE the filelock allowance as soon as litellm lifts the cap and
# `filelock>=4.2.1` resolves. Re-check with:
# curl -s https://pypi.org/pypi/litellm/json \
# | python -c "import json,sys;print([r for r in json.load(sys.stdin)['info']['requires_dist'] if 'filelock' in r])"
# Scoped by ID deliberately: any OTHER filelock CVE still fails the gate.
run: |
pip-audit --desc \
--ignore-vuln CVE-2026-112940 \
--ignore-vuln CVE-2026-112913 \
--ignore-vuln CVE-2026-112907
set +e
pip-audit --desc --format json --output pip-audit.json
audit_rc=$?
set -e
python - "$audit_rc" <<'PY'
import json
import sys

audit_rc = int(sys.argv[1])
try:
with open("pip-audit.json", encoding="utf-8") as handle:
report = json.load(handle)
except (OSError, ValueError) as exc:
sys.exit(f"pip-audit gave no report (exit {audit_rc}): {exc}")
if audit_rc not in (0, 1):
sys.exit(f"pip-audit failed (exit {audit_rc})")


def major(version):
head = version.split(".", 1)[0]
return int(head) if head.isdigit() else -1


failing, allowed = [], []
for dep in report.get("dependencies", []):
for vuln in dep.get("vulns", []):
row = (dep["name"], dep["version"], vuln["id"], vuln.get("fix_versions") or [])
fixes = row[3]
only_in_4x = bool(fixes) and min(major(v) for v in fixes) >= 4
if dep["name"].lower() == "filelock" and major(dep["version"]) < 4 and only_in_4x:
allowed.append(row)
else:
failing.append(row)
for name, version, vid, fixes in allowed:
print(
f"::warning::{name} {version} {vid}: fixed only in {', '.join(fixes)}, "
"which litellm's filelock<4.0 cap blocks (BerriAI/litellm#48394)"
)
for name, version, vid, fixes in failing:
print(f"::error::{name} {version} {vid}: fix {', '.join(fixes) or 'none listed'}")
print(f"{len(failing)} failing, {len(allowed)} filelock advisories allowed until the cap lifts")
sys.exit(1 if failing else 0)
PY
- name: Install SAST scanners
run: pip install bandit "semgrep==1.163.0"
- name: Bandit security scan
Expand Down
Loading