use std::collections::{BTreeMap, BTreeSet};
use crate::schema::{ApiProtocol, ModelSchema, ModelSource, ProprietaryAuth};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForeignReservedCredential {
pub credential: String,
pub host: Option<String>,
pub provider_hosts: Vec<String>,
}
pub fn is_reserved_credential(name: &str) -> bool {
if !name.is_ascii() {
return true;
}
let name = name.to_ascii_uppercase();
car_secrets::CAR_OWNED_SECRET_KEYS
.iter()
.any(|owned| owned.eq_ignore_ascii_case(&name))
|| car_secrets::CAR_OWNED_SECRET_PREFIXES
.iter()
.any(|prefix| name.starts_with(&prefix.to_ascii_uppercase()))
|| crate::registry::builtin_credential_env_names()
.iter()
.any(|reserved| reserved.eq_ignore_ascii_case(&name))
|| car_secrets::KEY_PROVIDERS.iter().any(|provider| {
provider
.keys
.iter()
.any(|key| key.eq_ignore_ascii_case(&name))
})
|| car_secrets::is_daemon_private_secret(car_secrets::DEFAULT_SERVICE, &name)
}
pub fn foreign_reserved_credential(schema: &ModelSchema) -> Option<ForeignReservedCredential> {
let (credentials, host) = credential_destination(schema)?;
let reserved: Vec<String> = credentials
.into_iter()
.filter(|name| is_reserved_credential(name))
.collect();
if reserved.is_empty() {
return None;
}
let owned = provider_hosts();
reserved.into_iter().find_map(|credential| {
let hosts = owned
.get(&credential.to_ascii_uppercase())
.cloned()
.unwrap_or_default();
let at_home = host.as_ref().is_some_and(|host| hosts.contains(host));
(!at_home).then(|| ForeignReservedCredential {
credential,
host: host.clone(),
provider_hosts: hosts.into_iter().collect(),
})
})
}
fn provider_hosts() -> BTreeMap<String, BTreeSet<String>> {
let mut owned: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
for schema in crate::registry::builtin_catalog() {
let Some((credentials, Some(host))) = credential_destination(&schema) else {
continue;
};
for credential in credentials {
owned
.entry(credential.to_ascii_uppercase())
.or_default()
.insert(host.clone());
}
}
owned
.entry("TYPESAFE_API_KEY".into())
.or_default()
.insert("api.typesafe.ai".into());
owned
}
fn credential_destination(schema: &ModelSchema) -> Option<(Vec<String>, Option<String>)> {
match &schema.source {
ModelSource::RemoteApi {
protocol: ApiProtocol::Bedrock,
..
} => None,
ModelSource::RemoteApi {
protocol: ApiProtocol::OpenRouter,
endpoint,
..
} => Some((
vec![
crate::openrouter::API_KEY_ENV.to_string(),
car_secrets::OPENROUTER_OAUTH_KEY.to_string(),
],
endpoint_host(endpoint),
)),
ModelSource::RemoteApi {
protocol:
ApiProtocol::OpenAiCompat
| ApiProtocol::OpenAiResponses
| ApiProtocol::Anthropic
| ApiProtocol::Google
| ApiProtocol::AzureOpenAi
| ApiProtocol::VertexAi,
endpoint,
..
}
| ModelSource::Proprietary {
auth: ProprietaryAuth::ApiKeyEnv { .. } | ProprietaryAuth::BearerTokenEnv { .. },
endpoint,
..
} => Some((schema.all_api_key_envs(), endpoint_host(endpoint))),
ModelSource::Proprietary {
auth: ProprietaryAuth::OAuth2Pkce { .. } | ProprietaryAuth::ChatGptSubscription {},
..
}
| ModelSource::Local { .. }
| ModelSource::CodexCli { .. }
| ModelSource::Ollama { .. }
| ModelSource::Mlx { .. }
| ModelSource::WhisperCpp { .. }
| ModelSource::WindowsSpeech {}
| ModelSource::VllmMlx { .. }
| ModelSource::ManagedVllmMlx { .. }
| ModelSource::AppleFoundationModels { .. }
| ModelSource::Delegated { .. } => None,
}
}
fn endpoint_host(endpoint: &str) -> Option<String> {
reqwest::Url::parse(endpoint.trim())
.ok()
.and_then(|url| url.host_str().map(str::to_string))
}
#[cfg(test)]
mod tests {
use super::*;
fn builtin_row(source_type: &str) -> ModelSchema {
crate::registry::builtin_catalog()
.into_iter()
.find(|schema| schema.source.kind() == source_type)
.unwrap_or_else(|| panic!("the built-in catalog has a {source_type} row"))
}
fn remote(protocol: ApiProtocol, endpoint: &str, api_key_env: &str) -> ModelSchema {
let mut schema = builtin_row("remote_api");
schema.source = ModelSource::RemoteApi {
endpoint: endpoint.into(),
api_key_env: api_key_env.into(),
api_key_envs: vec![],
api_version: None,
protocol,
};
schema
}
fn proprietary(endpoint: &str, auth: ProprietaryAuth) -> ModelSchema {
let mut schema = builtin_row("proprietary");
let ModelSource::Proprietary {
endpoint: row_endpoint,
auth: row_auth,
..
} = &mut schema.source
else {
unreachable!("builtin_row returned a proprietary row");
};
*row_endpoint = endpoint.into();
*row_auth = auth;
schema
}
#[test]
fn the_reserved_names_and_provider_hosts_the_tests_rely_on() {
for name in [
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"GOOGLE_API_KEY",
"ELEVENLABS_API_KEY",
"OPENROUTER_API_KEY",
"GEMINI_API_KEY",
"GITHUB_TOKEN",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
car_secrets::OPENROUTER_OAUTH_KEY,
car_secrets::PARSLEE_ACCESS_TOKEN_KEY,
] {
assert!(is_reserved_credential(name), "{name} must be reserved");
}
for name in car_secrets::CAR_OWNED_SECRET_KEYS.iter().copied().chain([
car_auth::codex::CODEX_CREDENTIAL_KEY,
crate::search::TAVILY_API_KEY_ENV,
"HF_TOKEN",
"connector:github:tokens",
"connector:linear:client_secret",
"connector:notion:Authorization",
"CAR_SOME_FUTURE_TOKEN",
"PARSLEE_SOME_FUTURE_SECRET",
]) {
assert!(is_reserved_credential(name), "{name} must be reserved");
}
for name in ["ÖPENAI_API_KEY", "OPENAI_API_KEY\u{200b}", "openai"] {
assert!(is_reserved_credential(name), "{name:?} fails closed");
}
for name in [
"openai_api_key",
"Anthropic_Api_Key",
"aws_secret_access_key",
] {
assert!(is_reserved_credential(name), "{name} must be reserved");
}
for name in ["MY_GATEWAY_API_KEY", "AZURE_OPENAI_API_KEY", "TEST_API_KEY"] {
assert!(!is_reserved_credential(name), "{name} is custom-named");
}
let owned = provider_hosts();
let hosts = |name: &str| owned.get(name).cloned().unwrap_or_default();
assert_eq!(
hosts("OPENAI_API_KEY"),
["api.openai.com".to_string()].into()
);
assert_eq!(
hosts("ANTHROPIC_API_KEY"),
["api.anthropic.com".to_string()].into()
);
assert_eq!(
hosts("GOOGLE_API_KEY"),
["generativelanguage.googleapis.com".to_string()].into()
);
assert_eq!(
hosts("ELEVENLABS_API_KEY"),
["api.elevenlabs.io".to_string()].into()
);
assert_eq!(
hosts("OPENROUTER_API_KEY"),
["openrouter.ai".to_string()].into()
);
for unsent in ["GITHUB_TOKEN", "GEMINI_API_KEY", "AWS_SECRET_ACCESS_KEY"] {
assert!(hosts(unsent).is_empty(), "{unsent} has no provider host");
}
}
#[test]
fn a_reserved_key_sent_to_a_foreign_host_is_reported() {
let foreign = foreign_reserved_credential(&remote(
ApiProtocol::OpenAiCompat,
"https://evil.example/v1",
"OPENAI_API_KEY",
))
.expect("OPENAI_API_KEY to evil.example is foreign");
assert_eq!(foreign.credential, "OPENAI_API_KEY");
assert_eq!(foreign.host.as_deref(), Some("evil.example"));
assert_eq!(foreign.provider_hosts, ["api.openai.com"]);
for (endpoint, host) in [
("https://api.openai.com@evil.example", Some("evil.example")),
(
"https://api.openai.com.evil.example",
Some("api.openai.com.evil.example"),
),
("http://127.0.0.1:8080/v1", Some("127.0.0.1")),
("api.openai.com", None),
] {
let foreign = foreign_reserved_credential(&remote(
ApiProtocol::OpenAiCompat,
endpoint,
"OPENAI_API_KEY",
))
.unwrap_or_else(|| panic!("{endpoint} must be foreign"));
assert_eq!(foreign.host.as_deref(), host, "{endpoint}");
}
}
#[test]
fn a_reserved_key_in_another_case_is_still_reserved() {
let foreign = foreign_reserved_credential(&remote(
ApiProtocol::OpenAiCompat,
"https://evil.example/v1",
"openai_api_key",
))
.expect("openai_api_key reaches OPENAI_API_KEY on Windows");
assert_eq!(foreign.credential, "openai_api_key");
assert_eq!(foreign.provider_hosts, ["api.openai.com"]);
assert_eq!(
foreign_reserved_credential(&remote(
ApiProtocol::OpenAiCompat,
"https://api.openai.com/v1",
"openai_api_key",
)),
None
);
}
#[test]
fn every_reserved_credential_slot_on_a_row_is_checked() {
let mut pooled = remote(ApiProtocol::OpenAiCompat, "https://evil.example", "MY_KEY");
if let ModelSource::RemoteApi { api_key_envs, .. } = &mut pooled.source {
api_key_envs.push("ANTHROPIC_API_KEY".into());
}
assert_eq!(
foreign_reserved_credential(&pooled).unwrap().credential,
"ANTHROPIC_API_KEY"
);
for auth in [
ProprietaryAuth::BearerTokenEnv {
env_var: "AWS_SECRET_ACCESS_KEY".into(),
},
ProprietaryAuth::ApiKeyEnv {
env_var: "GITHUB_TOKEN".into(),
},
] {
let schema = proprietary("https://evil.example", auth);
assert!(foreign_reserved_credential(&schema).is_some(), "{schema:?}");
}
let openrouter = remote(
ApiProtocol::OpenRouter,
"https://evil.example/api",
"MY_KEY",
);
assert_eq!(
foreign_reserved_credential(&openrouter).unwrap().credential,
"OPENROUTER_API_KEY"
);
let private = remote(
ApiProtocol::OpenAiCompat,
"https://api.openai.com",
car_secrets::PARSLEE_ACCESS_TOKEN_KEY,
);
assert!(foreign_reserved_credential(&private).is_some());
}
#[test]
fn a_provider_host_and_a_custom_credential_are_allowed() {
for (protocol, endpoint, credential) in [
(
ApiProtocol::OpenAiResponses,
"https://api.openai.com",
"OPENAI_API_KEY",
),
(
ApiProtocol::OpenAiCompat,
"https://API.OpenAI.com:443/v1/",
"OPENAI_API_KEY",
),
(
ApiProtocol::Anthropic,
"https://api.anthropic.com/v1/messages",
"ANTHROPIC_API_KEY",
),
(
ApiProtocol::Google,
"https://generativelanguage.googleapis.com",
"GOOGLE_API_KEY",
),
(
ApiProtocol::OpenRouter,
"https://openrouter.ai/api",
"OPENROUTER_API_KEY",
),
(
ApiProtocol::OpenAiCompat,
"https://evil.example/v1",
"MY_GATEWAY_API_KEY",
),
(
ApiProtocol::AzureOpenAi,
"https://me.openai.azure.com",
"AZURE_OPENAI_API_KEY",
),
] {
assert_eq!(
foreign_reserved_credential(&remote(protocol, endpoint, credential)),
None,
"{endpoint} with {credential}"
);
}
assert_eq!(
foreign_reserved_credential(&proprietary(
"https://api.elevenlabs.io",
ProprietaryAuth::ApiKeyEnv {
env_var: "ELEVENLABS_API_KEY".into()
},
)),
None
);
assert_eq!(
foreign_reserved_credential(&remote(
ApiProtocol::Bedrock,
"us-east-1",
"AWS_ACCESS_KEY_ID"
)),
None
);
}
#[test]
fn every_built_in_row_passes_its_own_check() {
for schema in crate::registry::builtin_catalog() {
assert_eq!(foreign_reserved_credential(&schema), None, "{}", schema.id);
}
}
}