fix: don't log cloud provider keys (#1145)
* fix: don't log cloud provider keys * test: make sure keys are redacted * change to newtype * change secret struct to public * test: test secretmap directly * Update bindings.ts --------- Co-authored-by: Shaan <shaankhosla@macbook-pro.mynetworksettings.com> Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
parent
075fb5f78d
commit
4239cd3584
2 changed files with 63 additions and 4 deletions
|
|
@ -3,6 +3,7 @@ use serde::de::{self, Visitor};
|
|||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use specta::Type;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
|
||||
|
|
@ -304,6 +305,34 @@ impl Default for OrtAcceleratorSetting {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Type)]
|
||||
#[serde(transparent)]
|
||||
pub(crate) struct SecretMap(HashMap<String, String>);
|
||||
|
||||
impl fmt::Debug for SecretMap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let redacted: HashMap<&String, &str> = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|(k, v)| (k, if v.is_empty() { "" } else { "[REDACTED]" }))
|
||||
.collect();
|
||||
redacted.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for SecretMap {
|
||||
type Target = HashMap<String, String>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for SecretMap {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
/* still handy for composing the initial JSON in the store ------------- */
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Type)]
|
||||
pub struct AppSettings {
|
||||
|
|
@ -365,7 +394,7 @@ pub struct AppSettings {
|
|||
#[serde(default = "default_post_process_providers")]
|
||||
pub post_process_providers: Vec<PostProcessProvider>,
|
||||
#[serde(default = "default_post_process_api_keys")]
|
||||
pub post_process_api_keys: HashMap<String, String>,
|
||||
pub post_process_api_keys: SecretMap,
|
||||
#[serde(default = "default_post_process_models")]
|
||||
pub post_process_models: HashMap<String, String>,
|
||||
#[serde(default = "default_post_process_prompts")]
|
||||
|
|
@ -573,12 +602,12 @@ fn default_post_process_providers() -> Vec<PostProcessProvider> {
|
|||
providers
|
||||
}
|
||||
|
||||
fn default_post_process_api_keys() -> HashMap<String, String> {
|
||||
fn default_post_process_api_keys() -> SecretMap {
|
||||
let mut map = HashMap::new();
|
||||
for provider in default_post_process_providers() {
|
||||
map.insert(provider.id, String::new());
|
||||
}
|
||||
map
|
||||
SecretMap(map)
|
||||
}
|
||||
|
||||
fn default_model_for_provider(provider_id: &str) -> String {
|
||||
|
|
@ -918,4 +947,33 @@ mod tests {
|
|||
assert!(!settings.auto_submit);
|
||||
assert_eq!(settings.auto_submit_key, AutoSubmitKey::Enter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_output_redacts_api_keys() {
|
||||
let mut settings = get_default_settings();
|
||||
settings
|
||||
.post_process_api_keys
|
||||
.insert("openai".to_string(), "sk-proj-secret-key-12345".to_string());
|
||||
settings.post_process_api_keys.insert(
|
||||
"anthropic".to_string(),
|
||||
"sk-ant-secret-key-67890".to_string(),
|
||||
);
|
||||
settings
|
||||
.post_process_api_keys
|
||||
.insert("empty_provider".to_string(), "".to_string());
|
||||
|
||||
let debug_output = format!("{:?}", settings);
|
||||
|
||||
assert!(!debug_output.contains("sk-proj-secret-key-12345"));
|
||||
assert!(!debug_output.contains("sk-ant-secret-key-67890"));
|
||||
assert!(debug_output.contains("[REDACTED]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_map_debug_redacts_values() {
|
||||
let map = SecretMap(HashMap::from([("key".into(), "secret".into())]));
|
||||
let out = format!("{:?}", map);
|
||||
assert!(!out.contains("secret"));
|
||||
assert!(out.contains("[REDACTED]"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -827,7 +827,7 @@ historyUpdatePayload: "history-update-payload"
|
|||
|
||||
/** user-defined types **/
|
||||
|
||||
export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: number; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean; app_language?: string; experimental_enabled?: boolean; lazy_stream_close?: boolean; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool; external_script_path: string | null; custom_filler_words?: string[] | null; whisper_accelerator?: WhisperAcceleratorSetting; ort_accelerator?: OrtAcceleratorSetting; whisper_gpu_device?: number; extra_recording_buffer_ms?: number }
|
||||
export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: number; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: SecretMap; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean; app_language?: string; experimental_enabled?: boolean; lazy_stream_close?: boolean; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool; external_script_path: string | null; custom_filler_words?: string[] | null; whisper_accelerator?: WhisperAcceleratorSetting; ort_accelerator?: OrtAcceleratorSetting; whisper_gpu_device?: number; extra_recording_buffer_ms?: number }
|
||||
export type AudioDevice = { index: string; name: string; is_default: boolean }
|
||||
export type AutoSubmitKey = "enter" | "ctrl_enter" | "cmd_enter"
|
||||
export type AvailableAccelerators = { whisper: string[]; ort: string[]; gpu_devices: GpuDeviceOption[] }
|
||||
|
|
@ -859,6 +859,7 @@ export type PasteMethod = "ctrl_v" | "direct" | "none" | "shift_insert" | "ctrl_
|
|||
export type PermissionAccess = "allowed" | "denied" | "unknown"
|
||||
export type PostProcessProvider = { id: string; label: string; base_url: string; allow_base_url_edit?: boolean; models_endpoint?: string | null; supports_structured_output?: boolean }
|
||||
export type RecordingRetentionPeriod = "never" | "preserve_limit" | "days_3" | "weeks_2" | "months_3"
|
||||
export type SecretMap = Partial<{ [key in string]: string }>
|
||||
export type ShortcutBinding = { id: string; name: string; description: string; default_binding: string; current_binding: string }
|
||||
export type SoundTheme = "marimba" | "pop" | "custom"
|
||||
export type TypingTool = "auto" | "wtype" | "kwtype" | "dotool" | "ydotool" | "xdotool"
|
||||
|
|
|
|||
Loading…
Reference in a new issue