Skip to content

Commit 617df19

Browse files
committed
feat(chutes): replace whitelist with dynamic -TEE suffix validation
- Replace CHUTES_ALLOWED_MODELS whitelist with DEFAULT_CHUTES_MODEL constant - Update validate_chutes_model() to accept ANY model ending with '-TEE' suffix (case-insensitive) - Update provider_allows_custom_models() to allow custom models for Chutes - Add comprehensive tests for -TEE suffix validation (valid/invalid/case-insensitive) - Default model remains moonshotai/Kimi-K2.5-TEE - Custom models can now be used via --model chutes:{model_name} format This allows users to specify any TEE model available on Chutes, not just whitelisted ones.
1 parent 2ddcb1f commit 617df19

3 files changed

Lines changed: 50 additions & 45 deletions

File tree

src/cortex-common/src/model_presets/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub use constants::{DEFAULT_MODEL, DEFAULT_MODELS, DEFAULT_PROVIDER};
1919

2020
// Re-export preset data and helpers
2121
pub use presets::{
22-
CHUTES_ALLOWED_MODELS, MODEL_PRESETS, get_model_preset, get_models_for_provider,
22+
DEFAULT_CHUTES_MODEL, MODEL_PRESETS, get_model_preset, get_models_for_provider,
2323
provider_allows_custom_models, validate_chutes_model,
2424
};
2525

src/cortex-common/src/model_presets/presets.rs

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use super::types::ModelPreset;
44

5-
/// Allowed models for Chutes provider (whitelist approach for security).
6-
/// Only these models can be used with the Chutes provider.
7-
pub const CHUTES_ALLOWED_MODELS: &[&str] = &["moonshotai/Kimi-K2.5-TEE"];
5+
/// Default model for Chutes provider.
6+
/// This is the fallback model when no specific model is provided.
7+
pub const DEFAULT_CHUTES_MODEL: &str = "moonshotai/Kimi-K2.5-TEE";
88

