src/lib.rs
//! # Móðguðr
//!
//! *Móðguðr (Modgud) guards **Gjallarbrú**, the bridge over the river Gjöll on the
//! road to Hel — she challenges everyone who tries to cross and demands to know who
//! they are.* This crate is that gatekeeper for the nordisk artifact world.
//!
//! It lives in its own leaf repo so **holger** and **nornir** can both depend on it
//! without forming a cycle (holger needs verdicts; nornir owns the security plane).
//!
//! Two engines, sharply split (design: `nornir/.nornir/modgunn-security-scanner.md`):
//! - [`scan`] — **Engine A**, the artifact security *verdict* (multi-ecosystem
//! CVE / license / provenance). Lean; holger's policy gate + static-holger
//! quarantine consume its [`core::Verdict`].
//! - [`scip`] — **Engine B**, the SCIP whole-world dep-tree *behaviour-delta* +
//! tripwires / graph-algorithms / anomaly detection. Heavy (128GB–1TB RAM);
//! dynamic plane only.
//!
//! `**` in the modules marks surface that is **being implemented**.
/// modgunn's semantic (crate) version — `env!("CARGO_PKG_VERSION")`.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// The 7-char git short-hash the build was cut from, stamped by `build.rs` reading
/// the on-disk git plumbing (pure-FS, zero-shell). `"unknown"` when no git metadata
/// is present at build time.
pub const GIT_HASH: &str = env!("MODGUNN_GIT_HASH");
/// The ONE identity string `modgunn --version`, the About card, and the
/// `about --json` oracle all share: `"<semver> (<git-hash>)"` (build-version-githash
/// -law). Matches the suite shape (`korp v0.1.0 (7f96892)`), so a `--version` line and
/// the About readout can never disagree about which build is running.
pub fn version_with_hash() -> String {
format!("{VERSION} ({GIT_HASH})")
}
pub mod core;
/// **About surface** (always compiled) — modgunn's identity card in the constellation-
/// wide COMMON FRAMEWORK (the JetBrains-style shared shell). Pure identity data
/// (serde_json only), so even the lean `scan` build answers `modgunn about`. See
/// [`about`].
pub mod about;
/// **🧰 Tools menu** (feature `framework`) — a menu of **jera**-backed demo actions
/// (start a container / generate demo advisory data THROUGH `jera::container::
/// ContainerSpec` run as a tracked job — never a bespoke docker call or thread). The
/// CONSUMED shared runtime, not a twin (L5). Live spawn behind `framework-oci`. See
/// [`tools`].
#[cfg(feature = "framework")]
pub mod tools;
/// **Service lifecycle** (feature `framework`) — modgunn's world-delta gatekeeper
/// declared as a **skidbladnir** `service::ServiceSpec` driven through the shared
/// `Lifecycle` (the ONE systemd renderer + a dedicated `modgunn` system user), not a
/// hand-rolled unit (L5). See [`service`].
#[cfg(feature = "framework")]
pub mod service;
/// **Introspection / emit marker** — record one functional-status row for the
/// nornir test-matrix warehouse. Thin wrapper over
/// `nornir_testmatrix::functional_status`, gated behind the `testmatrix` feature
/// (a compiled-out `#[inline]` no-op with no nornir dep when off). `component` is
/// the reporting surface (e.g. `"modgunn/engine-a-osv-scanner"`), `check` what it
/// verified, `ok` the verdict, `detail` a short human note. modgunn's functional
/// surfaces (the `warehouse` CLI verbs, Engine A `OsvScanner`, Engine B `scip`
/// diff, the Iceberg writer) call this so `nornir test` SEES each surface.
#[inline]
pub fn functional_status(component: &str, check: &str, ok: bool, detail: &str) {
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status(component, check, ok, detail);
#[cfg(not(feature = "testmatrix"))]
{
let _ = (component, check, ok, detail);
}
}
#[cfg(feature = "scan")]
pub mod scan;
/// **Engine A / Engine B** (feature `scan`): one [`engine::AdvisoryEngine`] contract,
/// two swappable advisory-matching strategies — [`engine::LinearEngine`] (A, the
/// shipped linear scan) and [`engine::IndexedEngine`] (B, a `(ecosystem, package)`
/// inverted index). Both emit byte-identical [`core::Verdict`]s; they differ only in
/// fleet-scan cost. Pick one at runtime via [`engine::EngineKind`] / [`engine::build`].
/// See [`engine`].
#[cfg(feature = "scan")]
pub mod engine;
/// OSV-format advisory import (feature `scan`): parse an offline OSV/GHSA/RUSTSEC
/// snapshot into [`scan::Advisory`]s for the `cve_advisories` warehouse table.
#[cfg(feature = "scan")]
pub mod osv;
/// Per-ecosystem version ordering + OSV range→exact expansion (feature `scan`):
/// the front-end that turns OSV `affected[].ranges` into the exact versions Engine
/// A's [`scan::Advisory`] matches against. See [`versions`].
#[cfg(feature = "scan")]
pub mod versions;
/// Exploit-intelligence + malicious-package feed import (feature `scan`): EPSS
/// (FIRST), KEV (CISA) and the OpenSSF Malicious Packages feed, enriching /
/// producing [`scan::Advisory`]s. See [`feeds`].
#[cfg(feature = "scan")]
pub mod feeds;
/// Baddie-detection **verdict semantics** (feature `scan`): the ONE implementation of
/// the b5 refuse/hold/act trichotomy + the [`baddie::BaddieAxis`] that fired and the
/// evidence-ladder rank. Reuses [`core::Decision`] rather than twinning a second
/// three-way enum. modgunn decides *whether a node is a baddie*; nornir's Mímir lens
/// only maps the stored verdict → colour. See [`baddie`] and
/// `.nornir/baddie-detection-design.md`.
#[cfg(feature = "scan")]
pub mod baddie;
/// Provenance verification hooks (feature `scan`): a sigstore/SLSA verify trait +
/// an OSSF Scorecard signal struct feeding the A3/A14/A24 checks. See [`provenance`].
#[cfg(feature = "scan")]
pub mod provenance;
/// GitLab **gemnasium** advisory import (feature `scan`): parse GitLab's advisory-DB
/// YAML (per-advisory) into a [`scan::Advisory`] — a `affected_range` constraint
/// grammar → range disjunction + a flat-YAML line-scanner (no `yaml` dep). See
/// [`gitlab`].
#[cfg(feature = "scan")]
pub mod gitlab;
/// Threat-intelligence **IOC** import (feature `scan`): abuse.ch URLhaus / Feodo /
/// SSLBL feeds → malicious URL / C2 IP / bad-hash [`ioc::Ioc`] indicators for the
/// shared `ioc_indicators` warehouse table (the garm-side signal modgunn owns for
/// both dwarves and garm). See [`ioc`].
#[cfg(feature = "scan")]
pub mod ioc;
/// Registry **yank / deprecation** feed import (feature `scan`): a crates.io yanked
/// version / an npm deprecated version is a CVE-less supply-chain "stop depending on
/// this" signal. Parses each into a `MODGUNN-YANK-*` / `MODGUNN-DEPRECATED-*`
/// [`scan::Advisory`] that lands in the SAME `cve_advisories` table — feeding the
/// Mímir yank-cascade join. See [`registry`].
#[cfg(feature = "scan")]
pub mod registry;
/// Package **behaviour / metadata features** for the A23 malicious-package model
/// (feature `scan`): fold an npm `package.json`'s install-lifecycle scripts into a
/// cheap, offline, metadata-only feature vector — install-script hooks, network-in-
/// install (dropper stage-2 fetch), `# URLs`, install-script Shannon entropy
/// (obfuscation, reusing [`core::shannon_entropy`]), and the sensitive-API set —
/// whose [`behavior::PackageBehavior::findings`] feed the verdict surface. The
/// read-side feature extractor (the `feeds::parse_*` shape). See [`behavior`].
#[cfg(feature = "scan")]
pub mod behavior;
/// NVD CVE JSON 2.0 import (feature `scan`): CVSS-vector + CWE + severity
/// enrichment keyed by CVE id (NVD is enrichment, not the package-match authority).
/// See [`nvd`].
#[cfg(feature = "scan")]
pub mod nvd;
/// CSAF VEX import (feature `scan`): consumer `known_not_affected` suppressions —
/// the ids a scan drops to kill unreachable-CVE noise. See [`vex`].
#[cfg(feature = "scan")]
pub mod vex;
/// SPDX **license list** import (feature `scan`): the official SPDX license-id
/// registry (`licenses.json`) — the license/compliance leg. Validates a declared
/// license id is real and flags SPDX-**deprecated** ids, reusing the gate's SPDX
/// expression parser. Reference data (no warehouse table); drives `license-lint`.
/// See [`spdx`].
#[cfg(feature = "scan")]
pub mod spdx;
/// CycloneDX 1.5 SBOM **emit** (feature `scan`): serialize a resolved component +
/// vuln set into a standards SBOM (multi-ecosystem purls). The other direction
/// from [`warehouse`]'s SBOM read. See [`sbom`].
#[cfg(feature = "scan")]
pub mod sbom;
/// Multi-ecosystem manifest / lockfile **ingest** (feature `manifests`): parse a
/// project's dependency manifest/lockfile (Cargo.lock, requirements.txt,
/// package-lock.json, pom.xml, go.mod, packages.lock.json, Gemfile.lock, …) into
/// the SAME [`sbom::SbomComponent`] model this crate emits — one
/// [`manifests::EcosystemParser`] seam, one parser per ecosystem. Pure-Rust,
/// zero-shell. The read-side counterpart to [`sbom`]'s emit. See [`manifests`].
#[cfg(feature = "manifests")]
pub mod manifests;
/// CAPEC → ATT&CK **mapping** import (feature `manifests`, for roxmltree): MITRE
/// CAPEC XML bridges `Related_Weaknesses` (CWE) and `Taxonomy_Mappings` (ATT&CK)
/// into a CWE → technique map, so a finding's CWE(s) tag it with adversary
/// techniques for the red graph. Reference data (no table, no hot path); drives
/// `attack-map`. See [`capec`].
#[cfg(feature = "manifests")]
pub mod capec;
/// **Aggregate / rollup views** — pure higher-level folds (fleet severity histogram,
/// license/ecosystem distribution, per-repo health, fleet rollup) over the scan +
/// advisory data, so a consumer can see modgunn at altitude. No warehouse/network;
/// the `warehouse` layer maps its `FleetFinding`s in. See [`rollup`].
pub mod rollup;
#[cfg(feature = "scip")]
pub mod scip;
/// Iceberg warehouse access **over skade only** (feature `warehouse`, off by
/// default). modgunn reads nornir's `sbom_components` table
/// and owns its own (`cve_advisories`, `world_snapshots`, `behavior_deltas`,
/// `tripwire_events`) in the shared `nornir` namespace — never depending on
/// nornir. Includes the skade-backed CDC seam Engine B polls. See [`warehouse`].
#[cfg(feature = "warehouse")]
pub mod warehouse;
/// Test-only serialization guard for the **process-global** `nornir_testmatrix`
/// functional-row buffer (`OnceLock<Mutex<Vec<_>>>`). Every modgunn test that does
/// the emit-then-drain dance (`drain` to clean → run the parser → `drain` to assert
/// the functional row landed) shares that one buffer; run in parallel, a second
/// test's `drain` steals the first test's rows and the assert-drain comes back empty
/// → nondeterministic RED. `nornir_testmatrix` guards its OWN such tests with an
/// internal `test_lock()`, but that is `pub(crate)` to the crate, so modgunn needs
/// its own. Every draining test in `behavior`/`provenance` takes THIS lock for the
/// whole test body so no two drains interleave. Poison-tolerant (an asserting test
/// panics while holding it; the next taker recovers the guard). Compiled only under
/// `test` + `testmatrix` — zero cost otherwise, matching the emitter's own gating.
#[cfg(all(test, feature = "testmatrix"))]
pub(crate) fn testmatrix_test_guard() -> std::sync::MutexGuard<'static, ()> {
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
}