unsloth/scripts
Daniel Han 659b9dad3f
Security audit: pin openai, and re-review the four digest-pinned entries (#9148)
* Security audit: pin openai, and re-review the four digest-pinned entries

The extras shard of the security audit has been red on main since 2026-08-17
19:14Z with four non-baselined CRITICALs, all in `openai`:

    Harvests environment variables/secrets AND makes network calls
      openai/_client.py, openai/lib/azure.py, openai/lib/bedrock.py
    Accesses cloud metadata/IMDS AND makes network calls
      openai/auth/_workload.py

It is not a code change here. The last green run was 18:23Z and every run after
18:40Z failed on the same four findings, on main and on unrelated PR branches
alike, which is upstream drift rather than anything in the tree.

`openai>=2.7.2` is the only floating spec in extras.txt that carries digest-pinned
baseline entries. openai 3.2.0 published at 19:14Z and changed all four files.
Confirmed by digest: the four `file_sha256` values in the baseline match openai
3.0.0 and 3.1.0 exactly and none of 3.2.0's.

The pin is deliberate and correct (#8104, #8565): the evidence for these entries
records that a network call exists but not where it goes, so `client.post(...,
data=api_key)` appended to one of these files would leave the evidence hash
untouched. Only the file digest can reopen that, so any edit upstream makes must
red the gate until someone re-reads it.

Which is the review, done here. Every destination in the four files at 3.2.0 is
first-party or a documented cloud metadata endpoint: api.openai.com,
auth.openai.com, 169.254.169.254 (Azure IMDS), metadata.google.internal,
management.azure.com, and bedrock-mantle.{region}.api.aws. The one instance-level
POST is the workload-identity token exchange, which defaults to
https://auth.openai.com/oauth/token and sets follow_redirects=False. The env reads
are OPENAI_API_KEY, OPENAI_ADMIN_KEY, OPENAI_WEBHOOK_SECRET,
AZURE_OPENAI_API_KEY, AZURE_OPENAI_AD_TOKEN and AWS_BEARER_TOKEN_BEDROCK, each
used to authenticate to its own service. Same benign patterns as the reviewed
3.0.0, so the four entries are re-pinned to 3.2.0's digests and nothing else in
the 214-entry baseline is touched.

Re-baselining alone would only buy time until the next release, so the spec is
pinned too. Every other requirement in extras.txt is already an exact pin (#8408
pinned the bare ones and skipped this one because it had a specifier). Pinning
makes the bump deliberate: whoever raises the version is the one who re-runs
--write-baseline and re-reads the diff, instead of the gate going red on release
day for a change nobody here made.

The new test asserts that invariant directly, since it is the part that will be
got wrong again: a package with digest-pinned baseline entries must not float in
studio/backend/requirements. Reverting the spec to `openai>=2.7.2` fails it and
names extras.txt:62.

Verified:
  - `scan_packages.py 'openai==3.2.0'` exits 0; on 3.1.0 it exits 1, so the pin
    still bites and the entries were not widened.
  - The whole extras shard (`--with-deps -r extras.txt`, 128 archives) exits 0
    with 0 active CRITICAL/HIGH, against 4 CRITICAL and exit 1 before.
  - tests/security/test_scan_packages.py: 121 passed.

Unrelated, and not a defect: the `openai-whisper==20250625` line in the same log
is an INFO, not the failure. That version is on PyPI as an sdist only, and the
bulk resolve runs `--only-binary :all:` so it never executes a setup.py, which is
the scanner's whole security model. "from versions: none" is what pip says about
an sdist-only package under that flag, and the per-spec fallback plus the direct
sdist fetch that follow are the designed path working. The scipy
`assert_raises(ValueError, CubicSpline, ...)` evidence quoted alongside it is a
MEDIUM and cannot fail the gate; it is a false positive on `bc1`, a boundary
condition variable in scipy's own test file that looks like a bech32 address.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Keep the openai pin off Python 3.9, which the first version of it broke

`openai==3.2.0` on its own was a regression, caught simulating the install rather
than reading it. openai 3.x is `requires-python >=3.10` and this project is
`>=3.9,<3.15`, so an exact pin resolves to nothing at all on 3.9, where `>=2.7.2`
had quietly been selecting 2.48.0. Fixing a CI gate by breaking an install is not
a fix.

Split on the marker instead, which is what the rest of this file already does for
soundfile and tabulate. 2.48.0 is the newest release that accepts 3.9, so the 3.9
branch pins exactly what 3.9 was resolving to before.

Resolved with uv against every interpreter in requires-python, --no-deps, three
ways (main, the bad pin, this):

  spec           3.9        3.10     3.12     3.13     3.14
  >=2.7.2        2.48.0     3.2.0    3.2.0    3.2.0    3.2.0
  ==3.2.0        NO SOLUTION  3.2.0  3.2.0    3.2.0    3.2.0
  split          2.48.0     3.2.0    3.2.0    3.2.0    3.2.0

So this is behaviour-preserving: identical to main on every supported Python, and
the whole of extras.txt resolves to the same 31 packages on 3.10, 3.12, 3.13 and
3.14 before and after, differing only in the `# via -r` filename. 3.9 fails on
both sides for an unrelated and pre-existing reason (scikit-learn==1.7.1 is itself
>=3.10), so nothing here changes that either.

The security audit is unaffected: it runs on 3.12, markers are evaluated by pip,
and the scan of the two-line spec fetches only 3.2.0 and exits 0. The 3.9 branch
is never scanned, exactly as the floating spec's 3.9 resolution never was.

The guard is widened to the second half of the same invariant: the exact pins for
a digest-pinned package must cover every supported Python between them, so a
marker partition with a hole in it fails. Its limit is now written down rather
than implied. It is marker-only and offline, so it catches a `>= "3.11"` beside a
`< "3.10"`, but it cannot catch a single unmarked pin whose release does not
support 3.9, because that means asking PyPI for the release's requires-python.
The resolution simulation above is what covers that case, and the test module
stays network-free.

Verified: reverting to `openai>=2.7.2` still fails the guard naming extras.txt.
tests/security/test_scan_packages.py: 121 passed.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Widen the pin guard to the pyproject inputs, and make `==` mean one version

Two review findings, both real, both reproduced before fixing.

The guard read `studio/backend/requirements/*.txt` and stopped there, but
security-audit.yml builds `audit-reqs/unsloth-deps.txt` out of pyproject's
`project.dependencies` plus the `huggingfacenotorch` extra and feeds that to the
hf-stack shard. A digest-pinned package declared there was invisible to the guard,
which is the whole failure mode it exists to catch, one file over. It now reads
both halves, from one helper that mirrors the workflow's own input transform.

Reading pyproject turns up the counterexample the finding named: unsloth_zoo is
digest-pinned (hf_xet_health.py, the credential send that motivated #8104) and
declared `unsloth_zoo>=2026.8.12`. That one is deliberately not version-pinned and
must not be. The recurrence being prevented is an upstream release WE DO NOT
CONTROL changing the bytes and reddening main on a day nobody touched the repo;
unsloth_zoo is ours, released in lockstep, and an exact pin would break that. When
its digest reopens, the change is one of ours and re-reviewing it is the point of
the pin. So it is named in an exemption rather than quietly skipped, and the
exemption is itself asserted: it must contain only names that really are digest
pinned, and at least one third-party package must remain, so the guard cannot be
defused by growing the list. That assertion earned its keep immediately, failing
on `unsloth` until it was removed for having no pinned entry.

Second, `==` has to mean one version. The old prefix regex accepted `openai==3.*`,
which pip resolves as a prefix match to whatever 3.x is newest, recreating the
exact release-day failure. The specifier is now parsed with
packaging.requirements rather than pattern-matched: exactly one clause, operator
`==`, version not ending in `.*`. Parsing also drops the hand-rolled name and
marker splitting, so extras and whitespace stop being edge cases.

Four negative controls, each red with its own message and green after:
  - `openai>=2.7.2` in extras.txt: not pinned to one version, names extras.txt:68.
  - `openai==3.*`: same assertion, names the wildcard spec.
  - `< "3.9"` instead of `< "3.10"`: openai uncovered on 3.9.
  - `openai>=2.7.2` added to pyproject's dependencies: names pyproject.toml, which
    is the finding above and was green before this commit.

tests/security/test_scan_packages.py: 121 passed.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Read pyproject through tomli when tomllib is not there

pyproject sets requires-python >=3.9 and testpaths ["tests/security"], so a
bare pytest from the repo root collects this module on 3.9 and 3.10, where
tomllib does not exist (it landed in 3.11, PEP 680). The two helpers added
here imported it unguarded, so on those interpreters the file raised
ModuleNotFoundError instead of reporting a result. tomli is already pinned for
python_version < 3.11 in extras-no-deps.txt, and four other suites in this repo
already use exactly this fallback.

The regression test supplies the backport rather than requiring it. The
tests-security job installs only pytest and PyYAML, so a test that leaned on a
real tomli being importable would importorskip its way to green there and never
execute the branch at all. Registering the parser this interpreter does have
under the name the fallback looks for keeps it load-bearing everywhere, while
import tomllib is made to fail for the duration so the fallback is provably
what gets consulted. Checked red on the unfixed helpers and green on the fixed
ones, including with tomli hidden.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-08-18 02:23:46 -07:00
..
data Make the Colab oracle tripwire able to fire, and stop blaming start.py for a hung agent CLI (#7838) 2026-08-04 07:42:42 -07:00
build_prequant_checkpoint.py Studio: run MiniMax-H3's INT8 denoiser from the ConvRot checkpoint (#8293) 2026-08-10 01:00:16 -07:00
build_te_prequant_checkpoint.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
build_whisper_cpp.sh Studio: add local speech-to-text dictation engine (#7095) 2026-07-23 01:39:03 -07:00
check_frontend_dep_removal.py Reduce and tighten code comments and docstrings repo-wide (#6095) 2026-06-08 23:09:51 -07:00
check_new_install_scripts.py Reduce and tighten code comments and docstrings repo-wide (#6095) 2026-06-08 23:09:51 -07:00
compare_engines.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
compile_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
diffusion_bench.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
diffusion_quality.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
enforce_kwargs_spacing.py Reduce and tighten code comments and docstrings repo-wide (#6095) 2026-06-08 23:09:51 -07:00
fbcache_flux_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
fp8_overflow_check.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
image_speedmem_bench.py Report the precision actually in use, and refuse an explicit one we cannot honor (#8165) 2026-08-09 01:15:13 -07:00
install_gemma4_mlx.sh Update Install Scripts (#5968) 2026-06-03 05:39:42 -07:00
install_qwen3_6_mlx.sh Studio: drop the mlx-lm 0.31.3 exclusion so current mlx-vlm resolves (#7061) 2026-08-07 02:41:04 -03:00
install_rocm_wsl_strixhalo.sh Pin the ROCm-on-WSL bootstrap to immutable refs (#8540) 2026-08-12 06:37:05 -07:00
int8_linear_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
leverage_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
lint_backend_python_floor.py One interpreter leg on a pull request, and a floor lint that reads more than syntax (#9100) 2026-08-17 18:48:26 -07:00
lint_no_parallel_clamp.py Studio: stop MTP forcing llama-server to a single parallel slot (#8172) 2026-08-08 21:09:58 -07:00
lint_workflow_triggers.py Harden the workflow-trigger lint: scan .yaml, and host it outside the workflow it audits (#8545) 2026-08-12 09:55:26 -07:00
lockfile_supply_chain_audit.py security: lockfile audit must block non-registry sources and missing integrity by default (#8541) 2026-08-12 05:36:17 -07:00
make_dmg_background.py Start the bottom taper under the icon so the disc still reads round (#8321) 2026-08-10 01:06:59 -07:00
notebook_to_python.py Reduce and tighten code comments and docstrings repo-wide (#6095) 2026-06-08 23:09:51 -07:00
notebook_validator.py Make the Colab oracle tripwire able to fire, and stop blaming start.py for a hung agent CLI (#7838) 2026-08-04 07:42:42 -07:00
nvfp4_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
nvfp4_t211_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
online_tokenization_ab.py Studio: tokenize the dataset online for plain-text single-pass runs (#8960) 2026-08-16 04:55:53 -07:00
perf_levers_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
perf_verify.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
prequant_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
profile_startup.py Measure where Studio's startup time actually goes (#7553) 2026-07-28 22:24:34 -07:00
quant_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
run_ruff_format.py Reduce and tighten code comments and docstrings repo-wide (#6095) 2026-06-08 23:09:51 -07:00
scan_npm_packages.py Replace standalone Studio wording with Unsloth (#7221) 2026-07-19 00:47:04 -07:00
scan_npm_packages_baseline.json scan_packages: key baseline on matched-code hash so payloads in baselined files are not auto-suppressed (#6552) 2026-07-01 04:03:59 -07:00
scan_packages.py Scan package archives across cores instead of one at a time (#9024) 2026-08-16 21:46:14 -07:00
scan_packages_baseline.json Security audit: pin openai, and re-review the four digest-pinned entries (#9148) 2026-08-18 02:23:46 -07:00
sd_cpp_smoke.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
sdpa_mask_backend_probe.py Diffusion: replace the attention perf claims with re-measured numbers (#8021) 2026-08-06 05:47:31 -07:00
sparse_accum_probe.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
stamp_studio_release.py Replace standalone Studio wording with Unsloth (#7221) 2026-07-19 00:47:04 -07:00
sync_allow_scripts_pins.py Studio: auto-sync allowScripts pins after dependency bumps (#6136) 2026-06-10 02:35:37 -07:00
uninstall.ps1 Windows: stop depending on the generated unsloth.exe console script (#8592) 2026-08-13 07:54:51 -07:00
uninstall.sh Studio: install sd.cpp under the Studio home, not beside it (#8226) 2026-08-10 07:03:20 -07:00
verify_comment_only_diff.py Reduce and tighten code comments and docstrings repo-wide (#6095) 2026-06-08 23:09:51 -07:00
verify_import_hoist.py feat(studio): rework train page setup flow (#7633) 2026-08-06 04:26:05 -07:00
verify_prequant_backend.py Studio: add image generation, editing workflows and LoRA training with Unsloth GGUFs (#6763) 2026-08-04 08:11:01 -07:00
video_quality.py Video: make MiniMax-H3's Diffusers path fast by default (#8320) 2026-08-10 04:31:09 -07:00
virustotal_scan.py Reduce antivirus false positives in the desktop installers (#8586) 2026-08-13 07:02:18 -07:00