mirror of
https://github.com/openai/codex.git
synced 2026-09-07 15:40:00 +00:00
Keep invalid hook types advisory
This commit is contained in:
@@ -5,7 +5,10 @@ use std::path::PathBuf;
|
||||
use codex_protocol::protocol::HookEventName;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Deserializer;
|
||||
use serde::Serialize;
|
||||
use serde::de::Error as SerdeError;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct HooksFile {
|
||||
@@ -102,10 +105,36 @@ impl HookEventsToml {
|
||||
pub struct MatcherGroup {
|
||||
#[serde(default)]
|
||||
pub matcher: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_hook_handlers")]
|
||||
pub hooks: Vec<HookHandlerConfig>,
|
||||
}
|
||||
|
||||
/// Deserialize hook handlers while dropping entries with unknown tagged variants.
|
||||
///
|
||||
/// The schema warning pass reports invalid `type` values before typed config
|
||||
/// deserialization. Dropping only those entries keeps startup warnings
|
||||
/// non-blocking without making unrelated hook shape errors silent.
|
||||
fn deserialize_hook_handlers<'de, D>(deserializer: D) -> Result<Vec<HookHandlerConfig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let values = Vec::<JsonValue>::deserialize(deserializer)?;
|
||||
let mut handlers = Vec::new();
|
||||
|
||||
for value in values {
|
||||
let invalid_type = value.get("type").is_some_and(|handler_type| {
|
||||
!matches!(handler_type.as_str(), Some("command" | "prompt" | "agent"))
|
||||
});
|
||||
match serde_json::from_value(value) {
|
||||
Ok(handler) => handlers.push(handler),
|
||||
Err(_) if invalid_type => {}
|
||||
Err(err) => return Err(SerdeError::custom(err)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(handlers)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum HookHandlerConfig {
|
||||
|
||||
@@ -128,6 +128,62 @@ command = "python3 /tmp/pre.py"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hooks_toml_drops_unknown_handler_type() {
|
||||
let parsed: HooksToml = toml::from_str(
|
||||
r#"
|
||||
[[UserPromptSubmit]]
|
||||
matcher = "^UserPromptSubmit$"
|
||||
|
||||
[[UserPromptSubmit.hooks]]
|
||||
type = "python"
|
||||
command = "python3 /tmp/ignored.py"
|
||||
|
||||
[[UserPromptSubmit.hooks]]
|
||||
type = 7
|
||||
command = "python3 /tmp/also-ignored.py"
|
||||
|
||||
[[UserPromptSubmit.hooks]]
|
||||
type = "command"
|
||||
command = "python3 /tmp/kept.py"
|
||||
"#,
|
||||
)
|
||||
.expect("unknown hook handler type should be dropped");
|
||||
|
||||
assert_eq!(
|
||||
parsed,
|
||||
HooksToml {
|
||||
events: HookEventsToml {
|
||||
user_prompt_submit: vec![MatcherGroup {
|
||||
matcher: Some("^UserPromptSubmit$".to_string()),
|
||||
hooks: vec![HookHandlerConfig::Command {
|
||||
command: "python3 /tmp/kept.py".to_string(),
|
||||
timeout_sec: None,
|
||||
r#async: false,
|
||||
status_message: None,
|
||||
}],
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
state: BTreeMap::new(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hooks_toml_keeps_non_enum_handler_errors_strict() {
|
||||
let result = toml::from_str::<HooksToml>(
|
||||
r#"
|
||||
[[UserPromptSubmit]]
|
||||
|
||||
[[UserPromptSubmit.hooks]]
|
||||
type = "command"
|
||||
"#,
|
||||
);
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn managed_hooks_requirements_flatten_hook_events() {
|
||||
let parsed: ManagedHooksRequirementsToml = toml::from_str(
|
||||
|
||||
Reference in New Issue
Block a user