From f61db311e13f27737c0720f9b652f42f03121804 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Sun, 4 Oct 2026 10:14:52 +0100 Subject: [PATCH 1/2] fix(policy): name the field in policy type errors Signed-off-by: Eric Curtin --- Cargo.lock | 1 + Cargo.toml | 1 + crates/openshell-policy-schema/Cargo.toml | 1 + crates/openshell-policy-schema/src/lib.rs | 60 +++++++++++++++++++++-- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a36b8cd4a5..cec0a9395e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4748,6 +4748,7 @@ dependencies = [ "noyalib", "serde", "serde_json", + "serde_path_to_error", "thiserror 2.0.20", ] diff --git a/Cargo.toml b/Cargo.toml index 98e323f2ea..b7241a02c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ socket2 = "0.6" # Serialization serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_path_to_error = "0.1" serde_yml = { package = "noyalib", version = "0.0.28", default-features = false, features = ["std", "compat-serde-yaml"] } toml = "0.8" apollo-parser = "0.8.5" diff --git a/crates/openshell-policy-schema/Cargo.toml b/crates/openshell-policy-schema/Cargo.toml index 6352090c26..babfef3720 100644 --- a/crates/openshell-policy-schema/Cargo.toml +++ b/crates/openshell-policy-schema/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true miette = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +serde_path_to_error = { workspace = true } serde_yml = { workspace = true } thiserror = { workspace = true } diff --git a/crates/openshell-policy-schema/src/lib.rs b/crates/openshell-policy-schema/src/lib.rs index 0adef3ac40..8642ce8599 100644 --- a/crates/openshell-policy-schema/src/lib.rs +++ b/crates/openshell-policy-schema/src/lib.rs @@ -591,9 +591,17 @@ pub fn parse_policy_with_limits(source: &str, limits: ParseLimits) -> Result) -> &[serde_yml::Value] { } fn join(parent: &str, child: &str) -> String { - let mut path = if parent.is_empty() { + bound_path(if parent.is_empty() { child.to_owned() } else { format!("{parent}.{child}") - }; + }) +} + +// Cap diagnostic paths so oversized authored keys cannot bloat errors. +fn bound_path(mut path: String) -> String { if path.len() > MAX_UNKNOWN_FIELD_PATH_BYTES { let mut end = MAX_UNKNOWN_FIELD_PATH_BYTES - 3; while !path.is_char_boundary(end) { @@ -1279,6 +1291,44 @@ mod tests { } } + fn decode_error_chain(source: &str) -> Vec { + let error = parse_policy(source).expect_err("type mismatch must fail closed"); + error.chain().map(ToString::to_string).collect() + } + + #[test] + fn type_errors_name_the_offending_field() { + let source = "version: 1\nnetwork_policies:\n github_api:\n endpoints:\n - host: api.github.com\n port: \"443\"\n"; + assert_eq!( + decode_error_chain(source), + [ + "failed to decode sandbox policy fields", + "network_policies.github_api.endpoints[0].port: type mismatch: expected unsigned integer, found string", + ] + ); + } + + #[test] + fn type_errors_without_a_path_keep_their_message() { + let chain = decode_error_chain("- version: 1\n"); + assert_eq!(chain[0], "failed to decode sandbox policy fields"); + assert!( + !chain[1].starts_with(".: "), + "unexpected prefix in {chain:?}" + ); + } + + #[test] + fn bounds_type_error_paths_under_long_keys() { + let policy_name = "é".repeat(2_000); + let source = format!( + "version: 1\nnetwork_policies:\n {policy_name}:\n endpoints:\n - host: example.com\n port: \"443\"\n" + ); + let chain = decode_error_chain(&source); + assert!(chain[1].contains("...: type mismatch")); + assert!(chain[1].len() <= MAX_UNKNOWN_FIELD_PATH_BYTES + 100); + } + #[test] fn accepts_open_user_data_maps() { let source = r#" From 90bf975689cb072f21e6f7ff54b28840feef2f15 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Tue, 6 Oct 2026 02:16:56 +0100 Subject: [PATCH 2/2] fix(policy): refresh example lockfiles Signed-off-by: Eric Curtin --- examples/governance-interceptor/Cargo.lock | 12 ++++++++++++ .../supervisor-middleware-content-guard/Cargo.lock | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/examples/governance-interceptor/Cargo.lock b/examples/governance-interceptor/Cargo.lock index d329018546..f116601386 100644 --- a/examples/governance-interceptor/Cargo.lock +++ b/examples/governance-interceptor/Cargo.lock @@ -1142,6 +1142,7 @@ dependencies = [ "noyalib", "serde", "serde_json", + "serde_path_to_error", "thiserror", ] @@ -1762,6 +1763,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "sha2" version = "0.10.9" diff --git a/examples/supervisor-middleware-content-guard/Cargo.lock b/examples/supervisor-middleware-content-guard/Cargo.lock index 0927d784b0..07b3e92e7b 100644 --- a/examples/supervisor-middleware-content-guard/Cargo.lock +++ b/examples/supervisor-middleware-content-guard/Cargo.lock @@ -1211,6 +1211,7 @@ dependencies = [ "noyalib", "serde", "serde_json", + "serde_path_to_error", "thiserror", ] @@ -1761,6 +1762,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "sha2" version = "0.10.9"