99
/// Available model presets.
1010
pub const MODEL_PRESETS: &[ModelPreset] = &[
@@ -838,6 +838,7 @@ pub fn get_models_for_provider(provider: &str) -> Vec<&'static ModelPreset> {
838838

839839
/// Validates that a model is allowed for the Chutes provider.
840840
/// Chutes only allows TEE (Trusted Execution Environment) models for security.
841+
/// Any model ending with '-TEE' suffix (case-insensitive) is accepted.
841842
/// Returns Ok(()) if valid, Err with message if invalid.
842843
pub fn validate_chutes_model(model: &str) -> Result<(), String> {
843844
let model = model.trim();
@@ -847,38 +848,26 @@ pub fn validate_chutes_model(model: &str) -> Result<(), String> {
847848
return Err("Model name cannot be empty for Chutes provider".to_string());
848849
}
849850

850-
// Check suffix (case-insensitive)
851+
// Check suffix (case-insensitive) - any model ending with -TEE is allowed
851852
if !model.to_uppercase().ends_with("-TEE") {
852853
return Err(format!(
853854
"Chutes provider only allows TEE models (models ending with '-TEE'). \
854-
Model '{}' is not a TEE model. Available TEE models: {}",
855+
Model '{}' is not a TEE model. Default model: {}",
855856
model,
856-
CHUTES_ALLOWED_MODELS.join(", ")
857-
));
858-
}
859-
860-
// SECURITY: Also verify model is in the allowed whitelist
861-
if !CHUTES_ALLOWED_MODELS
862-
.iter()
863-
.any(|&allowed| allowed.eq_ignore_ascii_case(model))
864-
{
865-
return Err(format!(
866-
"Model '{}' is not in the allowed Chutes models list. \
867-
Available models: {}",
868-
model,
869-
CHUTES_ALLOWED_MODELS.join(", ")
857+
DEFAULT_CHUTES_MODEL
870858
));
871859
}
872860

873861
Ok(())
874862
}
875863

876864
/// Checks if a provider restricts custom models.
877-
/// Chutes only allows predefined TEE models, no custom models.
865+
/// All providers allow custom models, but Chutes requires -TEE suffix.
878866
pub fn provider_allows_custom_models(provider: &str) -> bool {
879-
// Chutes does NOT allow custom models - only predefined TEE models
880-
// Use case-insensitive comparison
881-
!provider.eq_ignore_ascii_case("chutes")
867+
// All providers allow custom models
868+
// Chutes allows any model with -TEE suffix (validated via validate_chutes_model)
869+
let _ = provider; // Used for potential future provider-specific restrictions
870+
true
882871
}
883872

884873
#[cfg(test)]
@@ -887,52 +876,68 @@ mod tests {
887876

888877
#[test]
889878
fn test_validate_chutes_model_valid() {
890-
// Valid TEE models in whitelist
879+
// Default TEE model
891880
assert!(validate_chutes_model("moonshotai/Kimi-K2.5-TEE").is_ok());
892881
// Case insensitive
893882
assert!(validate_chutes_model("moonshotai/kimi-k2.5-tee").is_ok());
894883
assert!(validate_chutes_model("MOONSHOTAI/KIMI-K2.5-TEE").is_ok());
895884
// Whitespace handling
896885
assert!(validate_chutes_model(" moonshotai/Kimi-K2.5-TEE ").is_ok());
886+
// Any model with -TEE suffix is valid
887+
assert!(validate_chutes_model("custom-model-TEE").is_ok());
888+
assert!(validate_chutes_model("some-provider/my-model-TEE").is_ok());
889+
assert!(validate_chutes_model("another-model-tee").is_ok());
890+
assert!(validate_chutes_model("UPPERCASE-MODEL-TEE").is_ok());
897891
}
898892

899893
#[test]
900894
fn test_validate_chutes_model_invalid() {
901-
// Not a TEE model
895+
// Not a TEE model (no -TEE suffix)
902896
assert!(validate_chutes_model("gpt-4").is_err());
903897
assert!(validate_chutes_model("claude-3").is_err());
898+
assert!(validate_chutes_model("some-model").is_err());
904899

905-
// Has TEE suffix but NOT in whitelist
906-
assert!(validate_chutes_model("fake-model-TEE").is_err());
907-
assert!(validate_chutes_model("some-model-TEE").is_err());
908-
909-
// TEE in wrong position
900+
// TEE in wrong position (not at the end)
910901
assert!(validate_chutes_model("model-TEE-v2").is_err());
902+
assert!(validate_chutes_model("TEE-model").is_err());
903+
assert!(validate_chutes_model("my-TEE-model-v1").is_err());
911904

912905
// Empty string
913906
let result = validate_chutes_model("");
914907
assert!(result.is_err());
915908
assert!(result.unwrap_err().contains("cannot be empty"));
909+
910+
// Whitespace only
911+
let result = validate_chutes_model(" ");
912+
assert!(result.is_err());
913+
assert!(result.unwrap_err().contains("cannot be empty"));
916914
}
917915

918916
#[test]
919-
fn test_provider_allows_custom_models() {
920-
// Chutes does NOT allow custom models - case insensitive
921-
assert!(!provider_allows_custom_models("chutes"));
922-
assert!(!provider_allows_custom_models("Chutes"));
923-
assert!(!provider_allows_custom_models("CHUTES"));
917+
fn test_validate_chutes_model_error_message() {
918+
let result = validate_chutes_model("invalid-model");
919+
assert!(result.is_err());
920+
let err = result.unwrap_err();
921+
// Error message should mention the default model
922+
assert!(err.contains(DEFAULT_CHUTES_MODEL));
923+
assert!(err.contains("-TEE"));
924+
}
924925

925-
// Other providers allow custom models
926+
#[test]
927+
fn test_provider_allows_custom_models() {
928+
// All providers allow custom models
929+
assert!(provider_allows_custom_models("chutes"));
930+
assert!(provider_allows_custom_models("Chutes"));
931+
assert!(provider_allows_custom_models("CHUTES"));
926932
assert!(provider_allows_custom_models("cortex"));
927933
assert!(provider_allows_custom_models("openai"));
928934
assert!(provider_allows_custom_models("anthropic"));
929935
}
930936

931937
#[test]
932-
fn test_chutes_allowed_models_list() {
933-
// Verify the whitelist contains expected model
934-
assert!(CHUTES_ALLOWED_MODELS.contains(&"moonshotai/Kimi-K2.5-TEE"));
935-
// Whitelist should not be empty
936-
assert!(!CHUTES_ALLOWED_MODELS.is_empty());
938+
fn test_default_chutes_model() {
939+
// Verify the default model is a valid TEE model
940+
assert!(DEFAULT_CHUTES_MODEL.to_uppercase().ends_with("-TEE"));
941+
assert_eq!(DEFAULT_CHUTES_MODEL, "moonshotai/Kimi-K2.5-TEE");
937942
}
938943
}

src/cortex-tui/src/providers/manager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use cortex_engine::client::{
1111

1212
use super::config::CortexConfig;
1313
use super::models::{ModelInfo, get_models_for_provider, get_popular_models};
14-
use cortex_common::model_presets::{CHUTES_ALLOWED_MODELS, validate_chutes_model};
14+
use cortex_common::model_presets::{DEFAULT_CHUTES_MODEL, validate_chutes_model};
1515

1616
// ============================================================
1717
// PROVIDER MANAGER
@@ -49,7 +49,7 @@ impl ProviderManager {
4949
resetting to default",
5050
model
5151
);
52-
(provider.to_string(), CHUTES_ALLOWED_MODELS[0].to_string())
52+
(provider.to_string(), DEFAULT_CHUTES_MODEL.to_string())
5353
} else {
5454
(provider.to_string(), model.to_string())
5555
}
@@ -236,7 +236,7 @@ impl ProviderManager {
236236
switching to default",
237237
self.current_model
238238
);
239-
self.current_model = CHUTES_ALLOWED_MODELS[0].to_string();
239+
self.current_model = DEFAULT_CHUTES_MODEL.to_string();
240240
}
241241
}
242242

0 commit comments

Comments
 (0)