The task moves gatewayd's secret store to bxcore::secrets behind the Cargo feature `secrets` (spec, section 5). The moved module is given whole; SecretSpec and SecretSource move with it, so gatewayd's config keeps its [secrets.<name>] table with the per-secret type from bxcore. load and read_checked take the role's name first, so the three sentences that named gatewayd literally now name the role; gatewayd passes "gatewayd" and its bytes are unchanged (its tests/main.rs pins them and is not touched). A given loopd test parses the three manifests (loopd's, the workspace entry, bxcore's features) and checks the cfg line in bxcore's lib.rs; the task also checks `cargo tree -p loopd -e features` and builds loopd on its own. Checked: the reference, replayed from the task text applied mechanically to the start tree (master plus task 01), is byte for byte the reference; every before block found exactly once there. Step 2's failures recorded from that tree: ten test targets with E0432 on `bxcore::secrets`, two of four no_secrets tests. make gate on titan's toolchain: gate: ok, 963 tests (956 + 3 + 4), clippy clean, /tmp unchanged across the run. 23 one-line mutations of the reference, all caught; removing the zeroize wrapper is not observable and not counted. Given files pass fmt and the line limit; no chat-template tags. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
96 lines
3.6 KiB
Rust
96 lines
3.6 KiB
Rust
//! `loopd` holds no credentials, so no build of it enables `bxcore`'s feature `secrets` (M5b spec,
|
|
//! section 5), and its binary does not contain the code that reads them. Three manifests decide
|
|
//! that: `loopd`'s own, the workspace's entry for `bxcore`, and `bxcore`'s feature table. They are
|
|
//! parsed, so every spelling TOML allows is read. `make gate` builds the workspace with the feature
|
|
//! on (for `gatewayd`), so it cannot see a module that is not behind the feature; the last test
|
|
//! checks that line of `bxcore`'s `lib.rs`. Do not edit.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
fn manifest(path: &Path) -> toml::Table {
|
|
let text = std::fs::read_to_string(path)
|
|
.unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display()));
|
|
text.parse::<toml::Table>()
|
|
.unwrap_or_else(|e| panic!("cannot parse {}: {e}", path.display()))
|
|
}
|
|
|
|
fn crates() -> PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("..")
|
|
}
|
|
|
|
/// Every string in `value`, at any depth, that turns the feature on: `secrets`, `bxcore/secrets`
|
|
/// or `bxcore?/secrets`.
|
|
fn enabling(value: &toml::Value, found: &mut Vec<String>) {
|
|
match value {
|
|
toml::Value::String(s) if s == "secrets" || s.ends_with("/secrets") => {
|
|
found.push(s.clone())
|
|
}
|
|
toml::Value::Array(items) => items.iter().for_each(|v| enabling(v, found)),
|
|
toml::Value::Table(table) => table.values().for_each(|v| enabling(v, found)),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn loopd_never_names_the_feature() {
|
|
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
|
|
let table = manifest(&path);
|
|
assert!(table.contains_key("dependencies"), "{}", path.display());
|
|
let mut found = Vec::new();
|
|
enabling(&toml::Value::Table(table), &mut found);
|
|
assert!(found.is_empty(), "{} enables {found:?}", path.display());
|
|
}
|
|
|
|
#[test]
|
|
fn the_workspace_entry_for_bxcore_enables_nothing() {
|
|
let path = crates().join("../Cargo.toml");
|
|
let table = manifest(&path);
|
|
let entry = table
|
|
.get("workspace")
|
|
.and_then(|w| w.get("dependencies"))
|
|
.and_then(|d| d.get("bxcore"))
|
|
.unwrap_or_else(|| panic!("{}: no [workspace.dependencies] bxcore", path.display()));
|
|
assert_eq!(entry.get("features"), None, "{entry:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn only_the_feature_itself_turns_the_store_on() {
|
|
let path = crates().join("bxcore/Cargo.toml");
|
|
let table = manifest(&path);
|
|
let features = table
|
|
.get("features")
|
|
.and_then(toml::Value::as_table)
|
|
.unwrap_or_else(|| panic!("{}: no [features]", path.display()));
|
|
assert!(features.contains_key("secrets"), "{features:?}");
|
|
for (name, list) in features {
|
|
if name == "secrets" {
|
|
continue;
|
|
}
|
|
let mut found = Vec::new();
|
|
enabling(list, &mut found);
|
|
assert!(found.is_empty(), "feature {name} turns on {found:?}");
|
|
}
|
|
let zeroize = table
|
|
.get("dependencies")
|
|
.and_then(|d| d.get("zeroize"))
|
|
.unwrap_or_else(|| panic!("{}: no zeroize", path.display()));
|
|
assert_eq!(
|
|
zeroize.get("optional").and_then(toml::Value::as_bool),
|
|
Some(true),
|
|
"zeroize is built only with the feature"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_module_is_compiled_only_with_the_feature() {
|
|
let path = crates().join("bxcore/src/lib.rs");
|
|
let text = std::fs::read_to_string(&path)
|
|
.unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display()));
|
|
assert!(
|
|
text.contains("#[cfg(feature = \"secrets\")]\npub mod secrets;\n"),
|
|
"{}: `pub mod secrets;` must follow `#[cfg(feature = \"secrets\")]`",
|
|
path.display()
|
|
);
|
|
assert_eq!(text.matches("mod secrets").count(), 1, "{}", path.display());
|
|
}
|