feat(A.1 #2): build-time CSP regression guard for localhost LLM

Pins the connect-src CSP entries for http://127.0.0.1:* and
ws://127.0.0.1:* at build time. If a future edit to tauri.conf.json
strips the local-LLM permit, the kon crate fails to build rather than
shipping a binary whose webview fetch() silently 404s with an opaque
scope error (Vibe #438 / #487).

Closes item #2 of docs/whisper-ecosystem/brief.md — CSP widening itself
landed in an earlier commit; this is the regression-proofing the plan
calls for.
This commit is contained in:
2026-04-22 00:26:15 +01:00
parent 5c17544a63
commit f486ff4cbc

View File

@@ -9,5 +9,35 @@ fn main() {
println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition"); println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition");
} }
assert_localhost_llm_csp();
tauri_build::build() tauri_build::build()
} }
/// Regression guard for brief item #2 (pre-emptive localhost LLM scope).
///
/// Kon's bundled llama.cpp server and any BYO Ollama install speak HTTP
/// on `127.0.0.1:*`. If the `connect-src` CSP ever drops those entries,
/// `fetch()` from the webview to the local LLM silently 404s with an
/// opaque scope error (Vibe #438 / #487). We keep the current permit
/// pinned at build time so a stray edit can't regress it unnoticed.
fn assert_localhost_llm_csp() {
let conf_path = std::path::Path::new("tauri.conf.json");
println!("cargo:rerun-if-changed=tauri.conf.json");
let raw = std::fs::read_to_string(conf_path)
.expect("build.rs: failed to read tauri.conf.json for CSP regression guard");
let csp_start = raw
.find("\"csp\"")
.expect("build.rs: tauri.conf.json missing \"csp\" field");
// Read the rest of the CSP string — we only need to substring-search.
let csp_slice = &raw[csp_start..];
for required in ["http://127.0.0.1:*", "ws://127.0.0.1:*"] {
assert!(
csp_slice.contains(required),
"build.rs: tauri.conf.json CSP must permit {required} for local LLM \
connectivity (brief item #2). Restore the connect-src entry before \
building."
);
}
}