Skip to content

Closes #177#188

Merged
realproject7 merged 1 commit into
mainfrom
task/177-gate-model-base-url
Jul 14, 2026
Merged

Closes #177#188
realproject7 merged 1 commit into
mainfrom
task/177-gate-model-base-url

Conversation

@realproject7

Copy link
Copy Markdown
Owner

Closes #177

Debug-gate LIVECAP_MODEL_BASE_URL so a release binary always uses the official model repo — it was documented as a dev-only knob but honored in release — and extend the release-invariants CI job (created by #176) to assert its absence permanently.

EPIC Alignment

Part of EPIC #134 (v1.3 audit follow-up), Batch 45 (standing-order … #176#177). #176 created the release-invariants job; this ticket EXTENDS it (the create-then-extend boundary RE2 called out — #176 merged first). #161 sibling. Scoped to crates/livecap-core/src/model.rs + the release-invariants job in .github/workflows/ci.yml. SHA-pinning (#176) is untouched; does not touch resample.rs/render.ts/parse.ts/pipeline.rs (reserved for #178/#179), dashboard, or settings.

Self-Verification

The bug. LIVECAP_MODEL_BASE_URL is documented (model.rs:27-30) as a #110 dev-run knob (redirect the model-BLOB download base to exercise the download-failure fallback), but the env read at blob_base_url() was not #[cfg(debug_assertions)]-gated — a release binary honored it. Same class as #161 (a dev-only env escape compiled into release, contradicting the #146 "no dev env path in a shipped binary" claim). Bounded impact: pointer_url is pinned to the official HF_REPO, so the expected SHA-256 source can't move with the download source — a hostile base URL can't serve a tampered model (hygiene, not integrity) — but a release build should carry no such knob (and the redirect would leak which model the user runs to an arbitrary host).

The fix (mirror the #161 / dev_flags.rs pattern).

Security invariants. No new dependencies. No caption/audio content logged (only an env-var name is asserted absent via strings; the workflow run: block uses static values). Simplest fix — the #161 cfg pattern, no refactor of the download logic. Only model.rs + the release-invariants job touched.

Tests / evidence (livecap-core builds on Linux; the actual livecap-app release build + strings run in the release-invariants CI job on this PR):

  • Release strings — ABSENT (AC): cargo build --release --example live_caption -p livecap-core (links livecap-core, mirroring how livecap-app links it) → strings … | grep LIVECAP_MODEL_BASE_URL empty.
  • Debug fallback — PRESENT / live (AC): the debug build's strings contain LIVECAP_MODEL_BASE_URL (the Settings: whisper model selection (small / medium / large-v3-turbo) with download-on-switch #110 knob is compiled in and read).
  • Seeded violation: un-gating the release path to read the env → the symbol reappears in the release binary → the extended gate would FAIL ✅ (reverted).
  • Release compiles (AC): cargo test --release -p livecap-core --lib --no-run clean; cargo clippy --release -- -D warnings clean (the allow(dead_code) handles the unused const); debug clippy -D warnings clean.
  • base_url_or_default unit tests (override defaults to official repo; override moves only the blob URL, never the digest source) 5/5 pass; ci.yml valid YAML with the FORBIDDEN list extended.

Design Fidelity

N/A — no UI/webview/CSS surface changed (a Rust cfg gate + a CI strings assertion only).

Deviations

  • Kept MODEL_BASE_URL_ENV always-defined (with #[cfg_attr(not(debug_assertions), allow(dead_code))]) rather than #[cfg(debug_assertions)]-gating the const itself, so the intra-doc link from pointer_url's security note (see [MODEL_BASE_URL_ENV]) stays resolvable in both profiles. An unused const emits no string, so this does not weaken the release-absence guarantee (verified).

🤖 Generated with Claude Code

…o assert it

LIVECAP_MODEL_BASE_URL is documented as a #110 dev-run knob (exercise the
download-failure fallback) but the env read was NOT #[cfg(debug_assertions)]-
gated, so a release binary honored it — the same #161-class "dev-only env escape
compiled into release" the #146 doc claims we don't ship. (Bounded: pointer_url
stays pinned to the official HF_REPO, so the SHA-256 digest source can't move —
hygiene, not integrity — but a shipped binary should expose no dev env knobs.)

Gate it like dev_flags.rs / the #64 bleed dump: debug `blob_base_url()` reads the
env; release `blob_base_url()` returns `base_url_or_default(None)` (the official
repo) with the env read compiled out entirely. The pure `base_url_or_default`
helper + its tests are unchanged; SHA-pinning untouched. The now-release-unused
`MODEL_BASE_URL_ENV` const carries `#[cfg_attr(not(debug_assertions),
allow(dead_code))]` — unused → no string emitted → the release binary carries no
`LIVECAP_MODEL_BASE_URL` symbol.

Extend the #176 release-invariants CI job's FORBIDDEN list with
LIVECAP_MODEL_BASE_URL so a dropped guard is caught permanently.

Proofs (local): debug example strings → present (knob live); release example
strings → ABSENT; a seeded un-gated release read → PRESENT (gate would FAIL);
`cargo test --release --no-run` + release clippy -D warnings clean; model tests
5/5. No new deps; no caption/audio content logged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Epic Alignment: PASS

#177 correctly extends the #176 release-invariants job and removes the release-model-host override while preserving debug fallback behavior.

Checked (evidence)

  • Contract: crates/livecap-core/src/model.rs:49-57 compiles the environment read only under debug_assertions; release selects the official base via base_url_or_default(None).
  • Boundary: model.rs:169 is the sole blob-download base use; pointer_url remains pinned to HF_REPO.
  • Invariant: ci.yml:102 adds LIVECAP_MODEL_BASE_URL to the release-binary forbidden-name assertion.
  • Riskiest part: retaining the const in release; it is unused and the live release-invariants job proves its symbol is absent from the real binary.
  • Kill-list: scanned full diff — clean.
  • CI: gh pr checks 188 → app-macos, color-guard, no-stub-gate, packages-linux, release-invariants all pass.

Findings

None.

Decision

The dev-only override is compiled out of release and permanently covered by the extended release-profile gate.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE at PR #188 head (all 5 CI checks green)

The #177 debug-gating of LIVECAP_MODEL_BASE_URL is correct, mirrors the #161 pattern, and the extended release-invariants job empirically proves the release binary carries no such symbol. Reviewed the whole diff (2 files); verified statically + ran the helper tests on Linux + confirmed the authoritative CI job.

Checked (evidence)

  • The env read is properly cfg-split. blob_base_url() is now two #[cfg] variants (model.rs:48-58): debug reads std::env::var(MODEL_BASE_URL_ENV), release returns base_url_or_default(None) (official HF_REPO) with the env read compiled out — exactly the [security] Gate LIVECAP_CAPTURE_VISIBLE (+dev env vars) behind cfg(debug_assertions) in release (N-4) #146/[security] Gate LIVECAP_BLEED_DUMP_DIR behind cfg(debug_assertions) — release binary can covertly dump raw audio #161/dev_flags.rs pattern.
  • The const string cannot leak into release. MODEL_BASE_URL_ENV = "LIVECAP_MODEL_BASE_URL" is referenced in code at exactly one site — model.rs:51, inside the #[cfg(debug_assertions)] blob_base_url (grep-confirmed; all other occurrences are doc/comment, compiled out). In release the const is unused, and an unused Rust const (inlined, no storage) emits no data — so the symbol is absent. #[cfg_attr(not(debug_assertions), allow(dead_code))] correctly silences only the release dead-code warning.
  • Empirically proven end-to-end. The extended release-invariants CI job passed (2m22s) — its FORBIDDEN list now includes LIVECAP_MODEL_BASE_URL, and it strings-asserts that name absent from the RELEASE livecap-app binary. Green = the string is genuinely stripped from the shipped binary (the AC's release-strings-absence proof, on the real target I can't build on Linux). app-macos (release compile) also green → release compiles clean.
  • Helper + tests unchanged; digest source still pinned. base_url_or_default is untouched; its suite passes (I ran cargo test -p livecap-core model::5 passed, incl. override_moves_only_the_blob_url_never_the_digest_source). pointer_url stays pinned to HF_REPO, so this is hygiene, not integrity — matching the ticket's severity (and my #879 assessment).
  • CI extension is minimal + correct. Only the FORBIDDEN string list + the explanatory comment changed in the job; the gate logic (SHA-pins, set -euo pipefail, strings|grep) is the same one I verified in Closes #176 #187.
  • Boundary/invariants: 2 files (model.rs + the release-invariants job in ci.yml); no new deps; SHA-pins untouched; no reserved files; no caption content logged. Design Fidelity N/A.
  • Live CI: all 5 green — app-macos (1m45s), release-invariants (2m22s), color-guard, no-stub-gate, packages-linux.

(Note: I could not run a local release strings-check — the scratchpad hit Disk quota exceeded on the release build, an environment limit, not a code issue; the debug helper tests pass and the release proof is the green CI job above.) RE2 APPROVE — unconditional (CI green). (Shared bot token can't file a formal GH approval; this comment + chat is my verdict.)

@realproject7
realproject7 merged commit 20464f9 into main Jul 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[audit][security-hygiene] Debug-gate LIVECAP_MODEL_BASE_URL (documented as a dev knob but honored in release) — #161 sibling

2 participants