diff --git a/Cargo.lock b/Cargo.lock index f162faa..cd92c44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,9 +653,9 @@ dependencies = [ [[package]] name = "jsonc-parser" -version = "0.33.2" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff5a48f48971be8e762a6ff955725a0802b6e46c441057992da5a673db9fd3a" +checksum = "0ecded084f9b9a718d39668a42657c32253663d1f6afb3fbb9134f0305f73f48" dependencies = [ "serde", "serde_json", diff --git a/crates/controlfreak-installer/Cargo.toml b/crates/controlfreak-installer/Cargo.toml index 260c5df..881c007 100644 --- a/crates/controlfreak-installer/Cargo.toml +++ b/crates/controlfreak-installer/Cargo.toml @@ -8,7 +8,7 @@ version.workspace = true [dependencies] controlfreak-platform.workspace = true -jsonc-parser = { version = "0.33.2", features = ["cst", "serde", "serde_json"], default-features = false } +jsonc-parser = { version = "0.34.0", features = ["cst", "serde", "serde_json"], default-features = false } semver = "1.0.28" serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/crates/controlfreak-installer/src/config.rs b/crates/controlfreak-installer/src/config.rs index 3a4c815..d88c385 100644 --- a/crates/controlfreak-installer/src/config.rs +++ b/crates/controlfreak-installer/src/config.rs @@ -48,6 +48,9 @@ impl Document { allow_single_quoted_strings: false, allow_hexadecimal_numbers: false, allow_unary_plus_numbers: false, + allow_bare_decimal_point_numbers: false, + allow_non_finite_numbers: false, + allow_extended_string_escapes: false, }; let root = CstRootNode::parse(text, &options) .map_err(|_| "Invalid JSON; repair the configuration and retry.")?; diff --git a/crates/controlfreak-installer/tests/configuration.rs b/crates/controlfreak-installer/tests/configuration.rs index 2348ea1..b789839 100644 --- a/crates/controlfreak-installer/tests/configuration.rs +++ b/crates/controlfreak-installer/tests/configuration.rs @@ -218,6 +218,38 @@ fn malformed_ambiguous_or_wrong_type_configs_are_not_overwritten_or_logged() { } } +#[test] +fn json5_extensions_are_refused_without_changing_json_or_jsonc_configs() { + for value in [ + ".5", + "5.", + "5.e3", + "Infinity", + "-Infinity", + "NaN", + r#""synthetic\x41""#, + r#""synthetic\v""#, + r#""synthetic\0""#, + r#""synthetic\'""#, + "\"synthetic\\\ncontinued\"", + ] { + for (id, path) in [ + ("claude-code", "user/.claude.json"), + ("opencode", "user/.config/opencode/opencode.jsonc"), + ] { + let f = Fixture::new(); + let original = format!(r#"{{"synthetic-secret":{value}}}"#); + f.write(path, &original); + let result = f.configure(id, "replace"); + assert!(!result.status.success(), "accepted {value} in {path}"); + assert_eq!(fs::read_to_string(f.path(path)).unwrap(), original); + assert!(!f.path(&format!("state/{id}.json")).exists()); + assert!(!String::from_utf8_lossy(&result.stderr).contains("synthetic-secret")); + assert!(!f.report().contains("synthetic-secret")); + } + } +} + #[test] fn ambiguous_opencode_files_are_refused() { let f = Fixture::new();