mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Warn when hooks.json has unsupported top-level fields (#26426)
Addresses #25875. ## Summary `hooks.json` accepted unknown top-level fields. A file with `SessionStart` at the root parsed as an empty hook configuration without warning. ## Repro ```json { "SessionStart": [...] } ``` Previously: zero hooks, zero warnings. Now: ```text unknown field `SessionStart`, expected `hooks` ``` The supported shape remains: ```json { "hooks": { "SessionStart": [...] } } ``` ## Fix Reject unknown top-level fields and surface the parse warning in human and JSONL `codex exec` output.
This commit is contained in:
@@ -1225,6 +1225,62 @@ fn profile_user_layers_load_shared_hooks_json_once() {
|
||||
assert_eq!(listed.hooks[0].source_path, hooks_json_path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_hooks_json_is_reported_as_startup_warning() {
|
||||
let temp = tempdir().expect("create temp dir");
|
||||
let config_path =
|
||||
AbsolutePathBuf::try_from(temp.path().join("config.toml")).expect("absolute config path");
|
||||
let hooks_json_path =
|
||||
AbsolutePathBuf::try_from(temp.path().join("hooks.json")).expect("absolute hooks path");
|
||||
fs::write(
|
||||
hooks_json_path.as_path(),
|
||||
r#"{
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 /tmp/session-start.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}"#,
|
||||
)
|
||||
.expect("write hooks.json");
|
||||
let config_layer_stack = ConfigLayerStack::new(
|
||||
vec![ConfigLayerEntry::new(
|
||||
ConfigLayerSource::System { file: config_path },
|
||||
TomlValue::Table(Default::default()),
|
||||
)],
|
||||
ConfigRequirements::default(),
|
||||
ConfigRequirementsToml::default(),
|
||||
)
|
||||
.expect("config layer stack");
|
||||
|
||||
let engine = ClaudeHooksEngine::new(
|
||||
/*enabled*/ true,
|
||||
/*bypass_hook_trust*/ false,
|
||||
Some(&config_layer_stack),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
CommandShell {
|
||||
program: String::new(),
|
||||
args: Vec::new(),
|
||||
},
|
||||
);
|
||||
|
||||
assert!(engine.handlers.is_empty());
|
||||
assert_eq!(engine.warnings().len(), 1);
|
||||
assert!(engine.warnings()[0].contains("failed to parse hooks config"));
|
||||
assert!(
|
||||
engine.warnings()[0].contains(&hooks_json_path.display().to_string()),
|
||||
"warning should identify the malformed file: {}",
|
||||
engine.warnings()[0]
|
||||
);
|
||||
assert!(engine.warnings()[0].contains("unknown field `SessionStart`"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_hook_sources_run_with_plugin_env_and_plugin_source() {
|
||||
let temp = tempdir().expect("create temp dir");
|
||||
|
||||
Reference in New Issue
Block a user