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

nordisk/holger-ui

browser-tests/tests/security_page.rs

//! ★ **The Security page shows security, not the machinery** (lane HOL-SEC, owner 2026-10-27:
//! "Security must show security, not the machinery again"). A holger declaring sources (an
//! upload door, a pulled index, a mirrored upstream holger) and one downstream, its console in
//! a real browser: the Security page shows the gateway as holger words it
//! (`AdminService.SecuritySummary`): the intake chain, each source and what is checked for it,
//! the scanners and where scans run, who may read, the key that signs the sync and the
//! downstream, the last security events; and NOT the estate board, which is the Overview's.
//! The Admin page links to the board instead of drawing it.

use holger_ui_browser_tests::browser::{Browser, Tab, NEVER};
use holger_ui_browser_tests::rig::{Rig, CONSOLE, GRAPH_DOWNSTREAM};

async fn register_owner(tab: &Tab) {
    tab.until("the bootstrap door", "!document.getElementById('reg').hidden").await;
    tab.fill("#token", CONSOLE).await;
    tab.js("document.getElementById('role').value = 'admin'").await.unwrap();
    tab.until("the register button", "!!document.getElementById('register')").await;
    tab.js("document.getElementById('reg').open = true").await.unwrap();
    tab.fill("#user", "owner").await;
    tab.click("#register").await;
    tab.until("registered", holger_ui_browser_tests::browser::SIGNED_IN_OR_REGISTERED).await;
    if !tab.signed_in().await {
        // A real press: the sign-in starts its passkey only inside the person's gesture (RA2-6).
        tab.press("#login").await;
        tab.until("holger's session in this tab", holger_ui_browser_tests::browser::SIGNED_IN).await;
    }
}

/// The text of the section the server keyed `key`.
async fn section(tab: &Tab, key: &str) -> String {
    let v = tab.until(&format!("the {key} section"), &format!("(document.querySelector('[data-section={key}]') || {{}}).innerText || ''")).await;
    v.as_str().unwrap().to_owned()
}

#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn the_security_page_shows_the_gateway_and_not_the_board() {
    let rig = Rig::start_with_graph().await;
    let browser = Browser::launch().await.expect("chromium starts");
    let owner = browser.tab().await.unwrap();
    owner.open(&rig.at("/login")).await.unwrap();
    register_owner(&owner).await;

    owner.open(&rig.at("/security")).await.unwrap();
    owner.until("the security summary", "!!document.querySelector('[data-security=headline]')").await;
    let keys = owner.js("[...document.querySelectorAll('[data-section]')].map(s => s.dataset.section).join(',')").await.unwrap();
    // HOL-SANDBOX-TRAIT (holger 42953697): where untrusted work runs follows the chain.
    assert_eq!(keys.as_str().unwrap(), "chain,runner,sources,scanners,approvals,held,access,sync,events", "the server's sections, in its order");

    let chain = section(&owner, "chain").await;
    // innerText follows the page's CSS (a section title is set in capitals).
    assert!(chain.to_lowercase().contains("intake chain") && chain.contains("The importer cannot approve."), "{chain}");

    // Where untrusted work runs: the job runner, in holger's words.
    let runner = section(&owner, "runner").await;
    assert!(runner.to_lowercase().contains("where untrusted work runs"), "{runner}");

    // Each source, and what is checked for it; the mirror skips the chain, and the page says so.
    let sources = section(&owner, "sources").await;
    for name in ["crates-io", "cloud", "team"] {
        assert!(sources.contains(name), "the source {name} is on the Security page: {sources}");
    }
    assert!(sources.contains("skips the intake chain, so no scanner sees it"), "{sources}");

    // The scanners: provenance (cosign, DSSE, Chainguard), malware, CVEs of images too.
    let scanners = section(&owner, "scanners").await;
    for w in ["Provenance", "cosign keyless", "DSSE", "Chainguard", "Malware", "ClamAV", "CVEs", "container images"] {
        assert!(scanners.contains(w), "the scanners say {w:?}: {scanners}");
    }

    // Who may read: the estate's RON says reads are gated, the front open (HCON-5).
    let access = section(&owner, "access").await;
    assert!(access.contains("Reading packages needs a sign-in."), "{access}");
    assert!(access.contains("sign-in"), "each repository's word: {access}");

    // The sync: the key that signs what goes downstream, and the downstream.
    let sync = section(&owner, "sync").await;
    assert!(sync.contains("signed with its sync key") && sync.contains(GRAPH_DOWNSTREAM), "{sync}");
    section(&owner, "events").await;

    // Not the machinery.
    let board = owner.js("!!document.querySelector('[data-board=estate]') || !!document.querySelector('img.board-img')").await.unwrap();
    assert_eq!(board, serde_json::Value::Bool(false), "the Security page draws the machinery board: {}", owner.text().await);
    owner.assert_no_link_to(NEVER).await.unwrap();

    // The Admin page links to the board; the Flow page draws it (HOL-UI-FIX: only there).
    owner.open(&rig.at("/admin")).await.unwrap();
    owner.until("the link to the board", "!!document.querySelector('[data-board-link=estate] a[href=\"/flow\"]')").await;
    let board = owner.js("!!document.querySelector('[data-board=estate]')").await.unwrap();
    assert_eq!(board, serde_json::Value::Bool(false), "the Admin page draws the board again");
    owner.open(&rig.at("/flow")).await.unwrap();
    owner.until("the board on the Flow page", "!!document.querySelector('[data-board=estate] img.board-img')").await;
}