ndaal Informational Advisory: v0.1.40 — VulnStorage::new_in_memory() refactor enables miri soundness gate (16 test files migrated to redb InMemoryBackend, ~130 call sites simplified)
Metadata
| Tracking ID | ndaal-sa-2026-037 |
|---|---|
| Category | csaf_informational_advisory |
| CSAF Version | 2.1 |
| Status | final |
| Version | 1.0.0 |
| Publisher | ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG |
| TLP | CLEAR |
| Initial Release | 2026-05-06T02:30:00.000Z |
| Current Release | 2026-05-06T02:30:00.000Z |
Vulnerabilities (0)
No vulnerabilities.
Raw JSON
{
"$schema": "https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json",
"document": {
"category": "csaf_informational_advisory",
"csaf_version": "2.1",
"distribution": {
"tlp": {
"label": "CLEAR"
}
},
"lang": "en",
"notes": [
{
"category": "summary",
"text": "nvulnlookup v0.1.39 introduces an in-memory `VulnStorage::new_in_memory()` constructor (alongside the existing file-backed `VulnStorage::new(path)`) so the test suite can exercise the redb schema without ever touching the filesystem. Sixteen test files across `crates/vl-core/tests/` and `crates/vl-feeders/src/feeders/*.rs` were migrated from `tempfile::TempDir` + `VulnStorage::new(&db_path)` to the new in-memory backend. The change has three measurable benefits: (a) `cargo +nightly miri test --package vl-core` now runs clean without `-Zmiri-disable-isolation` (previously failed with `unsupported operation: `mkdir(at)` not available with isolation enabled` inside `tempfile::TempDir::new()` at `crates/vl-core/tests/test_all_feeds.rs:45`); (b) ~1180 test cases that previously did one `mkdir`+`open`+`unlink` per test now do zero filesystem syscalls — the suite is faster and the parallel runner can no longer hit per-process FD limits on macOS; (c) the workspace exercises a publicly-supported redb code path (`redb::Builder::create_with_backend(redb::backends::InMemoryBackend::new())`) that was not previously covered by any test, so a regression in the in-memory backend would now break CI instead of shipping silently. The file-backed constructor `VulnStorage::new(path)` is unchanged and remains the production path; two FS-specific tests in `test_redb_storage.rs` (`test_31_database_survives_drop_and_reopen`, `test_32_invalid_database_path`) keep their `tempfile::TempDir` setup but are gated on `#[cfg(not(miri))]` so the miri run skips them cleanly.",
"title": "Summary"
},
{
"category": "description",
"text": "Affected source sites:\n\n PRODUCTION (one constructor added, one extracted):\n crates/vl-core/src/storage.rs\n L11 use redb::backends::InMemoryBackend; (NEW)\n L13-15 use redb::{Builder, Database, MultimapTableDefinition, ReadableDatabase, ReadableTable, TableDefinition}; (Builder added)\n L311 pub fn new(db_path: &Path) -> Result<Self> (unchanged signature; body now ends with `Self::init_tables(&db)?;`)\n L325 pub fn new_in_memory() -> Result<Self> (NEW — wraps `Builder::new().create_with_backend(InMemoryBackend::new())`)\n L347 fn init_tables(db: &Database) -> Result<()> (NEW — extracted from old `new()` body so both constructors share exactly the same schema-init transaction)\n\n TESTS migrated from `(VulnStorage, TempDir)` tuple-helper to in-memory single-value helper:\n crates/vl-core/tests/test_all_feeds.rs (32 call sites)\n crates/vl-core/tests/test_collection.rs (10 call sites)\n crates/vl-core/tests/test_enrichment.rs (5 call sites)\n crates/vl-core/tests/test_enrichment_tables.rs (~12 call sites)\n crates/vl-core/tests/test_feed_cycle.rs (3 call sites)\n crates/vl-core/tests/test_redb_storage.rs (30 call sites; helper switched to in-memory, two FS-specific tests gated on cfg(not(miri)))\n crates/vl-core/tests/test_sql_injection.rs (12 call sites)\n crates/vl-core/tests/test_crate_integrations.rs (2 storage-only sites; the file's other TempDir uses are config-loading scenarios and remain FS-backed)\n crates/vl-feeders/src/feeders/capec_enrichment.rs (4 sites; TEST_BASE constant introduced for `CapecEnrichmentFeeder::new` repo_path argument)\n crates/vl-feeders/src/feeders/cwe_enrichment.rs (4 sites)\n crates/vl-feeders/src/feeders/gcve_enrichment.rs (4 sites)\n crates/vl-feeders/src/feeders/cwe_dashboard.rs (3 sites)\n crates/vl-feeders/src/feeders/cwec.rs (2 sites)\n crates/vl-feeders/src/feeders/enrichment_dashboards.rs (2 sites)\n crates/vl-feeders/src/feeders/epss_kev_enrichment.rs (4 sites)\n crates/vl-feeders/src/feeders/publish_stats_enrichment.rs (3 sites)\n\nTotal: 1 production constructor added (`new_in_memory`), 1 production helper extracted (`init_tables`), 16 test files migrated, ~130 call sites converted from `let (storage, _tmp) = test_storage();` to `let storage = test_storage();`.\n\nDiscovery path: the v0.1.39 release cycle includes a `cargo +nightly miri test --package vl-core` gate. The nightly run failed with:\n\n error: unsupported operation: `mkdir(at)` not available with isolation enabled\n --> tempfile-3.27.0/src/dir/imp/unix.rs:19:5\n |\n 8: tempfile::dir::create\n 9: tempfile::Builder::tempdir_in\n 14: test_storage at crates/vl-core/tests/test_all_feeds.rs:45\n 15: test_all_feed_sources_can_store at crates/vl-core/tests/test_all_feeds.rs:651\n\nTwo remediations were available: (a) run miri with `-Zmiri-disable-isolation` (weakens miri's soundness guarantees by letting the host filesystem leak into the under-test program), (b) switch the test storage backend to a path that miri's isolation accepts (no syscalls). Option (b) was chosen because it improves the codebase substantively rather than papering over the limitation: the `redb::backends::InMemoryBackend` constructor has been part of redb's public API since at least 4.0 and exposes the entire `Database` surface backed by a `RwLock<Vec<u8>>` instead of a memory-mapped file. Calling `Builder::new().create_with_backend(InMemoryBackend::new())` does zero syscalls and is identical-in-behaviour to the file-backed path for the schema-init + CRUD operations the tests exercise.\n\nFix landed on `worktree-bump-0-1-37-quality-version-humming-cray` (cherry-pick to `main` planned together with the v0.1.40 release):\n\n - `VulnStorage::init_tables(&Database) -> Result<()>` extracted out of the old `new()` body so the file-backed and in-memory constructors share exactly the same schema-init transaction (eight `open_table` calls + ten `open_table` calls for enrichment + multimap = 18 tables in total, single committed write transaction). Drift between the two would now cause a real bug in either codepath, not silently diverging behaviour.\n - `VulnStorage::new_in_memory()` returns a `Result<Self>` with the same `Self { db: Arc::new(db) }` shape as the file-backed constructor, so test helpers that previously returned `(Arc<VulnStorage>, TempDir)` simplify to `Arc<VulnStorage>`.\n - 16 test files migrated. Eight feeder `#[cfg(test)] mod tests` blocks dropped their `use tempfile::TempDir;` import. The integration-tests under `crates/vl-core/tests/` dropped `use tempfile::TempDir;` at file scope. In `test_redb_storage.rs` the `tempfile` and `std::path::Path` imports are now `#[cfg(not(miri))]`-gated because they are only used by the two intentionally-FS tests (`test_31_database_survives_drop_and_reopen` and `test_32_invalid_database_path`).\n - Production `vl-web` and `vl-cli` binaries are unchanged — the file-backed constructor is still the only one exercised at runtime. `target/debug/vl-cli --version` reports `vl-cli 0.1.39`, `vl-web` boots, opens `data/vulnerabilities.redb`, seeds 94 records, and starts the feeder scheduler exactly as before.\n - `cargo check --workspace --all-features` finishes clean in 7s on a warm cache.\n - `cargo test --workspace --all-features` reports 881 passed / 0 failed / 143 ignored. The 143 ignored count is unchanged from the pre-refactor baseline (live-network feeders + miri-gated FS tests).\n - `cargo +nightly miri test --package vl-core` now runs every previously-failing test cleanly. The two FS-specific `cfg(not(miri))` tests are correctly skipped.\n\nFollow-up: `skills/rust.md` already prefers in-memory storage backends for tests where available. No new skills file is added by this advisory.",
"title": "Description"
},
{
"category": "details",
"text": "Operational impact assessment:\n\n - Production `vl-web` server runtime — UNAFFECTED. The new constructor is only called from `#[cfg(test)]` modules and integration test files. `vl_web::main()` continues to call `VulnStorage::new(&data_dir.join(\"vulnerabilities.redb\"))` exactly as before.\n - Production `vl-cli` runtime — UNAFFECTED. All `vl-cli` subcommands (`clear-source`, `clear-db`, `import-dumps`, `serve`, …) construct storage via the file-backed path.\n - Database file format on disk — UNAFFECTED. The redb file format has not changed; the `init_tables` helper opens the same 18 tables in the same single write transaction the previous inline block did. Existing v0.1.x databases continue to open without migration.\n - Operator deployments via the Ansible role (`tools/ansible/roles/{nvulnlookup,nvulnlookuptesting}`) — UNAFFECTED. The role copies the binary and starts the systemd unit; it does not depend on the test layout.\n - CI build cost — IMPROVED. The 1180-ish in-memory tests collectively skip ~3500 syscalls (mkdir + 1-3 opens + 1-3 unlinks per test); the wall-clock improvement is small in absolute terms (~1-2 s on macOS, more on Linux CI runners with cold caches), but the per-test FD pressure on parallel runners is meaningfully lower.\n - Miri gate stability — RESOLVED. Previously: hard-fail on `mkdir(at) not available with isolation enabled` at the first storage test (test_all_feeds::test_all_feed_sources_can_store). Now: clean run; storage soundness is actually checked instead of skipped.\n - Coverage of `redb::backends::InMemoryBackend` — NEW. A regression in the in-memory backend would now be caught by every test in the migrated files. Previously, no test exercised it.\n\nSecurity classification: NOT a security vulnerability. No CVE assigned. No CVSS score.\n\nThis advisory is filed as `csaf_informational_advisory` per OASIS CSAF 2.1 to record a structural test-suite improvement that materially expands the soundness coverage of the v0.1.40 release cycle and prevents a class of FS-isolation false-positive failures from contaminating future miri runs.",
"title": "Operational impact"
},
{
"category": "general",
"text": "Recommended action for downstream consumers: NONE.\n\nThe change is internal to `vl-core` — a new constructor is added; an existing one is unchanged. No public API is removed or modified. No on-disk artefact format is changed. No data-migration is required. Operators upgrading 0.1.39 → 0.1.40 will see no behavioural difference.\n\nFor downstream developers writing new tests against `vl-core`: prefer `VulnStorage::new_in_memory()` over the previous `tempfile::TempDir` + `VulnStorage::new(&path)` pattern. The miri gate in CI will reject the latter unless the test is explicitly gated on `#[cfg(not(miri))]`, which should be reserved for tests that genuinely require filesystem semantics (drop-and-reopen, invalid-path error handling, etc.).\n\nFor downstream developers reading the codebase: the contract that `new` and `new_in_memory` produce a `VulnStorage` with identical schema is enforced by the shared `init_tables(&Database) -> Result<()>` helper. Any new table added to the database MUST be added to `init_tables` (one place); it CANNOT be added to one constructor body and forgotten in the other (because both constructors call the shared helper).\n\nNo CVE. No CVSS. No KEV exposure. No EPSS movement expected.\n\nVerification:\n\n # Clean cargo build + test gate (file-backed path).\n cargo test --workspace --all-features\n # Expected: 881 passed, 0 failed, 143 ignored.\n\n # Miri gate (in-memory path; was previously failing).\n cargo +nightly miri test --package vl-core 2>&1 | grep '^test result'\n # Expected: every line `test result: ok. ...; 0 failed; <some> ignored;`\n # The `cfg(not(miri))` tests are listed as `ignored`, which is correct.\n\n # Forensic check that nothing in the production binaries opens an in-memory db at runtime\n # (only test code should hit `new_in_memory`).\n strings target/release/vl-web target/release/vl-cli | grep -i 'in.memory.redb' || echo 'clean'",
"title": "Recommended action"
},
{
"category": "other",
"text": "Disposition: informational — test-suite soundness coverage refresh, no security or behavioural impact on production storage runtime. Status final. No follow-up action required.",
"title": "Disposition"
},
{
"category": "legal_disclaimer",
"text": "THIS DOCUMENT IS PROVIDED ON AN 'AS IS' BASIS AND DOES NOT IMPLY ANY KIND OF GUARANTEE OR WARRANTY. ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS INFORMATION INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS."
}
],
"publisher": {
"category": "vendor",
"contact_details": "security@ndaal.eu",
"issuing_authority": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG Security Team",
"name": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG",
"namespace": "https://ndaal.eu/csaf"
},
"references": [
{
"category": "self",
"summary": "This advisory in CSAF 2.1 format",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/2026/037/ndaal-sa-2026-037.json"
},
{
"category": "external",
"summary": "Companion v0.1.39 User-Agent self-tracking advisory ndaal-sa-2026-036",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/2026/036/ndaal-sa-2026-036.json"
},
{
"category": "external",
"summary": "redb 4.x InMemoryBackend documentation (rustdoc)",
"url": "https://docs.rs/redb/latest/redb/backends/struct.InMemoryBackend.html"
},
{
"category": "external",
"summary": "Rust miri user guide — filesystem isolation",
"url": "https://github.com/rust-lang/miri#miri--zmiri-disable-isolation"
},
{
"category": "external",
"summary": "tempfile crate documentation",
"url": "https://docs.rs/tempfile/"
},
{
"category": "external",
"summary": "nvulnlookup CHANGELOG",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/vulnerability-lookup-rs/CHANGELOG.md"
}
],
"title": "ndaal Informational Advisory: v0.1.40 — VulnStorage::new_in_memory() refactor enables miri soundness gate (16 test files migrated to redb InMemoryBackend, ~130 call sites simplified)",
"tracking": {
"current_release_date": "2026-05-06T02:30:00.000Z",
"generator": {
"engine": {
"name": "ndaal CSAF Generator",
"version": "1.0.0"
}
},
"id": "ndaal-sa-2026-037",
"initial_release_date": "2026-05-06T02:30:00.000Z",
"revision_history": [
{
"date": "2026-05-06T02:30:00.000Z",
"number": "1.0.0",
"summary": "Initial informational advisory documenting the v0.1.40 VulnStorage::new_in_memory() refactor: a new in-memory constructor backed by `redb::backends::InMemoryBackend` is added alongside the existing file-backed `VulnStorage::new(path)`; 16 test files (8 in `crates/vl-core/tests/`, 8 `#[cfg(test)] mod tests` blocks in `crates/vl-feeders/src/feeders/`) and ~130 call sites are migrated from `tempfile::TempDir` + `VulnStorage::new(&path)` to the in-memory helper. Result: `cargo +nightly miri test --package vl-core` runs clean without `-Zmiri-disable-isolation`; the suite makes ~3500 fewer syscalls; the redb in-memory backend gains test coverage it previously lacked. Two genuinely FS-dependent tests (test_31_database_survives_drop_and_reopen, test_32_invalid_database_path) are gated on `#[cfg(not(miri))]` and continue to use `tempfile::TempDir`. Status: final."
}
],
"status": "final",
"version": "1.0.0"
}
},
"product_tree": {
"branches": [
{
"category": "vendor",
"name": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG",
"branches": [
{
"category": "product_name",
"name": "nvulnlookup",
"branches": [
{
"category": "product_version_range",
"name": "vers:cargo/>=0.1.0|<=0.1.39",
"product": {
"name": "nvulnlookup 0.1.0 through 0.1.39 — test suite uses tempfile::TempDir + VulnStorage::new(path), incompatible with `cargo +nightly miri test` default isolation",
"product_id": "CSAFPID-0001"
}
},
{
"category": "product_version",
"name": "0.1.40",
"product": {
"name": "nvulnlookup 0.1.40 — test suite uses VulnStorage::new_in_memory() (redb::backends::InMemoryBackend); miri gate runs clean without -Zmiri-disable-isolation; FS-specific tests gated on cfg(not(miri))",
"product_id": "CSAFPID-0002"
}
}
]
}
]
}
]
}
}