main
Drew Owens 0.11.2 source 784acbf 5d ago
//! ADVERSARIAL REVIEW OF 0.6.3 — pass fifteen, gating the deploy.
//!
//! 0.6.3 fixes the self-foreign bug for ATTRIBUTABLE routes: the foreignness
//! question is now asked after the chain is recorded, and every descendant of
//! our own attribution root is excluded. The descendant set is seeded from the
//! root our INVOKE created (the CATSTART), never from a shared ancestor — and
//! `explorer.exe` is not in `WATCHED_IMAGES` at all, so it cannot bridge two
//! trees. A user's Start-Menu CATSTART can therefore never become a "descendant"
//! of ours. Re-attribution is gated on COM being attached, and `Stop` finally
//! records a RouteTry.
//!
//! The self-foreign bug is not gone, though. It is gone for the routes that have
//! a root.

mod common;

use common::{Bridge, TempDir};
use serde_json::{json, Value};

fn s(v: &Value) -> String {
    serde_json::to_string_pretty(v).unwrap_or_default()
}

/// AN UNATTRIBUTABLE ROUTE STILL CALLS ITS OWN CHAIN A STRANGER.
///
/// The exclusion that 0.6.3 added is `!root_pids.contains(p.pid) &&
/// !descendants.contains(p.pid)`, and both sets are empty when
/// `w.attribution` is `None` — which is exactly what `explorer` and `cmdStart`
/// are, by design ("no handle, no kill"). Their chains are also never entered in
/// `a.chain`, because `attributed_chain` returns `None` without a root. So every
/// clause passes for a launcher process we started ourselves, and
/// `foreign_chain_seen` latches on it.
///
/// The fixture below has NO other launcher chain: `preexisting.json` is empty
/// and each route is invoked once, so every terminable process in every census
/// is ours by construction. A `foreignChainSeen: true` here can only be our own.
///
/// It still matters for the same reason as last pass: `reattribute_after_attach`
/// bails on this flag before it consults lineage, so sign-in recovery remains
/// unreachable on any attempt that gets as far as route three. It is reachable
/// on routes one and two, which is the shape the live sign-in actually took — so
/// this is narrower than the bug 0.6.3 fixed, not the same one.
#[test]
fn an_unattributable_routes_own_chain_is_not_a_foreign_chain() {
    let dir = TempDir::new("adv15-selfforeign");
    std::fs::write(
        dir.join("routes.json"),
        // Every route starts a chain and stalls before Run Begin, so the ladder
        // walks all four and reaches the two that cannot name what they started.
        json!({"shellExecute": {"chain": true},
               "powershell": {"chain": true},
               "explorer": {"chain": true},
               "cmdStart": {"chain": true}})
        .to_string(),
    )
    .unwrap();
    std::fs::write(dir.join("preexisting.json"), json!([]).to_string()).unwrap();
    let lnk = format!("{}/SOLIDWORKS Design.lnk", dir.path());
    std::fs::write(&lnk, "x").unwrap();

    let b = Bridge::start_in(
        TempDir::new("adv15-scratch"),
        &[
            ("SWX_SOLIDWORKS_SHORTCUT", &lnk),
            ("SWX_LAUNCH_FAKE_DIR", &dir.path()),
            ("SWX_LAUNCH_RUN_BEGIN_SECS", "1"),
            ("SWX_LAUNCH_SLDWORKS_SECS", "2"),
            ("SWX_LAUNCH_ATTACH_SECS", "3"),
            ("SWX_LAUNCH_POLL_MS", "100"),
            ("SWX_LAUNCH_RETRY_SETTLE_MS", "400"),
            ("SWX_LAUNCH_BACKOFF_SECS", "0"),
        ],
    );
    b.call("swx_start", json!({"reason": "starting SOLIDWORKS", "waitSeconds": 0}));
    let deadline = std::time::Instant::now() + std::time::Duration::from_secs(45);
    loop {
        let st = b.call("swx_status", json!({}));
        if st["launch"]["finished"] == true || std::time::Instant::now() > deadline {
            break;
        }
        std::thread::sleep(std::time::Duration::from_millis(200));
    }
    let st = b.call("swx_status", json!({}));
    let routes: Vec<String> = st["launch"]["routesTried"]
        .as_array()
        .map(|a| a.iter().filter_map(|t| t["route"].as_str().map(String::from)).collect())
        .unwrap_or_default();
    assert!(
        routes.len() >= 3,
        "precondition: the ladder reached an unattributable route: {routes:?}"
    );
    assert!(
        st["launch"]["foreignChainSeen"] != true,
        "the attempt recorded a FOREIGN chain on a machine whose only launcher processes are the \
         ones it started itself (routes {routes:?}, preexisting empty). For `explorer` and \
         `cmdStart` there is no attribution root, so `root_pids` and `descendants` are both empty \
         and their own chains match every clause of the foreignness test. \
         `reattribute_after_attach` bails on this flag before it reaches lineage, so sign-in \
         recovery stays unreachable on any attempt that gets this far.\n{}\n\
         --- server log ---\n{}",
        s(&st["launch"]),
        b.log()
    );
}