Files
Lumotia/src-tauri/build.rs
Jake 6fd38932ce fix(A.1 #2): parse tauri.conf.json properly in CSP regression guard
Review feedback: the original guard substring-searched the whole file
after the first "csp" token, which (a) false-passes if any unrelated
JSON value elsewhere in the config happens to contain a localhost URL
and (b) false-fails if the CSP is ever re-serialised with escaped
forward slashes.

Switches to serde_json + a /app/security/csp pointer lookup, then
splits the CSP on ';', finds the connect-src directive, tokenises its
allow-list on whitespace, and requires an exact match for both
http://127.0.0.1:* and ws://127.0.0.1:*. The error now also includes
the current connect-src value so a developer who breaks it can see
exactly what needs restoring.
2026-04-22 00:35:33 +01:00

67 lines
2.8 KiB
Rust

fn main() {
// INTERIM: both llama-cpp-sys-2 and whisper-rs-sys statically link
// their own copy of ggml, so GNU ld / lld see duplicate symbols when
// linking the kon binary and its lib-tests. --allow-multiple-definition
// makes the linker pick the first definition; safe while both crates
// pin compatible ggml revisions. Replace with a system-ggml shared-lib
// setup as a follow-up.
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") {
println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition");
}
assert_localhost_llm_csp();
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.
///
/// Parses `tauri.conf.json` properly and inspects the `connect-src`
/// directive of the CSP, rather than substring-searching the whole
/// file — the latter would both false-pass on unrelated values
/// containing a localhost URL and false-fail on JSON-escaped forward
/// slashes.
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 conf: serde_json::Value = serde_json::from_str(&raw)
.expect("build.rs: tauri.conf.json is not valid JSON");
let csp = conf
.pointer("/app/security/csp")
.and_then(|v| v.as_str())
.expect("build.rs: tauri.conf.json missing app.security.csp string");
// Split the CSP into directives (`default-src 'self'; script-src ...`)
// and find connect-src. Everything after the directive name up to the
// next `;` is the allow-list.
let connect_src = csp
.split(';')
.map(str::trim)
.find_map(|directive| directive.strip_prefix("connect-src"))
.map(str::trim)
.expect(
"build.rs: tauri.conf.json CSP missing connect-src directive — \
local LLM fetches will be blocked (brief item #2)",
);
let tokens: Vec<&str> = connect_src.split_whitespace().collect();
for required in ["http://127.0.0.1:*", "ws://127.0.0.1:*"] {
assert!(
tokens.iter().any(|t| *t == required),
"build.rs: tauri.conf.json CSP connect-src must permit {required} \
for local LLM connectivity (brief item #2). Current connect-src: \
{connect_src:?}"
);
}
}