mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
fix(desktop): show OpenAI-compatible endpoint settings in the baked-env UI (#8117)
## Summary `is_safe_to_reveal` (`desktop/src-tauri/src/managed_agents/env_vars.rs`) is the single default-deny allowlist for showing baked env values. It lists `DATABRICKS_HOST`/`DATABRICKS_MODEL`/`DATABRICKS_MODEL_FILTER` but not their OpenAI-compatible counterparts, so a build that bakes `OPENAI_COMPAT_BASE_URL`, `OPENAI_COMPAT_MODEL`, `OPENAI_COMPAT_API` shows them as `••••••`. Adds those three non-secret settings to the allowlist: an endpoint URL, a model id, and an `auto|chat|responses` enum. **No secret is revealed: `OPENAI_COMPAT_API_KEY` is deliberately not added and remains masked.** ### Related issue None found. ### Testing - New test `baked_env_openai_compat_endpoint_is_unmasked_but_key_stays_masked` covers the three revealed settings and asserts the API key stays masked; `baked_env_allowlist_is_case_insensitive` extended. - `cargo test --lib baked_env` in `desktop/src-tauri`: 11 passed. `cargo fmt` clean. - Not exercised in the running desktop UI. Signed-off-by: Tom Bedor <tbedor@squareup.com> Co-authored-by: Tom Bedor <tbedor@squareup.com>
This commit is contained in:
@@ -714,6 +714,31 @@ fn baked_env_thinking_summary_is_unmasked() {
|
|||||||
assert!(!summary.masked);
|
assert!(!summary.masked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn baked_env_openai_compat_endpoint_is_unmasked_but_key_stays_masked() {
|
||||||
|
// The OpenAI-compatible endpoint, model id, and API flavor are
|
||||||
|
// non-secret defaults; the API key next to them is a credential.
|
||||||
|
let entries = baked_env_from_map(&[
|
||||||
|
("OPENAI_COMPAT_BASE_URL", "https://llm.example/v1"),
|
||||||
|
("OPENAI_COMPAT_MODEL", "balanced"),
|
||||||
|
("OPENAI_COMPAT_API", "chat"),
|
||||||
|
("OPENAI_COMPAT_API_KEY", "placeholder"),
|
||||||
|
]);
|
||||||
|
assert_eq!(entries.len(), 4);
|
||||||
|
let find = |k: &str| entries.iter().find(|e| e.key == k).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
find("OPENAI_COMPAT_BASE_URL").value,
|
||||||
|
"https://llm.example/v1"
|
||||||
|
);
|
||||||
|
assert!(!find("OPENAI_COMPAT_BASE_URL").masked);
|
||||||
|
assert_eq!(find("OPENAI_COMPAT_MODEL").value, "balanced");
|
||||||
|
assert!(!find("OPENAI_COMPAT_MODEL").masked);
|
||||||
|
assert_eq!(find("OPENAI_COMPAT_API").value, "chat");
|
||||||
|
assert!(!find("OPENAI_COMPAT_API").masked);
|
||||||
|
assert!(find("OPENAI_COMPAT_API_KEY").masked);
|
||||||
|
assert_eq!(find("OPENAI_COMPAT_API_KEY").value, "••••••");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn baked_env_allowlist_is_case_insensitive() {
|
fn baked_env_allowlist_is_case_insensitive() {
|
||||||
// Known-safe keys — case-insensitive match must allow them.
|
// Known-safe keys — case-insensitive match must allow them.
|
||||||
@@ -729,6 +754,11 @@ fn baked_env_allowlist_is_case_insensitive() {
|
|||||||
assert!(super::is_safe_to_reveal("DATABRICKS_HOST"));
|
assert!(super::is_safe_to_reveal("DATABRICKS_HOST"));
|
||||||
assert!(super::is_safe_to_reveal("databricks_model"));
|
assert!(super::is_safe_to_reveal("databricks_model"));
|
||||||
assert!(super::is_safe_to_reveal("DATABRICKS_MODEL"));
|
assert!(super::is_safe_to_reveal("DATABRICKS_MODEL"));
|
||||||
|
assert!(super::is_safe_to_reveal("openai_compat_base_url"));
|
||||||
|
assert!(super::is_safe_to_reveal("OPENAI_COMPAT_BASE_URL"));
|
||||||
|
assert!(super::is_safe_to_reveal("OPENAI_COMPAT_MODEL"));
|
||||||
|
assert!(super::is_safe_to_reveal("OPENAI_COMPAT_API"));
|
||||||
|
assert!(!super::is_safe_to_reveal("OPENAI_COMPAT_API_KEY"));
|
||||||
// Keys NOT in the allowlist — masked regardless of naming pattern.
|
// Keys NOT in the allowlist — masked regardless of naming pattern.
|
||||||
assert!(!super::is_safe_to_reveal("my_api_key"));
|
assert!(!super::is_safe_to_reveal("my_api_key"));
|
||||||
assert!(!super::is_safe_to_reveal("GITHUB_TOKEN"));
|
assert!(!super::is_safe_to_reveal("GITHUB_TOKEN"));
|
||||||
|
|||||||
@@ -181,6 +181,9 @@ pub fn validate_user_env_keys(env_vars: &BTreeMap<String, String>) -> Result<(),
|
|||||||
/// - `BUZZ_AGENT_THINKING_EFFORT` — non-secret enum (none/minimal/low/medium/high/xhigh/max)
|
/// - `BUZZ_AGENT_THINKING_EFFORT` — non-secret enum (none/minimal/low/medium/high/xhigh/max)
|
||||||
/// - `BUZZ_AGENT_THINKING_SUMMARY` — non-secret enum (auto/concise/detailed)
|
/// - `BUZZ_AGENT_THINKING_SUMMARY` — non-secret enum (auto/concise/detailed)
|
||||||
/// - `DATABRICKS_HOST`, `DATABRICKS_MODEL`, `DATABRICKS_MODEL_FILTER` — Block non-secret defaults
|
/// - `DATABRICKS_HOST`, `DATABRICKS_MODEL`, `DATABRICKS_MODEL_FILTER` — Block non-secret defaults
|
||||||
|
/// - `OPENAI_COMPAT_BASE_URL`, `OPENAI_COMPAT_MODEL`, `OPENAI_COMPAT_API` — OpenAI-compatible
|
||||||
|
/// endpoint, model id, and API flavor (`auto|chat|responses`); the key itself
|
||||||
|
/// (`OPENAI_COMPAT_API_KEY`) is not listed and stays masked
|
||||||
pub(crate) fn is_safe_to_reveal(key: &str) -> bool {
|
pub(crate) fn is_safe_to_reveal(key: &str) -> bool {
|
||||||
const SAFE_KEYS: &[&str] = &[
|
const SAFE_KEYS: &[&str] = &[
|
||||||
"BUZZ_AGENT_PROVIDER",
|
"BUZZ_AGENT_PROVIDER",
|
||||||
@@ -190,6 +193,9 @@ pub(crate) fn is_safe_to_reveal(key: &str) -> bool {
|
|||||||
"DATABRICKS_HOST",
|
"DATABRICKS_HOST",
|
||||||
"DATABRICKS_MODEL",
|
"DATABRICKS_MODEL",
|
||||||
"DATABRICKS_MODEL_FILTER",
|
"DATABRICKS_MODEL_FILTER",
|
||||||
|
"OPENAI_COMPAT_BASE_URL",
|
||||||
|
"OPENAI_COMPAT_MODEL",
|
||||||
|
"OPENAI_COMPAT_API",
|
||||||
];
|
];
|
||||||
let upper = key.to_ascii_uppercase();
|
let upper = key.to_ascii_uppercase();
|
||||||
SAFE_KEYS.iter().any(|safe| upper == *safe)
|
SAFE_KEYS.iter().any(|safe| upper == *safe)
|
||||||
|
|||||||
Reference in New Issue
Block a user