Skip to the content.

gpustack/gpustack — security scan

Repository: gpustack/gpustack Commit scanned: 6bc353b253eb Scan date: 2026-07-24 Disclosure status: public (post-only — clean scan)

Summary

Severity Count
Critical 1
High 74
Medium 61
Low 0
Info 0

Total findings: 136 (0 real after curation) — 22nd clean scan

GPUStack is a GPU cluster manager for high-performance AI model serving (5.4k★, Apache-2.0, 97% Python) — it registers worker nodes, proxies OpenAI- compatible inference to vLLM/SGLang backends, exposes API-key auth, and offers on-demand SSH-accessible GPU instances. For a serving control-plane the three questions that decide everything are (1) is the model-proxy an open SSRF pivot?, (2) is the worker↔server tunnel authenticated?, and (3) is credential / API-key handling sound? — and GPUStack answers all three well. The 136 findings collapse to zero real ones: a 46-strong SQL cluster that is entirely Alembic migration DDL, a ws:///file-perms/secret trio that are all rule artifacts over correct code, and a dependency tail that is reachability-gated.

Top findings (all resolved to non-issues)

1. The one Critical — asyncmy SQL-injection CVE — MySQL-backend-gated and ORM-abstracted

2. 46 SQLAlchemy “highs” — the #1 recurring parameterized-identifier FP

3. Model-proxy “tainted-url-host” — worker_auth-gated, host from cluster state

4. security.py “generic-api-key” — a docstring example over exemplary crypto

5. Four “insecure-websocket” + a “world-readable” file-perms flag — rule artifacts over correct code

Patterns observed

A serving control-plane where the count scales with schema churn, not risk. GPUStack’s 136 findings are dominated by two structural artifacts — 46 SQL flags that are all migration/enum DDL, and 29 GitHub-Actions mutable-ref flags (pin-your-actions-to-a-SHA hardening) — neither of which touches an attacker-reachable surface. Strip those and the “attack surface” of a GPU cluster manager turns out to be narrow and well-tended: one authenticated model-proxy, one authenticated worker tunnel, and one API-key module built on argon2/blake2b/secrets. The team’s security instincts show in the details that rules can’t credit — the 0o700 randomized unix socket for the operator gateway, the scheme-preserving websocket upgrade, the worker_auth dependency stapled to the proxy router.

The dependency tail is the honest residual, and it’s reachability-gated. Beyond the MySQL-gated asyncmy Critical, trivy surfaces two pyasn1 DoS CVEs (transitive, only reachable when parsing attacker-supplied ASN.1) and a build-time setuptools sdist-normalization issue (not a runtime path). There’s no dependabot.yml or renovate.json in the repo, but with daily merges from multiple maintainers this is a project that clearly tracks its stack by hand — so this stays a note, not a manufactured “wire up Dependabot” issue (Kiln lesson: don’t invent process work for a sophisticated maintainer). Refreshing asyncmy, pyasn1, and setuptools is worthwhile supply-chain hygiene; none of it is exploitable in the shipped default (SQLite) posture.

Every secret and every XSS flag dissolved on contact. All 17 gitleaks hits are non-secrets: 15 in tests/fixtures/ model-estimate blobs and test_model_routes.py, one curl header in an integration doc, one docstring example. The 5 direct-use-of-jinja2 “XSS” findings are server-side code and config generationcodegen/generate.py (Python codegen), cloud_providers/ user_data.py (cloud-init), k8s/manifest_template.py (K8s manifests) — none of which renders into a browser. Even the trivy “ConfigMap with secrets” flag on the bundled Higress gateway config resolves to a single password: "" (empty). The one-line takeaway: a GPU serving control-plane can carry a big raw finding count and still be clean, because the count tracks migration history and CI lint, not reachable risk.

Notes on the tool

Disclosure timeline

Reproduce

git clone https://github.com/gpustack/gpustack /tmp/scan-target
python scanner/run_scan.py --repo /tmp/scan-target --reports-dir ./reports/gpustack-gpustack --min-severity medium