Skip to the content.

SwanHubX/SwanLab - security scan

Repository: SwanHubX/SwanLab Commit scanned: 2ed6af76e57da90a2a1ad417d69c63efd361224d Scan date: 2026-06-28 Disclosure status: public — post-only (strict-norm: real SECURITY.md + commercial backing; no upstream issue filed)

Summary

Severity Count
Critical 0
High 19
Medium 13
Low 0 (filtered at --min-severity medium)
Info 0

Total findings: 32 (0 code-level; all are a dependency tail + 2 test-fixture FPs)

SwanLab is an open-source, self-hostable AI training tracker and visualization tool — a W&B-style experiment dashboard with a Python SDK, image/3D/audio media logging, webhooks, and a private-deployment edition. It’s a 4k-star, Apache, commercially-backed project (swanlab.cn cloud) with a real security policy.

This is the 10th clean scan in the series, and a slightly unusual one: the interesting result is the absence of code-level findings. Semgrep scanned 361 files under swanlab/ and returned zero results — no SQL injection, no shell=True, no eval, no SSRF pattern, nothing — on a web platform with auth, file upload, and webhooks. The entire finding list is a dependency refresh (no Dependabot is wired) plus two test-fixture secret false positives.

Top findings

1. Code is clean — semgrep 0/361, gitleaks 2/2 false positives

2. The one reachable dependency item: pillow image-parsing CVEs

3. The rest of the tail is transitive version-match

Patterns observed

Clean code, drifting lockfile, no Dependabot. SwanLab inverts the usual shape: most scans in this series have a clean-ish dependency story and a few code-level patterns to triage. Here it’s the opposite — the code is spotless and the only signal is that uv.lock carries 30 advisories with no .github/dependabot.yml to keep it current. That’s a process gap, not a vulnerability, and on a commercially-backed project with a security team it’s the kind of thing they very likely already track on the cloud side.

Reachability is what separates the one real item from the noise. The starlette SSRF and ujson parser CVEs sound server-reachable on a dashboard platform, but a 30-second import check shows neither framework is directly used in this repo — they’re transitive. The pillow CVEs sound like generic image-library noise, but SwanLab’s whole media-logging feature funnels user-supplied images through Pillow, so that’s the row that actually matters. Same lockfile, opposite verdicts, decided by “is the affected surface reached from this code.”

Strict-norm handling. SwanLab ships a real SECURITY.md with a dedicated maintainer-alias triage address, and is backed by a commercial cloud. That puts it firmly in post-only territory: no grouped public “review” issue. The dep tail is entirely public CVEs with mechanical fixes (bump + wire Dependabot), so there was nothing here that warranted a private security report either — the honest, proportionate action is this write-up.

Notes on the tool

Disclosure timeline

This is a post-only clean-scan write-up. The code scanned clean, the two secret hits are test fixtures, and the dependency tail is public CVEs with mechanical fixes — mostly transitive, one (pillow) feature-reachable. On a strict-norm, commercially-backed project with its own security channel, the proportionate action is to document rather than file.

Reproduce

git clone https://github.com/SwanHubX/SwanLab /tmp/scan-target
python scanner/run_scan.py --repo /tmp/scan-target \
  --reports-dir ./reports/swanhubx-swanlab \
  --min-severity medium --ignore-samples