fix: migrate off unsound serde_yml to serde_norway

serde_yml (RUSTSEC-2025-0068) and its libyml backend
(RUSTSEC-2025-0067) are archived, unsound, and unmaintained with no
patched version. Swap to serde_norway, the maintained serde_yaml fork
on unsafe-libyaml-norway — a drop-in for our from_str / to_string /
Value usage across persistence, undo, and the catalog parser.

Clears both advisories (cargo audit / osv-scanner / grype all clean;
serde_yml + libyml gone from the tree). No behaviour change; full
suite 2151/0/1.
This commit is contained in:
claude@clouddev1
2026-06-02 14:34:34 +00:00
parent c9a92c9c20
commit 9e2372b039
6 changed files with 27 additions and 30 deletions
+5 -5
View File
@@ -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<String, String>,
) {
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:?}"),
}
}
+1 -1
View File
@@ -281,7 +281,7 @@ fn read_version(body: &str) -> Result<u32, MigrateError> {
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)
+4 -4
View File
@@ -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<SchemaSnapshot, YamlError> {
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<SchemaSnapshot, YamlError> {
/// unparseable body for the same reason.
#[must_use]
pub(super) fn parse_stored_mode(body: &str) -> Option<Mode> {
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)
}
+2 -2
View File
@@ -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();