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:
tbedor-square2
2026-10-06 10:07:26 -07:00
committed by GitHub
parent 1e2281fd45
commit 403c19f673
2 changed files with 36 additions and 0 deletions

View File

@@ -714,6 +714,31 @@ fn baked_env_thinking_summary_is_unmasked() {
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]
fn baked_env_allowlist_is_case_insensitive() {
// 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_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.
assert!(!super::is_safe_to_reveal("my_api_key"));
assert!(!super::is_safe_to_reveal("GITHUB_TOKEN"));

View File

@@ -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_SUMMARY` — non-secret enum (auto/concise/detailed)
/// - `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 {
const SAFE_KEYS: &[&str] = &[
"BUZZ_AGENT_PROVIDER",
@@ -190,6 +193,9 @@ pub(crate) fn is_safe_to_reveal(key: &str) -> bool {
"DATABRICKS_HOST",
"DATABRICKS_MODEL",
"DATABRICKS_MODEL_FILTER",
"OPENAI_COMPAT_BASE_URL",
"OPENAI_COMPAT_MODEL",
"OPENAI_COMPAT_API",
];
let upper = key.to_ascii_uppercase();
SAFE_KEYS.iter().any(|safe| upper == *safe)