app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.9.9 source: user-presence guard; adversary pass 45 (unreadable log unverifiable, latch keyed by pid+creation); manife…
e772fef
20d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
//! ADVERSARIAL REVIEW OF 0.9.7 — pass forty-five, the last of this cycle.
//!
//! readiness.rs, perflog.rs, quit.rs and the dispatch gate compile here and were
//! exercised; com.rs and LiveOs are cfg(windows): READ, NOT RUN.
//!
//! WHAT CHECKS OUT
//! * The pass-44 findings pass, and the platform add-in is now keyed on the DLL
//! (`swk3dcompassaddin.dll`), not a word.
//! * Underscore stripping: no handler reads any underscore-prefixed key except
//! the dispatcher's own `_startupNotVerified`, so stripping ab's injected
//! `_callerBudgetMs` changes nothing in the bridge — ab enforces its budget on
//! its own side. Nothing caller-facing is lost.
//! * swx_dismiss_blocking_dialogs on the Connected notice: its words are
//! template placeholders ("Title", "Description", "Button1"…), which
//! `classify_words` maps to `unknown`, which is never answered — refused.
//! * (2) The 60 s quiet window decides only the NO-MARKER paths. A session
//! whose marker is after its last LOAD_ADDIN is `complete` however busy the
//! log is, so DocSave-time <PROCMEM> lines cannot hold a healthy platform
//! start. The one place a session's own activity can hold it is a session
//! that never loaded the platform add-in while somebody works by hand,
//! saving more often than once a minute — and the bridge's own document
//! commands are refused before the first ready, so it cannot keep the log
//! busy itself. Availability, narrow; noted, not tested. Clock skew and
//! coarse mtime shift the 60 s by seconds; they cannot fake a marker.
mod common;
use common::*;
use serde_json::{json, Value};
fn s(v: &Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
fn session(scratch: &TempDir) {
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({"running": true, "untitled": 1, "active": Value::Null, "docs": [], "files": {}}).to_string(),
)
.unwrap();
}
const HEADER: &str = "; SolidWorks generated log file";
const COMPASS: &str = r#"<AD2> "LOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13"#;
const TOOLBOX: &str = r#"<AD2> "LOAD_ADDIN" "SWToolbox.dll" "SOLIDWORKS Toolbox Library" [1.0] "34.0" 14 COM.new 1770885070 13"#;
const MARKER: &str = "<3DXLOAD_TIME>24000</3DXLOAD_TIME>";
/// SEVERITY 2 — AN UNREADABLE LOG IS TREATED AS NO LOG, AND NO LOG LETS COM
/// DECIDE ALONE.
///
/// `startup_complete` returns the same `Unavailable` for "no log to read" and
/// for "the performance log … could not be read", and the verdict table maps
/// `(Unavailable, Some(true))` to READY. So a log that EXISTS but cannot be
/// opened — locked, an ACL, an antivirus holding it, a path the process cannot
/// reach — hands the verdict to COM, which was MEASURED reporting complete before
/// the marker on a real start. That is the 10-01 crash window by a different
/// door.
///
/// Absent and unreadable are different facts. "No log" on a machine whose
/// SOLIDWORKS never writes one can defensibly leave COM alone; "a log I could
/// not read" says the edition DOES write one and this bridge simply did not see
/// it. That is `unverifiable` — reads after the grace, mutations refused — the
/// same answer the bridge already gives when nothing ties the log to the
/// session.
#[test]
fn a_log_that_exists_but_cannot_be_read_does_not_let_com_decide_alone() {
use std::os::unix::fs::PermissionsExt;
let scratch = TempDir::new("adv43-unreadable");
session(&scratch);
let log = scratch.join("SolidWorksPerformance.log");
std::fs::write(&log, format!("{HEADER}\n{TOOLBOX}\n{COMPASS}\n")).unwrap();
std::fs::set_permissions(&log, std::fs::Permissions::from_mode(0o000)).unwrap();
let unreadable = std::fs::read(&log).is_err();
let logp = log.to_string_lossy().to_string();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
let _ = std::fs::set_permissions(&log, std::fs::Permissions::from_mode(0o644));
assert!(unreadable, "precondition: this user cannot read the file (not running as root)");
assert!(
r["errorCode"] == json!("host_app_starting"),
"the performance log EXISTS and could not be read — and its contents, had they been \
readable, say a start is loading (the platform add-in loaded, no marker). The bridge \
treated \"could not be read\" exactly like \"there is no log\" and let COM — measured true \
before the marker — decide alone: swx_new_document ran. Unreadable is unverifiable, not \
absent. {}",
s(&r)
);
}
/// SEVERITY 2 — THE READY LATCH IS KEYED BY PID ALONE, SO A NEW SESSION THAT
/// REUSES THE PID IS BORN READY.
///
/// `SESSIONS: HashMap<i64, Seen>` — the key is the process id; `seen.ready`, once
/// true, short-circuits every later assessment (`alreadyReadyThisSession`). The
/// process CREATION TIME is computed two lines earlier and not part of the key.
/// Windows reissues pids, and the map lives as long as the bridge server — so a
/// SOLIDWORKS quit and relaunched in one bridge lifetime that happens to get the
/// same pid skips the whole gate: no log, no COM, no grace. Ready at second 0 —
/// the crash window again. Everything else in this bridge that identifies a
/// session (session history, the quit ladder's `same_process`) keys on pid +
/// creation time; this is the one that does not.
///
/// It survives a com-host respawn (correct — the map is in the server), and a
/// server restart empties it (also correct). The fix is the key: (pid,
/// creation time).
#[test]
fn a_new_session_that_reuses_the_pid_does_not_inherit_ready() {
let scratch = TempDir::new("adv43-pidreuse");
session(&scratch);
let log = scratch.join("SolidWorksPerformance.log");
// Session one: a completed start, written since the (hour-old) process began.
std::fs::write(&log, format!("{HEADER}\n{TOOLBOX}\n{COMPASS}\n{MARKER}\n")).unwrap();
let logp = log.to_string_lossy().to_string();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let first = b.call("swx_start", json!({}));
let pid1 = first["readiness"]["processId"].clone();
let made = b.call("swx_new_document", json!({"type": "part"}));
assert!(made["success"] == json!(true), "precondition: session one became ready: {}", s(&made));
// Session two: SOLIDWORKS relaunched, the SAME pid reissued, created NOW,
// and its log (rewritten at start) shows add-ins loading with no marker.
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
std::fs::write(scratch.join("mock-sldworks-created"), now.to_string()).unwrap();
std::fs::write(&log, format!("{HEADER}\n{TOOLBOX}\n{COMPASS}\n")).unwrap();
let st = b.call("swx_status", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
let text = s(&st);
assert!(
text.contains(&format!("\"processCreatedAt\": {now}")) && pid1.is_number(),
"precondition: a new process (created {now}) with pid {pid1} — status: {text}"
);
assert!(
r["errorCode"] == json!("host_app_starting"),
"a NEW SOLIDWORKS process — created just now, its log showing add-ins loading and no marker \
— reused the pid of a session the bridge had already seen ready, and the latch \
(`alreadyReadyThisSession`, keyed by pid alone) waved swx_new_document through without \
reading the log or COM. Key the latch by (pid, creation time), as every other session \
identity in this bridge is keyed. {}",
s(&r)
);
}