667 lines
22 KiB
Rust
667 lines
22 KiB
Rust
use proxywarden_lib::component_detection::{
|
|
proxyfier_component_from_inventory, singbox_component_from_inventory,
|
|
};
|
|
use proxywarden_lib::component_inventory::{
|
|
authorize_component_action, classify_component_candidates,
|
|
component_inventory_fingerprint_for_cutover, legacy_proxifyre_topshelf_path_matches,
|
|
prove_legacy_cutover, BinaryIdentityEvidence, CandidateRole, ComponentCandidateProbe,
|
|
ComponentClassification, InventoryAction, InventoryIssue, LegacyCutoverEvidence,
|
|
LegacyCutoverProof, LegacyProxifyreScmProfile, MarkerEvidence, ServiceEvidence,
|
|
AMBIGUOUS_LEGACY, MANUAL_MIGRATION_REQUIRED, OWNERSHIP_MISMATCH,
|
|
};
|
|
use proxywarden_lib::component_status::resolve_component_statuses_with_inventories;
|
|
use proxywarden_lib::models::{ComponentId, ComponentState};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
#[test]
|
|
fn managed_current_requires_marker_files_and_exact_service_path() {
|
|
let root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\ProxiFyre");
|
|
let inventory = classify_component_candidates(
|
|
ComponentId::Proxyfier,
|
|
vec![probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Current,
|
|
&root,
|
|
true,
|
|
MarkerEvidence::Valid,
|
|
BinaryIdentityEvidence::Unknown,
|
|
Some(service(&root.join("ProxiFyre.exe"), true)),
|
|
)],
|
|
);
|
|
|
|
assert_eq!(
|
|
inventory.classification(),
|
|
ComponentClassification::ManagedCurrent
|
|
);
|
|
assert_eq!(
|
|
inventory
|
|
.selected_candidate()
|
|
.expect("selected current")
|
|
.binary_version,
|
|
Some("2.4.0.0".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn same_service_name_with_foreign_path_is_ownership_mismatch() {
|
|
let root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\ProxiFyre");
|
|
let inventory = classify_component_candidates(
|
|
ComponentId::Proxyfier,
|
|
vec![probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Current,
|
|
&root,
|
|
true,
|
|
MarkerEvidence::Valid,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(
|
|
PathBuf::from(r"C:\Foreign\ProxiFyre.exe").as_path(),
|
|
false,
|
|
)),
|
|
)],
|
|
);
|
|
|
|
let candidate = inventory
|
|
.selected_candidate()
|
|
.expect("foreign current candidate");
|
|
assert_eq!(candidate.classification, ComponentClassification::Foreign);
|
|
assert_eq!(candidate.issues[0].code, OWNERSHIP_MISMATCH);
|
|
}
|
|
|
|
#[test]
|
|
fn tools_proxifyre_is_legacy_and_never_current() {
|
|
let root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let inventory = classify_component_candidates(
|
|
ComponentId::Proxyfier,
|
|
vec![probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Legacy,
|
|
&root,
|
|
false,
|
|
MarkerEvidence::NotRequired,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(&root.join("ProxiFyre.exe"), true)),
|
|
)],
|
|
);
|
|
|
|
assert_eq!(
|
|
inventory.classification(),
|
|
ComponentClassification::ManagedLegacy
|
|
);
|
|
let component = proxyfier_component_from_inventory(&inventory);
|
|
assert_eq!(component.actions, vec!["Перенести ProxiFyre"]);
|
|
assert!(component
|
|
.problems
|
|
.iter()
|
|
.any(|problem| problem.contains("явного переноса")));
|
|
}
|
|
|
|
#[test]
|
|
fn bare_singbox_root_stays_foreign_without_complete_identity() {
|
|
let root = PathBuf::from(r"C:\Program Files\sing-box");
|
|
let mut candidate = probe(
|
|
ComponentId::Singbox,
|
|
CandidateRole::ForeignByDefault,
|
|
&root,
|
|
false,
|
|
MarkerEvidence::NotRequired,
|
|
BinaryIdentityEvidence::Unknown,
|
|
Some(service(&root.join("ProxyWardenSingBox.exe"), true)),
|
|
);
|
|
candidate.legacy_identity_complete = false;
|
|
let inventory = classify_component_candidates(ComponentId::Singbox, vec![candidate]);
|
|
|
|
assert_eq!(inventory.classification(), ComponentClassification::Foreign);
|
|
}
|
|
|
|
#[test]
|
|
fn current_root_without_required_marker_is_incomplete() {
|
|
let root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\ProxiFyre");
|
|
let inventory = classify_component_candidates(
|
|
ComponentId::Proxyfier,
|
|
vec![probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Current,
|
|
&root,
|
|
true,
|
|
MarkerEvidence::Missing,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(&root.join("ProxiFyre.exe"), true)),
|
|
)],
|
|
);
|
|
|
|
assert_eq!(
|
|
inventory.classification(),
|
|
ComponentClassification::Incomplete
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn startup_status_preserves_foreign_and_incomplete_inventory_errors() {
|
|
let proxyfier_root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\ProxiFyre");
|
|
let foreign_proxyfier = classify_component_candidates(
|
|
ComponentId::Proxyfier,
|
|
vec![probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Current,
|
|
&proxyfier_root,
|
|
true,
|
|
MarkerEvidence::Valid,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(
|
|
PathBuf::from(r"C:\Foreign\ProxiFyre.exe").as_path(),
|
|
false,
|
|
)),
|
|
)],
|
|
);
|
|
let singbox_root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\sing-box");
|
|
let mut incomplete_singbox_probe = probe(
|
|
ComponentId::Singbox,
|
|
CandidateRole::Current,
|
|
&singbox_root,
|
|
false,
|
|
MarkerEvidence::NotRequired,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(&singbox_root.join("ProxyWardenSingBox.exe"), true)),
|
|
);
|
|
incomplete_singbox_probe
|
|
.missing_files
|
|
.push(singbox_root.join("ProxyWardenSingBox.xml"));
|
|
let incomplete_singbox =
|
|
classify_component_candidates(ComponentId::Singbox, vec![incomplete_singbox_probe]);
|
|
|
|
let statuses =
|
|
resolve_component_statuses_with_inventories(&foreign_proxyfier, &incomplete_singbox);
|
|
let proxyfier = statuses
|
|
.iter()
|
|
.find(|status| status.id == ComponentId::Proxyfier)
|
|
.expect("ProxiFyre status");
|
|
let singbox = statuses
|
|
.iter()
|
|
.find(|status| status.id == ComponentId::Singbox)
|
|
.expect("sing-box status");
|
|
|
|
assert_eq!(proxyfier.state, ComponentState::Error);
|
|
assert_eq!(singbox.state, ComponentState::Error);
|
|
assert!(!proxyfier.problems.is_empty());
|
|
assert!(!singbox.problems.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn current_candidate_wins_but_legacy_remains_visible() {
|
|
let current_root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\ProxiFyre");
|
|
let legacy_root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let inventory = classify_component_candidates(
|
|
ComponentId::Proxyfier,
|
|
vec![
|
|
probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Legacy,
|
|
&legacy_root,
|
|
false,
|
|
MarkerEvidence::NotRequired,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(&legacy_root.join("ProxiFyre.exe"), true)),
|
|
),
|
|
probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Current,
|
|
¤t_root,
|
|
true,
|
|
MarkerEvidence::Valid,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(¤t_root.join("ProxiFyre.exe"), true)),
|
|
),
|
|
],
|
|
);
|
|
|
|
assert_eq!(inventory.candidates.len(), 2);
|
|
assert_eq!(
|
|
inventory.classification(),
|
|
ComponentClassification::ManagedCurrent
|
|
);
|
|
assert_eq!(
|
|
inventory
|
|
.selected_candidate()
|
|
.expect("selected current")
|
|
.root,
|
|
current_root
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_managed_legacy_candidates_block_selection() {
|
|
let roots = [
|
|
PathBuf::from(r"C:\Tools\ProxiFyre"),
|
|
PathBuf::from(r"C:\Program Files\ProxiFyre"),
|
|
];
|
|
let probes = roots
|
|
.iter()
|
|
.map(|root| {
|
|
probe(
|
|
ComponentId::Proxyfier,
|
|
CandidateRole::Legacy,
|
|
root,
|
|
false,
|
|
MarkerEvidence::NotRequired,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(&root.join("ProxiFyre.exe"), true)),
|
|
)
|
|
})
|
|
.collect();
|
|
let inventory = classify_component_candidates(ComponentId::Proxyfier, probes);
|
|
|
|
assert!(inventory.selected_candidate().is_none());
|
|
assert_eq!(inventory.issues[0].code, AMBIGUOUS_LEGACY);
|
|
}
|
|
|
|
#[test]
|
|
fn reparse_point_is_never_managed() {
|
|
let root = PathBuf::from(r"C:\Program Files\ProxyWarden\components\sing-box");
|
|
let mut candidate = probe(
|
|
ComponentId::Singbox,
|
|
CandidateRole::Current,
|
|
&root,
|
|
false,
|
|
MarkerEvidence::NotRequired,
|
|
BinaryIdentityEvidence::KnownPackage,
|
|
Some(service(&root.join("ProxyWardenSingBox.exe"), true)),
|
|
);
|
|
candidate.has_reparse_point = true;
|
|
let inventory = classify_component_candidates(ComponentId::Singbox, vec![candidate]);
|
|
|
|
assert_eq!(inventory.classification(), ComponentClassification::Foreign);
|
|
assert_eq!(
|
|
inventory.selected_candidate().unwrap().issues[0].code,
|
|
OWNERSHIP_MISMATCH
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn exact_frozen_proxifyre_identity_is_the_only_automatic_cutover() {
|
|
let root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let inventory = legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&root,
|
|
"ProxiFyreService",
|
|
&topshelf_path(&root),
|
|
"2.2.1.0",
|
|
);
|
|
|
|
let proof = prove_legacy_cutover(&inventory, &exact_cutover_evidence())
|
|
.expect("exact identity must produce an opaque proof");
|
|
assert_eq!(proof.fingerprint().len(), 64);
|
|
}
|
|
|
|
#[test]
|
|
fn discovery_evidence_never_substitutes_for_cutover_identity() {
|
|
let auto_root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let cases = [
|
|
legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
Path::new(r"C:\Program Files\ProxiFyre"),
|
|
"ProxiFyreService",
|
|
&topshelf_path(Path::new(r"C:\Program Files\ProxiFyre")),
|
|
"2.2.1.0",
|
|
),
|
|
legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&auto_root,
|
|
"ProxiFyre",
|
|
&topshelf_path(&auto_root),
|
|
"2.2.1.0",
|
|
),
|
|
legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&auto_root,
|
|
"ProxiFyreService",
|
|
&format!(
|
|
r#""{}" --service"#,
|
|
auto_root.join("ProxiFyre.exe").display()
|
|
),
|
|
"2.2.1.0",
|
|
),
|
|
legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&auto_root,
|
|
"ProxiFyreService",
|
|
&topshelf_path(&auto_root),
|
|
"2.4.0.0",
|
|
),
|
|
];
|
|
|
|
for inventory in cases {
|
|
assert_eq!(
|
|
inventory.classification(),
|
|
ComponentClassification::ManagedLegacy
|
|
);
|
|
assert_manual_without_mutation(prove_legacy_cutover(&inventory, &exact_cutover_evidence()));
|
|
}
|
|
|
|
let inventory = legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&auto_root,
|
|
"ProxiFyreService",
|
|
&topshelf_path(&auto_root),
|
|
"2.2.1.0",
|
|
);
|
|
let mut bad_manifest = exact_cutover_evidence();
|
|
bad_manifest.proxifyre_manifest_matches = false;
|
|
assert_manual_without_mutation(prove_legacy_cutover(&inventory, &bad_manifest));
|
|
|
|
let mut bad_snapshot_fingerprint = exact_cutover_evidence();
|
|
bad_snapshot_fingerprint
|
|
.proxifyre_scm_snapshot_fingerprint
|
|
.clear();
|
|
assert_manual_without_mutation(prove_legacy_cutover(&inventory, &bad_snapshot_fingerprint));
|
|
|
|
let mut bad_profile = exact_cutover_evidence();
|
|
bad_profile
|
|
.proxifyre_scm_profile
|
|
.as_mut()
|
|
.expect("profile")
|
|
.delayed_auto_start = true;
|
|
assert_manual_without_mutation(prove_legacy_cutover(&inventory, &bad_profile));
|
|
|
|
let mut extra_candidate = inventory.clone();
|
|
extra_candidate
|
|
.candidates
|
|
.push(extra_candidate.candidates[0].clone());
|
|
assert_manual_without_mutation(prove_legacy_cutover(
|
|
&extra_candidate,
|
|
&exact_cutover_evidence(),
|
|
));
|
|
|
|
let mut alias_collision = exact_cutover_evidence();
|
|
alias_collision.additional_matching_service = true;
|
|
assert_manual_without_mutation(prove_legacy_cutover(&inventory, &alias_collision));
|
|
}
|
|
|
|
#[test]
|
|
fn legacy_singbox_is_always_manual_and_has_zero_mutation_authority() {
|
|
let root = PathBuf::from(r"C:\Program Files\ProxyWarden\sing-box");
|
|
let inventory = legacy_inventory(
|
|
ComponentId::Singbox,
|
|
&root,
|
|
"ProxyWardenSingBox",
|
|
&format!(r#""{}""#, root.join("ProxyWardenSingBox.exe").display()),
|
|
"1.13.19",
|
|
);
|
|
|
|
assert_eq!(
|
|
inventory.classification(),
|
|
ComponentClassification::ManagedLegacy
|
|
);
|
|
let component = singbox_component_from_inventory(&inventory);
|
|
assert!(component.actions.is_empty());
|
|
assert!(component
|
|
.problems
|
|
.iter()
|
|
.any(|problem| problem.contains("ручного переноса")));
|
|
assert_manual_without_mutation(prove_legacy_cutover(&inventory, &exact_cutover_evidence()));
|
|
}
|
|
|
|
#[test]
|
|
fn generic_inventory_authorization_never_grants_cutover() {
|
|
let root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let inventory = legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&root,
|
|
"ProxiFyreService",
|
|
&topshelf_path(&root),
|
|
"2.2.1.0",
|
|
);
|
|
|
|
let error = authorize_component_action(&inventory, InventoryAction::Cutover)
|
|
.expect_err("generic lifecycle authorization must not grant cutover");
|
|
assert_eq!(error.code, "legacy_cutover_required");
|
|
let mut current = inventory.clone();
|
|
current.candidates[0].classification = ComponentClassification::ManagedCurrent;
|
|
let missing =
|
|
proxywarden_lib::component_inventory::ComponentInventory::missing(ComponentId::Proxyfier);
|
|
for inventory in [¤t, &missing] {
|
|
assert!(authorize_component_action(inventory, InventoryAction::Cutover).is_err());
|
|
}
|
|
prove_legacy_cutover(&inventory, &exact_cutover_evidence())
|
|
.expect("strict gate remains the only proof constructor");
|
|
}
|
|
|
|
#[test]
|
|
fn generic_inventory_authorization_never_writes_legacy_runtime_config() {
|
|
let root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let inventory = legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&root,
|
|
"ProxiFyreService",
|
|
&topshelf_path(&root),
|
|
"2.2.1.0",
|
|
);
|
|
|
|
for action in [
|
|
InventoryAction::Apply,
|
|
InventoryAction::Start,
|
|
InventoryAction::Stop,
|
|
] {
|
|
let error = authorize_component_action(&inventory, action)
|
|
.expect_err("legacy runtime actions require explicit cutover");
|
|
assert_eq!(error.code, "legacy_cutover_required");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn cutover_inventory_fingerprint_is_stable_and_binds_live_service_state() {
|
|
let root = PathBuf::from(r"C:\Tools\ProxiFyre");
|
|
let inventory = legacy_inventory(
|
|
ComponentId::Proxyfier,
|
|
&root,
|
|
"ProxiFyreService",
|
|
&topshelf_path(&root),
|
|
"2.2.1.0",
|
|
);
|
|
let first = component_inventory_fingerprint_for_cutover(&inventory);
|
|
assert_eq!(
|
|
first,
|
|
component_inventory_fingerprint_for_cutover(&inventory)
|
|
);
|
|
|
|
let mut changed = inventory.clone();
|
|
changed.candidates[0]
|
|
.service
|
|
.as_mut()
|
|
.expect("service")
|
|
.status = "running".to_string();
|
|
assert_ne!(first, component_inventory_fingerprint_for_cutover(&changed));
|
|
}
|
|
|
|
#[test]
|
|
fn frozen_scm_profile_rejects_each_unsafe_or_unknown_field() {
|
|
let mutations: [fn(&mut LegacyProxifyreScmProfile); 15] = [
|
|
|profile| profile.service_type = 0x20,
|
|
|profile| profile.start_type = 3,
|
|
|profile| profile.error_control = 0,
|
|
|profile| profile.account_name = "NetworkService".to_string(),
|
|
|profile| profile.display_name = "ProxiFyre".to_string(),
|
|
|profile| profile.description.clear(),
|
|
|profile| profile.dependencies.push("Tcpip".to_string()),
|
|
|profile| profile.load_order_group = Some("Network".to_string()),
|
|
|profile| profile.has_failure_actions = true,
|
|
|profile| profile.failure_actions_on_non_crash = true,
|
|
|profile| profile.delayed_auto_start = true,
|
|
|profile| profile.sid_type = 1,
|
|
|profile| {
|
|
profile
|
|
.required_privileges
|
|
.push("SeDebugPrivilege".to_string())
|
|
},
|
|
|profile| profile.has_triggers = true,
|
|
|profile| profile.untrusted_mutation_rights = true,
|
|
];
|
|
|
|
assert!(exact_scm_profile().matches_frozen_2_2_1_profile());
|
|
for mutate in mutations {
|
|
let mut profile = exact_scm_profile();
|
|
mutate(&mut profile);
|
|
assert!(!profile.matches_frozen_2_2_1_profile());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn topshelf_cutover_path_is_token_exact_and_pair_order_independent() {
|
|
let executable = Path::new(r"C:\Tools\ProxiFyre\ProxiFyre.exe");
|
|
for path_name in [
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" -displayname "ProxiFyre Service" -servicename "ProxiFyreService""#,
|
|
r#"C:\Tools\ProxiFyre\ProxiFyre.exe -servicename ProxiFyreService -displayname "ProxiFyre Service""#,
|
|
] {
|
|
assert!(legacy_proxifyre_topshelf_path_matches(
|
|
path_name, executable
|
|
));
|
|
}
|
|
for path_name in [
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" --service"#,
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" -displayname "ProxiFyre Service" -servicename ProxiFyreService --run"#,
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" -displayname "Foreign" -servicename ProxiFyreService"#,
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" -displayname "ProxiFyre Service" -servicename ProxiFyre"#,
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe -displayname "ProxiFyre Service" -servicename ProxiFyreService"#,
|
|
] {
|
|
assert!(!legacy_proxifyre_topshelf_path_matches(
|
|
path_name, executable
|
|
));
|
|
}
|
|
assert!(!legacy_proxifyre_topshelf_path_matches(
|
|
r#""C:\Program Files\ProxiFyre\ProxiFyre.exe" -displayname "ProxiFyre Service" -servicename ProxiFyreService"#,
|
|
Path::new(r"C:\Program Files\ProxiFyre\ProxiFyre.exe"),
|
|
));
|
|
}
|
|
|
|
fn assert_manual_without_mutation(result: Result<LegacyCutoverProof, InventoryIssue>) {
|
|
assert_eq!(
|
|
result
|
|
.expect_err("manual identity must not yield a proof")
|
|
.code,
|
|
MANUAL_MIGRATION_REQUIRED
|
|
);
|
|
}
|
|
|
|
fn exact_cutover_evidence() -> LegacyCutoverEvidence {
|
|
LegacyCutoverEvidence {
|
|
proxifyre_manifest_matches: true,
|
|
proxifyre_scm_profile: Some(exact_scm_profile()),
|
|
proxifyre_scm_snapshot_fingerprint: "9".repeat(64),
|
|
additional_matching_service: false,
|
|
}
|
|
}
|
|
|
|
fn exact_scm_profile() -> LegacyProxifyreScmProfile {
|
|
LegacyProxifyreScmProfile {
|
|
service_type: 0x10,
|
|
start_type: 2,
|
|
error_control: 1,
|
|
account_name: "LocalSystem".to_string(),
|
|
display_name: "ProxiFyre Service".to_string(),
|
|
description: "ProxiFyre - SOCKS5 ProxiFyre Service".to_string(),
|
|
dependencies: Vec::new(),
|
|
load_order_group: None,
|
|
has_failure_actions: false,
|
|
failure_actions_on_non_crash: false,
|
|
delayed_auto_start: false,
|
|
sid_type: 0,
|
|
required_privileges: Vec::new(),
|
|
has_triggers: false,
|
|
untrusted_mutation_rights: false,
|
|
}
|
|
}
|
|
|
|
fn legacy_inventory(
|
|
component_id: ComponentId,
|
|
root: &Path,
|
|
service_name: &str,
|
|
path_name: &str,
|
|
version: &str,
|
|
) -> proxywarden_lib::component_inventory::ComponentInventory {
|
|
let executable_name = match component_id {
|
|
ComponentId::Proxyfier => "ProxiFyre.exe",
|
|
ComponentId::Singbox => "sing-box.exe",
|
|
ComponentId::ControlApp => "ProxyWarden.exe",
|
|
};
|
|
let service_executable = match component_id {
|
|
ComponentId::Singbox => root.join("ProxyWardenSingBox.exe"),
|
|
ComponentId::Proxyfier | ComponentId::ControlApp => root.join(executable_name),
|
|
};
|
|
classify_component_candidates(
|
|
component_id.clone(),
|
|
vec![ComponentCandidateProbe {
|
|
component_id,
|
|
role: CandidateRole::Legacy,
|
|
root: root.to_path_buf(),
|
|
root_exists: true,
|
|
has_reparse_point: false,
|
|
executable_path: Some(root.join(executable_name)),
|
|
missing_files: Vec::new(),
|
|
marker: MarkerEvidence::NotRequired,
|
|
marker_required: false,
|
|
binary_identity: BinaryIdentityEvidence::KnownPackage,
|
|
binary_version: Some(version.to_string()),
|
|
service: Some(ServiceEvidence {
|
|
name: service_name.to_string(),
|
|
status: "stopped".to_string(),
|
|
path_name: Some(path_name.to_string()),
|
|
executable_path: Some(service_executable),
|
|
path_matches_candidate: true,
|
|
binary_version: Some(version.to_string()),
|
|
}),
|
|
service_required: true,
|
|
legacy_identity_complete: true,
|
|
}],
|
|
)
|
|
}
|
|
|
|
fn topshelf_path(root: &Path) -> String {
|
|
format!(
|
|
r#""{}" -displayname "ProxiFyre Service" -servicename "ProxiFyreService""#,
|
|
root.join("ProxiFyre.exe").display()
|
|
)
|
|
}
|
|
|
|
fn probe(
|
|
component_id: ComponentId,
|
|
role: CandidateRole,
|
|
root: &Path,
|
|
marker_required: bool,
|
|
marker: MarkerEvidence,
|
|
binary_identity: BinaryIdentityEvidence,
|
|
service: Option<ServiceEvidence>,
|
|
) -> ComponentCandidateProbe {
|
|
let executable_name = match component_id {
|
|
ComponentId::Proxyfier => "ProxiFyre.exe",
|
|
ComponentId::Singbox => "sing-box.exe",
|
|
ComponentId::ControlApp => "ProxyWarden.exe",
|
|
};
|
|
ComponentCandidateProbe {
|
|
component_id,
|
|
role,
|
|
root: root.to_path_buf(),
|
|
root_exists: true,
|
|
has_reparse_point: false,
|
|
executable_path: Some(root.join(executable_name)),
|
|
missing_files: Vec::new(),
|
|
marker,
|
|
marker_required,
|
|
binary_identity,
|
|
binary_version: Some("2.4.0.0".to_string()),
|
|
service,
|
|
service_required: true,
|
|
legacy_identity_complete: true,
|
|
}
|
|
}
|
|
|
|
fn service(executable: &std::path::Path, matches: bool) -> ServiceEvidence {
|
|
ServiceEvidence {
|
|
name: "ProxiFyreService".to_string(),
|
|
status: "stopped".to_string(),
|
|
path_name: Some(format!(r#""{}" --service"#, executable.display())),
|
|
executable_path: Some(executable.to_path_buf()),
|
|
path_matches_candidate: matches,
|
|
binary_version: Some("2.4.0.0".to_string()),
|
|
}
|
|
}
|