Repository navigation
fix(vmafx-tune-go): deep bug audit — 5 fixes (JSON NaN, subprocess hang, codec cache) - #505
Merged
Merged
Conversation
lusoris
marked this pull request as ready for review
May 31, 2026 21:57
…ng, codec cache)
Deep audit of cmd/vmafx-tune/ (Stage-1 Go CLI per ADR-0705 / ADR-0713) and its
pkg/{report,bisect,encoder} dependencies, closing five distinct defects in one
PR — every fix ships with a regression test.
1. JSON NaN propagation in bisect_samples
report.EmitJSON and cmd/vmafx-tune/cmd.emitSweepJSON sanitised only the
top-level row floats, leaving []bisect.Sample declared as raw float64 in the
wire shape. One non-finite sample (e.g. from a corrupt vmaf XML mean)
crashed json.MarshalIndent with "unsupported value: NaN" and broke AGENTS.md
rebase-sensitive invariant #2 (Python ↔ Go parser parity). New public
report.SanitizeBisectSamples walks the nested floats; mirrored in
emitLadderJSON for Cloud + Hull + Renditions across BitratekBps, VMAF,
TargetVMAF.
2. parseVMAFXMLMean accepted "NaN" / "+Inf" / "-Inf"
Go strconv.ParseFloat returns those tokens without error, so a corrupt vmaf
XML mean fed non-finite scores into bisect.Sample. Parser now rejects
non-finite means at the source so the bisect step records a score failure
rather than propagating a corrupt value.
3,4,5. Subprocess hang risk (ffmpeg, vmaf, ffprobe)
Every exec.Command in pkg/encoder (ffmpeg encode, ffprobe bitrate probe,
codec discovery) and pkg/bisect (vmaf scoring) ran with no context and no
timeout. A hung child pinned the sweep forever. Switched to
exec.CommandContext with per-stage upper bounds overridable via
VMAFX_TUNE_ENCODE_TIMEOUT (default 60m), VMAFX_TUNE_SCORE_TIMEOUT
(default 30m), VMAFX_TUNE_PROBE_TIMEOUT (default 30s).
6. Codec-discovery cache stale-key
The prior sync.Once gate locked in whichever ffmpeg binary path was probed
first, with "_ = ffmpegBin" masquerading as cache invalidation. Cache key
is now the binary path; calling with a different path triggers a re-probe.
RefreshCodecCache updates the cache key alongside the map.
Tests
- 8 new Go regression tests across pkg/{report,bisect,encoder} and
cmd/vmafx-tune/cmd/ — every fix has a focused test that fails without
the change.
- Existing pkg/encoder/discover_test.go updated to the new per-binary cache
shape (sync.Once references removed).
- go test -race -count=1 ./cmd/vmafx-tune/... ./pkg/{bisect,encoder,ladder,
report}/... — all pass.
- go vet ./... clean.
- Python compare-parser tests (88 across tools/vmaf-tune/tests/test_{bisect,
compare,compare_rate_quality_sweep,compare_no_bisect}.py) still pass —
verifies the JSON schema-v1/v2 parser parity invariant.
Docs
- changelog.d/fixed/0979-vmafx-tune-go-deep-bug-audit.md
- docs/rebase-notes.md (fork-local; no Netflix upstream counterpart)
- docs/state.md (T-VMAFX-TUNE-GO-DEEP-BUG-AUDIT-2026-05-31 closed)
Bug fixes; no ADR per CLAUDE §12 r8.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
lusoris
force-pushed
the
fix/vmafx-tune-go-audit-20260531
branch
from
May 31, 2026 21:57
9df521d to
029ec5f
Compare
This was referenced May 31, 2026
lusoris
added a commit
that referenced
this pull request
May 31, 2026
…510) Re-run of the gosec static-security scan against the post-#505 / post-#509-close master tip. PR #509 had source conflicts with PR #505 on pkg/bisect/bisect.go and pkg/encoder/encoder.go; this v2 sweep applies the same security-hardening fixes while preserving the ctx + per-stage timeout that #505 added. Findings: 38 raw, 10 in generated/cgo trampoline code (excluded via -exclude-generated), 26 fork-original. All 26 source findings now discharged. Post-fix `gosec -exclude-generated ./...` returns 0 issues. Real bug fixed: - G304 in cmd/vmafx-mcp/impl.go::describeModel — joined caller-supplied `name` onto repo root and os.Stat-ed the result, allowing {"name": "../../../etc/passwd"} to escape libvmaf.AllowedRoots(). Routed through libvmaf.ValidatePath; regression test at cmd/vmafx-mcp/impl_gosec_test.go::TestDescribeModelRejectsTraversal. Hardening: - G306/G301 in cmd/vmafx-tune/cmd/compare.go::writeOutput — tightened to 0o600 (file) / 0o750 (directory). - G104 in cmd/vmafx-mcp/impl.go::runVmafScore — outFile.Close() and os.Remove(outPath) return values now checked. - G204/G304 ×22 — `//nolint:gosec` comments rewritten to `// #nosec G<rule> -- <citation>` (gosec ignores nolint:gosec). - G202 in cmd/vmafx-controller/queue/queue.go — verified safe (only repeatCommaQ output concatenated; status values bind via placeholders). CI gate: gosec step in .github/workflows/go-ci.yml + make lint-go target. Differs from PR #509: - pkg/bisect/bisect.go: keeps PR #505's exec.CommandContext + scoreTimeout() — just adds the #nosec G204 citation. - pkg/encoder/encoder.go: keeps PR #505's exec.CommandContext + encodeTimeout()/probeTimeout() — same. Verification: - gosec -exclude-generated -quiet ./... → 0 findings (was 26) - go vet ./... clean - go build ./... clean - go test -race ./cmd/vmafx-mcp/ -run TestDescribeModel → 5/5 pass - go test -race ./... (skip vmafx-operator envtest) → all packages OK except TestHandleVmafScore_RoutesToDirect + TestHandleDescribeModel_DirectAugments in cmd/vmafx-mcp/ which require libvmaf.so and fail identically on master (verified via stash + re-run). - bash scripts/ci/check-adr-numbering.sh → PASS - bash scripts/ci/check-copyright.sh → PASS - pre-commit run --files → PASS Refs ADR-0983. Co-authored-by: Lusoris <lusoris@pm.me> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Deep audit of
cmd/vmafx-tune/(Stage-1 Go CLI per ADR-0705 / ADR-0713) and itspkg/{report,bisect,encoder,ladder}dependencies. Five distinct bugs bundledinto one PR, every fix shipping with a focused regression test.
Bug 1 — JSON NaN propagation in
bisect_samples.report.EmitJSONandcmd/vmafx-tune/cmd.emitSweepJSONsanitised only the top-level row floats,leaving
[]bisect.Sampledeclared as rawfloat64in the wire shape. Asingle non-finite VMAF / bitrate / encode-time sample (e.g. propagated from
a corrupt vmaf XML mean) crashed
json.MarshalIndentwith"unsupported value: NaN"and broke the Python ↔ Go parser-parity invariant (AGENTS.mdrebase-sensitive invariant chore(docs): update mkdocs site_url to vmafx.github.io/vmafx #2). New public helper
report.SanitizeBisectSampleswalks the nested floats; mirrored coverageadded in
emitLadderJSONforCloud+Hull+RenditionsacrossBitratekBps,VMAF,TargetVMAF.Bug 2 —
parseVMAFXMLMeanaccepted the literal tokens"NaN"/"+Inf"/
"-Inf". Gostrconv.ParseFloatreturns those silently. The parser nowrejects non-finite means at the source so the bisect step records a score
failure rather than seeding bug 1 failure mode.
Bugs 3 / 4 / 5 — Subprocess hang risk (ffmpeg, vmaf, ffprobe / codec
discovery). Every
exec.Commandinpkg/encoderandpkg/bisectran withno context and no timeout, so a hung child pinned the sweep forever. All
switched to
exec.CommandContextwith per-stage upper bounds overridablevia
VMAFX_TUNE_ENCODE_TIMEOUT(default60m),VMAFX_TUNE_SCORE_TIMEOUT(default
30m),VMAFX_TUNE_PROBE_TIMEOUT(default30s).Bug 6 — Codec-discovery cache stale-key. The prior
sync.Oncegatelocked in whichever ffmpeg binary path was probed first, with
_ = ffmpegBinmasquerading as cache invalidation. Cache key is now thebinary path; calling with a different path triggers a re-probe and replaces
the cache.
Plus a
pkg/encoder/discover_test.goupdate to the new per-binary cacheshape (removed the
sync.Oncereferences) and minorgofmtalignment ofthe timeout helpers.
No ADR — bug fixes per CLAUDE §12 r8.
Test plan
go test -race -count=1 -timeout=180s ./cmd/vmafx-tune/... ./pkg/bisect/... ./pkg/encoder/... ./pkg/ladder/... ./pkg/report/...— all packages pass.go vet ./...clean.gofmt -l ...clean.pre-commit run --files <all touched files>clean.pytest tools/vmaf-tune/tests/test_bisect.py tools/vmaf-tune/tests/test_compare.py tools/vmaf-tune/tests/test_compare_rate_quality_sweep.py tools/vmaf-tune/tests/test_compare_no_bisect.py— 88/88 pass.TestEmitJSON_NaNInBisectSamplesDoesNotCrash(pkg/report/sanitize_test.go); pre-fix it would crash with"json: unsupported value: NaN", post-fix it round-trips cleanly throughjson.Unmarshal.TestProbeAvailableCodecs_CacheRespectsBinaryPath(pkg/encoder/discover_cache_test.go); pre-fix the second call returns the stale first-binary result, post-fix it re-probes.Per-surface notes (CLAUDE §12 r10)
cmd/vmafx-tune/cmd/{compare,ladder}.go— public-CLI emitter behaviour(NaN sanitisation) hardened; no user-visible CLI flag change, so the
doc-substance rule has nothing to update.
pkg/report.SanitizeBisectSamples— new exported helper; internal to theGo binary (not a libvmaf C-API surface), no
docs/api/update needed.VMAFX_TUNE_{ENCODE,SCORE,PROBE}_TIMEOUT— surfaced inthe changelog fragment and rebase-notes for operator discoverability.
Deep-dive deliverables (CLAUDE §12 r11)
(NaN→null, ctx with timeout, cache-key on binary path).
AGENTS.mdinvariant note: rebase invariants incmd/vmafx-tune/AGENTS.mdalready cover NaN coercion (chore(docs): update mkdocs site_url to vmafx.github.io/vmafx #2) and thebisect midpoint (chore(deps): Pin dependencies #3); no edits needed.
changelog.d/fixed/0979-vmafx-tune-go-deep-bug-audit.md.docs/rebase-notes.mdentry added.Scope guardrails
go test -race,go vet,gofmt,pre-commit runall clean.origin/master(1 ahead, 0 behind at push time).docs/state.mdrow added per CLAUDE §12 r13.🤖 Generated with Claude Code