Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger-ui

crates/holger-ui-server/tests/no_c_crypto.rs

//! **No openssl, no ring, no aws-lc in THIS BINARY — measured, not hoped.**
//!
//! This process ships into an appliance image. A C TLS stack in it is a
//! cross-compile problem (`x86_64-linux-musl-gcc` on every build box), a CVE
//! feed, and a second source of randomness inside the one process whose job is
//! to be the way in. The thin server speaks plain HTTP to holger on the
//! private network, and that is only worth anything if nothing re-introduces a
//! C TLS stack through a transitive edge nobody looked at.
//!
//! # Why this walks the graph instead of grepping the lock
//!
//! Written the easy way first, and it was wrong. `ring` IS in this workspace's
//! `Cargo.lock` — `ureq` pulls `rustls`, which pulls `ring` — and it is not in
//! this crate's dependency graph at all. A flat grep therefore measured the
//! WORKSPACE and reported it as a fact about the BINARY, which is the shape of
//! claim this repository writes contract tests to prevent, not to make.
//!
//! So the lock is parsed as what it is — a graph — and walked from
//! `holger-ui-server`. The answer is then the same one
//! `cargo tree -p holger-ui-server -e features | grep -iE 'openssl|ring'` gives
//! (measured 2026-09-20: empty), with no process spawned, because the LAW's
//! first line is no shelling out.
//!
//! `aws-lc-sys` is named alongside the other two: it is what `rustls` resolves
//! to by default since 0.23, and it is the edge that killed a gunnar build on
//! loki 2026-09-17 on a host with no musl C compiler.

use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::path::PathBuf;

/// Every crate name this binary must not resolve, at any depth.
const FORBIDDEN: &[&str] = &["openssl-sys", "openssl", "ring", "aws-lc-sys", "aws-lc-rs"];

/// `name -> its direct dependencies`, from `Cargo.lock`.
///
/// A deliberately small parser rather than a TOML dependency: the input is one
/// file cargo generates in a fixed shape, and the only two keys that matter are
/// `name` and the `dependencies` array.
fn lock_graph(text: &str) -> BTreeMap<String, Vec<String>> {
    let mut graph: BTreeMap<String, Vec<String>> = BTreeMap::new();
    let mut name = String::new();
    let mut deps: Vec<String> = Vec::new();
    let mut in_deps = false;

    let flush = |graph: &mut BTreeMap<String, Vec<String>>, name: &mut String, deps: &mut Vec<String>| {
        if !name.is_empty() {
            graph
                .entry(std::mem::take(name))
                .or_default()
                .append(deps);
        }
        deps.clear();
    };

    for line in text.lines() {
        let trimmed = line.trim();
        if trimmed == "[[package]]" {
            flush(&mut graph, &mut name, &mut deps);
            in_deps = false;
        } else if let Some(rest) = trimmed.strip_prefix("name = \"") {
            if !in_deps {
                name = rest.trim_end_matches('"').to_owned();
            }
        } else if trimmed.starts_with("dependencies = [") {
            in_deps = true;
        } else if in_deps {
            if trimmed == "]" {
                in_deps = false;
            } else if let Some(entry) = trimmed.strip_prefix('"') {
                // `"serde"`, `"serde 1.0.229"` or `"serde 1.0.229 (registry+…)"`.
                let entry = entry.trim_end_matches(',').trim_end_matches('"');
                if let Some(first) = entry.split_whitespace().next() {
                    deps.push(first.to_owned());
                }
            }
        }
    }
    flush(&mut graph, &mut name, &mut deps);
    graph
}

/// Everything reachable from `root`, `root` included.
fn reachable(graph: &BTreeMap<String, Vec<String>>, root: &str) -> BTreeSet<String> {
    let mut seen = BTreeSet::new();
    let mut queue = VecDeque::new();
    queue.push_back(root.to_owned());
    while let Some(next) = queue.pop_front() {
        if !seen.insert(next.clone()) {
            continue;
        }
        for dep in graph.get(&next).into_iter().flatten() {
            queue.push_back(dep.clone());
        }
    }
    seen
}

fn graph() -> BTreeMap<String, Vec<String>> {
    let lock = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("..")
        .join("..")
        .join("Cargo.lock");
    let text = std::fs::read_to_string(&lock)
        .unwrap_or_else(|e| panic!("cannot read {}: {e}", lock.display()));
    lock_graph(&text)
}

#[test]
fn this_binarys_graph_carries_no_c_crypto() {
    let graph = graph();
    let mine = reachable(&graph, "holger-ui-server");
    assert!(
        mine.contains("hyper"),
        "the walk did not reach this crate's own dependencies, so it proves nothing"
    );

    let found: Vec<&&str> = FORBIDDEN.iter().filter(|c| mine.contains(**c)).collect();
    assert!(
        found.is_empty(),
        "{found:?} is reachable from holger-ui-server. This thin server speaks plain HTTP to \
         holger on the private network and links no C TLS stack; name the edge that pulled \
         it in and close it rather than widening this list."
    );
}

/// The walk has to be able to FIND one, or the test above is green because it
/// looks nowhere. `ring` really is in this workspace — `ureq` -> `rustls` ->
/// `ring` — just not in this binary, and that distinction is the whole test.
#[test]
fn the_walk_would_find_one_if_it_were_there() {
    let graph = graph();
    let workspace_wide: BTreeSet<String> = graph.keys().cloned().collect();
    assert!(
        workspace_wide.contains("ring"),
        "if `ring` has left the workspace entirely this assertion is stale — drop it, and \
         say in the commit that the other test now has nothing to distinguish itself from"
    );
    assert!(
        !reachable(&graph, "holger-ui-server").contains("ring"),
        "the two sets are supposed to differ, and this is the crate that proves they can"
    );
}