From f6064d8751a3567d5d2dd877d3b8b68b2b5a6d68 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 26 Sep 2026 20:22:54 -0700 Subject: [PATCH] fix(config): warn about unknown keys in the configuration Keys that aren't part of the configuration were silently ignored, so a misspelled key like linux_initrd instead of linux-initrd, or menu_timeout instead of menu-timeout, quietly did nothing. Warn about each ignored key with its full path while loading the configuration. Also warn when an action or generator configures more than one kind, as only the first one found is used. --- Cargo.lock | 11 +++++ Cargo.toml | 4 ++ crates/boot/Cargo.toml | 1 + crates/boot/src/config/loader.rs | 71 ++++++++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d347cec..5435c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,7 @@ dependencies = [ "hex", "jaarg", "log", + "serde_ignored", "sha2", "toml", "uefi", @@ -237,6 +238,16 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_ignored" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index c584f8c..d2adb48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ version = "1.0.229" default-features = false features = ["alloc", "derive"] +[workspace.dependencies.serde_ignored] +version = "0.1.14" +default-features = false + [workspace.dependencies.sha2] version = "0.11.0" default-features = false diff --git a/crates/boot/Cargo.toml b/crates/boot/Cargo.toml index c86cb09..8db728b 100644 --- a/crates/boot/Cargo.toml +++ b/crates/boot/Cargo.toml @@ -15,6 +15,7 @@ edera-sprout-bls.path = "../bls" edera-sprout-parsing.path = "../parsing" hex.workspace = true jaarg.workspace = true +serde_ignored.workspace = true sha2.workspace = true toml.workspace = true log.workspace = true diff --git a/crates/boot/src/config/loader.rs b/crates/boot/src/config/loader.rs index da45b78..9d219a3 100644 --- a/crates/boot/src/config/loader.rs +++ b/crates/boot/src/config/loader.rs @@ -1,10 +1,14 @@ use crate::options::SproutOptions; +use alloc::format; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use anyhow::{Context, Result, bail}; +use core::fmt::Display; use core::ops::Deref; use edera_sprout_config::{RootConfiguration, latest_version}; use eficore::platform::tpm::PlatformTpm; -use log::info; +use log::{info, warn}; +use serde_ignored::Path; use toml::Value; use uefi::proto::device_path::LoadedImageDevicePath; @@ -35,6 +39,59 @@ fn load_raw_config(options: &SproutOptions) -> Result> { Ok(content) } +/// Format the configuration key `path` with dots, like "actions.linux.chainload.path". +fn key_path(path: &Path) -> String { + // Joins the `parent` path and the `key` with a dot, unless the parent is the root. + fn join(parent: &Path, key: impl Display) -> String { + match parent { + Path::Root => key.to_string(), + parent => format!("{}.{}", key_path(parent), key), + } + } + + match path { + Path::Root => String::new(), + Path::Seq { parent, index } => join(parent, index), + Path::Map { parent, key } => join(parent, key), + // Optional values and wrapper types don't add a key of their own. + Path::Some { parent } + | Path::NewtypeStruct { parent } + | Path::NewtypeVariant { parent } => key_path(parent), + } +} + +/// Warn about each declaration in `config` that configures more than one kind. +/// For example, an action with both chainload and print configured only runs chainload. +fn warn_multiple_kinds(config: &RootConfiguration) { + for (name, action) in &config.actions { + let kinds = [ + action.chainload.is_some(), + action.print.is_some(), + action.edera.is_some(), + ]; + if kinds.iter().filter(|kind| **kind).count() > 1 { + warn!( + "action {} configures more than one action, only one is used", + name + ); + } + } + + for (name, generator) in &config.generators { + let kinds = [ + generator.matrix.is_some(), + generator.bls.is_some(), + generator.list.is_some(), + ]; + if kinds.iter().filter(|kind| **kind).count() > 1 { + warn!( + "generator {} configures more than one generator, only one is used", + name + ); + } + } +} + /// Loads the [RootConfiguration] for Sprout. pub fn load(options: &SproutOptions) -> Result { // Load the raw configuration from the sprout config file. @@ -59,9 +116,15 @@ pub fn load(options: &SproutOptions) -> Result { } // If the version is supported, parse the full configuration. - let config: RootConfiguration = value - .try_into() - .context("unable to parse sprout.toml file")?; + // Keys that aren't part of the configuration, such as misspelled keys, are ignored. + // Warn about each one, as the configuration may not do what was intended. + let config: RootConfiguration = serde_ignored::deserialize(value, |path| { + warn!("ignoring unknown configuration key: {}", key_path(&path)); + }) + .context("unable to parse sprout.toml file")?; + + // Warn about declarations that configure more than one kind, as only one is used. + warn_multiple_kinds(&config); // Return the parsed configuration. Ok(config)