| 417 | } |
| 418 | |
| 419 | fn validate_vertex_model_id(value: &str) -> Result<(), Status> { |
| 420 | let trimmed = value.trim(); |
| 421 | if trimmed.is_empty() { |
| 422 | return Err(Status::invalid_argument("model_id is required")); |
| 423 | } |
| 424 | if value != trimmed { |
| 425 | return Err(Status::invalid_argument(format!( |
| 426 | "Vertex AI model_id must not include leading or trailing whitespace: {value:?}" |
| 427 | ))); |
| 428 | } |
| 429 | if value.contains('/') || value.contains('\\') { |
| 430 | return Err(Status::invalid_argument(format!( |
| 431 | "Vertex AI model_id must not contain path separators: {value:?}" |
| 432 | ))); |
| 433 | } |
| 434 | if value.chars().any(|c| matches!(c, '?' | '#' | '%')) { |
| 435 | return Err(Status::invalid_argument(format!( |
| 436 | "Vertex AI model_id must not contain URL delimiters or percent escapes: {value:?}" |
| 437 | ))); |
| 438 | } |
| 439 | if value.contains("..") { |
| 440 | return Err(Status::invalid_argument(format!( |
| 441 | "Vertex AI model_id must not contain traversal segments: {value:?}" |
| 442 | ))); |
| 443 | } |
| 444 | if value.chars().any(|c| c.is_control() || c.is_whitespace()) { |
| 445 | return Err(Status::invalid_argument(format!( |
| 446 | "Vertex AI model_id must not contain whitespace or control characters: {value:?}" |
| 447 | ))); |
| 448 | } |
| 449 | Ok(()) |
| 450 | } |
| 451 | |
| 452 | fn is_allowed_vertex_override_host(host: &str) -> bool { |
| 453 | matches!( |