app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
//! ADVERSARIAL REVIEW OF 0.4.2 — round three, "the attribution law".
//!
//! The law itself holds. I could not find a way to make the bridge terminate a
//! process it did not start: attribution now comes only from the invoke's own
//! handle, the walk descends and cannot invent a root, seeds are live
//! identity-confirmed processes, and the kill re-verifies on the open handle.
//! Rounds one and two are closed at the mechanism.
//!
//! What is left is the OTHER half of the same question. The bridge no longer
//! adopts a stranger's PROCESSES — but it still adopts a stranger's EVIDENCE.
//! A launcher log is attributed to us on "newer than the newest log that
//! existed before our invoke", with no link to the chain we started, so a
//! launch the user begins while we watch supplies our stage, our `Run Begin`
//! and, at the end, the route we remember as the one that works.
//!
//! Every test here FAILS on f818f14.
mod common;
use common::{Bridge, TempDir};
use serde_json::{json, Value};
use std::time::{Duration, Instant};
use swx_bridge::launch::{self, Attribution};
use swx_bridge::launchos::{FakeOs, ProcInfo};
fn s(v: &Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
fn p(pid: u32, image: &str, created: u64, ppid: Option<u32>) -> ProcInfo {
ProcInfo { pid, image: image.to_string(), created_at: Some(created), ppid }
}
/// A simulated machine on which NONE of the bridge's four routes starts
/// anything — and on which the user then starts SOLIDWORKS themselves.
struct Sim {
dir: TempDir,
lnk: String,
}
impl Sim {
fn new(tag: &str, routes: Value) -> Sim {
let dir = TempDir::new(tag);
std::fs::write(dir.join("routes.json"), routes.to_string()).unwrap();
std::fs::write(
dir.join("preexisting.json"),
json!([
{"pid": 6076, "image": "3DEXPERIENCELauncher.exe", "ageSeconds": 30000},
{"pid": 90552, "image": "3DEXPERIENCELauncherSysTray.exe", "ageSeconds": 30000}
])
.to_string(),
)
.unwrap();
let lnk = format!("{}/SOLIDWORKS Design.lnk", dir.path());
std::fs::write(&lnk, "not really a shortcut").unwrap();
Sim { dir, lnk }
}
/// The user starts SOLIDWORKS from their Start Menu. The simulator records
/// it exactly as it records one of ours — because from the outside, that is
/// precisely how indistinguishable the two are.
fn user_starts_solidworks(&self) {
FakeOs::record_invoke(std::path::Path::new(&self.dir.path()), "userStartMenu", &self.lnk);
}
fn terminated(&self) -> Vec<u64> {
std::fs::read_to_string(self.dir.join("terminated.jsonl"))
.unwrap_or_default()
.lines()
.filter_map(|l| serde_json::from_str::<Value>(l).ok())
.filter_map(|v| v.get("pid")?.as_u64())
.collect()
}
fn boot(&self) -> Bridge {
let env: Vec<(&str, &str)> = vec![
("SWX_SOLIDWORKS_SHORTCUT", Box::leak(self.lnk.clone().into_boxed_str())),
("SWX_LAUNCH_FAKE_DIR", Box::leak(self.dir.path().into_boxed_str())),
("SWX_LAUNCH_RUN_BEGIN_SECS", "3"),
("SWX_LAUNCH_SLDWORKS_SECS", "6"),
("SWX_LAUNCH_ATTACH_SECS", "12"),
("SWX_LAUNCH_POLL_MS", "100"),
("SWX_LAUNCH_RETRY_SETTLE_MS", "400"),
("SWX_LAUNCH_ATTACH_POLL_MS", "150"),
("SWX_LAUNCH_INFLIGHT_SECS", "120"),
("SWX_LAUNCH_BACKOFF_SECS", "0"),
];
Bridge::start_in(TempDir::new("adv3-scratch"), &env)
}
}
/// Every route runs and starts nothing; the user's own launch works.
fn nothing_of_ours_starts() -> Value {
json!({
"shellExecute": {"chain": false},
"powershell": {"chain": false},
"explorer": {"chain": false},
"cmdStart": {"chain": false},
"userStartMenu": {"chain": true, "runBeginAfterMs": 0, "helperAfterMs": 50,
"sldworksAfterMs": 100, "attachAfterMs": 600}
})
}
fn wait_for_launch(b: &Bridge, secs: u64) -> Value {
let deadline = Instant::now() + Duration::from_secs(secs);
loop {
let last = b.call("swx_status", json!({}));
if last["launch"]["finished"] == true || Instant::now() > deadline {
return last;
}
std::thread::sleep(Duration::from_millis(150));
}
}
/// Drive the shared scenario: our route starts nothing, the user starts
/// SOLIDWORKS 350ms later, and it comes up.
fn someone_elses_launch(tag: &str) -> (Sim, Bridge, Value) {
let m = Sim::new(tag, nothing_of_ours_starts());
let b = m.boot();
b.call("swx_start", json!({"reason": "modelling a bracket", "waitSeconds": 0}));
std::thread::sleep(Duration::from_millis(350));
m.user_starts_solidworks();
let st = wait_for_launch(&b, 30);
(m, b, st)
}
// ════════════════════════════════════════════════════════════════════════════
// THE EVIDENCE IS STILL ADOPTED, EVEN THOUGH THE PROCESSES ARE NOT
// ════════════════════════════════════════════════════════════════════════════
/// `watch_route` attributes a launcher log on ONE test — is it newer than the
/// newest log that existed before our invoke? That answers "this log is not
/// pre-existing". It does not answer "this log is ours", and nothing else is
/// asked: the log is not required to belong to the chain the invoke handed
/// back, nor is any attribution required to exist at all.
///
/// So on a run where our route started nothing — which this bridge detects,
/// names, and (rightly) keeps going after — a launch the user starts while we
/// are watching writes the log we then read as our own, and `Run Begin` in
/// THEIR log becomes our `runBeginSeen`.
///
/// The same rule that stopped the bridge adopting the user's PROCESSES has to
/// be applied to their EVIDENCE: a log is ours when our attributed chain is
/// alive to have written it, or the state is `unattributedLog` and says so.
#[test]
fn a_launcher_log_from_someone_elses_launch_is_not_our_run_begin() {
let (_m, b, st) = someone_elses_launch("adv3-userlog");
assert!(
st["launch"]["outcome"] == "attached",
"precondition: the user's launch succeeded and the bridge attached to it: {}",
s(&st["launch"])
);
assert!(
st["launch"]["attributed"] == false,
"precondition: our own route started nothing and attributed nothing: {}",
s(&st["launch"])
);
assert!(
st["launch"]["runBeginSeen"] != json!(true),
"the bridge recorded that it saw 'Run Begin' — from a launcher log written by a launch \
the USER started. Its own route started nothing at all: there is no chain of ours that \
could have written a log. 'Newer than the baseline' means the log is not pre-existing; \
it does not mean it is ours.\n{}\n--- server log ---\n{}",
s(&st["launch"]),
b.log()
);
}
/// The consequence, and the reason this is not cosmetic.
///
/// `finish("attached")` guards the route memory with `run_begin_seen` — added
/// in round two precisely so that "a user who starts SOLIDWORKS from their
/// Start Menu mid-flight" could not teach the bridge that whatever route it
/// happened to be trying is the one that works. That guard is defeated by the
/// hole above: the user's `Run Begin` sets `run_begin_seen`, so the bridge
/// writes `preferredRoute` for a route that started NOTHING — and then tries
/// that route first on every future cold start, on this machine, for ever.
#[test]
fn a_route_that_started_nothing_is_never_remembered_as_the_one_that_works() {
let (_m, b, st) = someone_elses_launch("adv3-preferred");
assert!(
st["launch"]["outcome"] == "attached" && st["launch"]["attributed"] == false,
"precondition: attached, with nothing attributed to our own route: {}",
s(&st["launch"])
);
assert!(
st["launch"]["preferredRoute"].is_null(),
"the bridge learned '{}' as the route that works on this machine — from a run in which \
that route started nothing whatsoever and the USER launched SOLIDWORKS. It is now \
first in the plan for every future cold start. The round-two guard (`run_begin_seen`) \
was supposed to prevent exactly this, and it is satisfied by somebody else's log.\n{}\n\
--- server log ---\n{}",
st["launch"]["preferredRoute"].as_str().unwrap_or("?"),
s(&st["launch"]),
b.log()
);
}
/// The one piece of good news worth pinning down so it cannot regress: the
/// user's PROCESSES are still safe. Nothing descends from an attributed root,
/// so nothing is killed. This test passes today; it is here so that a future
/// change to the log rule cannot quietly re-open the process side.
#[test]
fn the_users_own_launcher_chain_is_still_never_terminated() {
let (m, b, st) = someone_elses_launch("adv3-userprocs");
let killed = m.terminated();
assert!(
killed.is_empty(),
"the bridge terminated {killed:?} — processes belonging to a launch it did not start.\n\
{}\n--- server log ---\n{}",
s(&st["launch"]),
b.log()
);
}
// ════════════════════════════════════════════════════════════════════════════
// THE WALK CANNOT CROSS A ROOT THE CENSUS DOES NOT LIST
// ════════════════════════════════════════════════════════════════════════════
/// `attributed_chain` confirms the root by finding it IN THE CENSUS — and the
/// census is filtered to the six `WATCHED_IMAGES`. `ShellExecuteExW` hands back
/// whatever process the shell created for the `.lnk`, and the release is
/// explicit that no real reply has ever been observed (§9). If that process is
/// anything other than `CATSTART.exe` or `SWXDesktopLauncher.exe` — a shim, a
/// wrapper, a future Platform build, `rundll32` — the root cannot be confirmed,
/// the seed list is empty, the walk has nothing to descend from, and the real
/// CATSTART it started is never attributed.
///
/// The bridge then reports that stall as `cleanup:"nothingToClean"` with
/// `attributed:true` — a CLEAN recovery — and goes on to the next route, with a
/// launcher chain of its own still running. The parent link is right there: the
/// orphan's ppid is the handle-proven pid we were given.
#[test]
fn a_root_the_census_cannot_see_still_carries_its_children() {
let invoked = 1_000_000u64;
let root = Attribution {
pid: 500,
created_at: Some(invoked + 1),
source: "shellExecuteHandle".into(),
};
// The root itself is not one of the six watched images, so it is not in the
// census at all. The chain it started IS.
let census = vec![
p(501, "CATSTART.exe", invoked + 2, Some(500)),
p(502, "SWXDesktopLauncher.exe", invoked + 3, Some(501)),
];
let chain = launch::attributed_chain(&census, Some(&root), &[], &[], invoked);
let pids: Vec<u32> = chain.clone().unwrap_or_default().iter().map(|x| x.pid).collect();
assert!(
pids.contains(&501) && pids.contains(&502),
"the invoke handed back pid 500 as the root it started, and pid 501 records 500 as its \
parent — but the walk found nothing ({pids:?}) because 500 is not itself a terminable \
image, so it never became a seed. The supervisor reads the empty result as \
'nothingToClean' and moves on, leaving the chain running. A handle-proven root is \
proof of lineage whether or not the root is a process we may terminate."
);
}