diff --git a/Cargo.lock b/Cargo.lock index 8cb4a0e..78a8c41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -908,16 +908,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "libyml" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980" -dependencies = [ - "anyhow", - "version_check", -] - [[package]] name = "line-clipping" version = "0.3.7" @@ -1545,7 +1535,7 @@ dependencies = [ "ratatui", "rusqlite", "serde", - "serde_yml", + "serde_norway", "sysinfo", "tempfile", "tokio", @@ -1718,18 +1708,16 @@ dependencies = [ ] [[package]] -name = "serde_yml" -version = "0.0.12" +name = "serde_norway" +version = "0.9.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" +checksum = "e408f29489b5fd500fab51ff1484fc859bb655f32c671f307dcd733b72e8168c" dependencies = [ "indexmap", "itoa", - "libyml", - "memchr", "ryu", "serde", - "version_check", + "unsafe-libyaml-norway", ] [[package]] @@ -2192,6 +2180,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unsafe-libyaml-norway" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39abd59bf32521c7f2301b52d05a6a2c975b6003521cbd0c6dc1582f0a22104" + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index d9c8c53..a42d918 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,10 @@ rand = "0.10.1" ratatui = "0.30.0" rusqlite = { version = "0.39.0", features = ["backup", "bundled", "column_metadata"] } serde = { version = "1.0.228", features = ["derive"] } -serde_yml = "0.0.12" +# Maintained fork of `serde_yaml` (the deprecated original). Replaces +# `serde_yml`, which was archived as unsound + unmaintained +# (RUSTSEC-2025-0068, and its `libyml` backend RUSTSEC-2025-0067). +serde_norway = "0.9.42" sysinfo = { version = "0.39.0", default-features = false, features = ["system"] } tokio = { version = "1.52.2", features = ["full"] } tracing = "0.1.44" diff --git a/src/friendly/format.rs b/src/friendly/format.rs index ca3c338..7e7192b 100644 --- a/src/friendly/format.rs +++ b/src/friendly/format.rs @@ -23,7 +23,7 @@ pub struct Catalog { impl Catalog { fn load() -> Self { - let value: serde_yml::Value = serde_yml::from_str(EN_US) + let value: serde_norway::Value = serde_norway::from_str(EN_US) .expect("embedded en-US.yaml must parse (ADR-0019 §8.6 startup check)"); let mut entries = HashMap::new(); flatten(&value, String::new(), &mut entries); @@ -44,12 +44,12 @@ impl Catalog { } fn flatten( - value: &serde_yml::Value, + value: &serde_norway::Value, prefix: String, out: &mut HashMap, ) { match value { - serde_yml::Value::Mapping(map) => { + serde_norway::Value::Mapping(map) => { for (k, v) in map { let k_str = k .as_str() @@ -62,14 +62,14 @@ fn flatten( flatten(v, next, out); } } - serde_yml::Value::String(s) => { + serde_norway::Value::String(s) => { out.insert(prefix, s.clone()); } // Empty top-level (Null) is fine — an empty catalog // loads as no entries. Anything else is a structure // error worth panicking over since the catalog is // shipped with the binary. - serde_yml::Value::Null if prefix.is_empty() => {} + serde_norway::Value::Null if prefix.is_empty() => {} other => panic!("catalog value at `{prefix}` is not a string: {other:?}"), } } diff --git a/src/persistence/migrations.rs b/src/persistence/migrations.rs index 4099f71..9ea3810 100644 --- a/src/persistence/migrations.rs +++ b/src/persistence/migrations.rs @@ -281,7 +281,7 @@ fn read_version(body: &str) -> Result { struct VersionOnly { version: u32, } - let v: VersionOnly = serde_yml::from_str(body).map_err(|e| { + let v: VersionOnly = serde_norway::from_str(body).map_err(|e| { MigrateError::VersionParse(e.to_string()) })?; Ok(v.version) diff --git a/src/persistence/yaml.rs b/src/persistence/yaml.rs index 1dfef61..4e72718 100644 --- a/src/persistence/yaml.rs +++ b/src/persistence/yaml.rs @@ -1,5 +1,5 @@ //! `project.yaml` writer (hand-rolled, ADR-0015 §3) and -//! reader (`serde_yml`, ADR-0015 §7). +//! reader (`serde_norway`, ADR-0015 §7). //! //! The schema YAML uses a small, fixed set of structures — //! tables, columns, relationships — and the values it carries @@ -7,7 +7,7 @@ //! the fixed `Type` enum, action names from `ReferentialAction`). //! Hand-rolling the writer avoids pulling a YAML serializer //! dep just for the write path; the read path uses -//! `serde_yml` because we need to handle whatever the user +//! `serde_norway` because we need to handle whatever the user //! (or a future migrator, or a hand-edit) puts in there. // // `pub(crate)` items in this private submodule are @@ -268,7 +268,7 @@ const fn is_safe_yaml_char(c: char) -> bool { /// fatal banner per ADR-0015 §8. pub(crate) fn parse_schema(body: &str) -> Result { let raw: RawProject = - serde_yml::from_str(body).map_err(|e| YamlError::Syntax(e.to_string()))?; + serde_norway::from_str(body).map_err(|e| YamlError::Syntax(e.to_string()))?; if raw.version != 1 { return Err(YamlError::UnsupportedVersion(raw.version)); } @@ -351,7 +351,7 @@ pub(crate) fn parse_schema(body: &str) -> Result { /// unparseable body for the same reason. #[must_use] pub(super) fn parse_stored_mode(body: &str) -> Option { - let raw: RawProject = serde_yml::from_str(body).ok()?; + let raw: RawProject = serde_norway::from_str(body).ok()?; raw.project.mode.as_deref().and_then(Mode::from_keyword) } diff --git a/src/undo.rs b/src/undo.rs index 58a5fa8..a2df9fa 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -412,7 +412,7 @@ impl SnapshotStore { let path = self.index_path(); let mut index = if path.exists() { let body = fs::read_to_string(&path).map_err(|e| io_err("read index", &path, e))?; - serde_yml::from_str(&body).map_err(|e| SnapshotError::Index { + serde_norway::from_str(&body).map_err(|e| SnapshotError::Index { message: e.to_string(), })? } else { @@ -437,7 +437,7 @@ impl SnapshotStore { fn save_index(&self, index: &Index) -> Result<()> { create_dir_all(&self.root)?; - let body = serde_yml::to_string(index).map_err(|e| SnapshotError::Index { + let body = serde_norway::to_string(index).map_err(|e| SnapshotError::Index { message: e.to_string(), })?; let path = self.index_path();