Why every measurement engine on PC 1 ran on defaults: lock_permissions cut the app folder's inheritance with a non-inheritable user:F, which left files COPIED in before the start (settings.json, machine-id, wallet.json) with no ACE at all, unreadable by their owner; the folder grant is now user:(OI)(CI)F with /T (unit test on the argument shape); a --sweep engine never asks for the firewall rule; the playbook copies the firewall flag; the settings fixture test
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
0b7f9afa7b
commit
d3e207b2ed
4 changed files with 46 additions and 7 deletions
|
|
@ -500,3 +500,18 @@ mod tests {
|
|||
assert!(p.node_override_params.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod fixture_tests {
|
||||
/// `IGNEUM_TEST_SETTINGS=<path> cargo test settings_fixture`: parses a real settings.json with this crate's
|
||||
/// struct and prints what it read (6 October 2026: PC 1's copied file read as defaults; this names the field).
|
||||
#[test]
|
||||
fn settings_fixture_parses_when_given() {
|
||||
let Ok(p) = std::env::var("IGNEUM_TEST_SETTINGS") else { return };
|
||||
let t = std::fs::read_to_string(&p).unwrap();
|
||||
match serde_json::from_str::<super::Settings>(&t) {
|
||||
Ok(s) => println!("parsed: address {} cards {} remote_jobs {} setup_done {}", s.address, s.cards.len(), s.remote_jobs, s.setup_done),
|
||||
Err(e) => panic!("the crate refuses the file: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,11 @@ impl Updater {
|
|||
if crate::platform::start_at_login_is_on() {
|
||||
let _ = crate::platform::set_start_at_login(true);
|
||||
}
|
||||
firewall_first_run(shared);
|
||||
// a measurement engine (--sweep) uses the installed app's node and asks for nothing: the rule is the
|
||||
// installed app's (the dry run of 6 October 2026 raised a second UAC prompt from here)
|
||||
if !shared.runtime.sweep_only {
|
||||
firewall_first_run(shared);
|
||||
}
|
||||
}
|
||||
u.failed_versions = std::fs::read_to_string(u.failed_path()).ok().and_then(|t| serde_json::from_str::<Vec<String>>(&t).ok()).unwrap_or_default();
|
||||
// the cached manifest: the rollback floor and the consensus override are known before the first check
|
||||
|
|
|
|||
|
|
@ -166,17 +166,26 @@ pub fn lock_permissions(path: &Path, dir: bool) {
|
|||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let _ = dir;
|
||||
let user = std::env::var("USERNAME").unwrap_or_default();
|
||||
if !user.is_empty() {
|
||||
let _ = quiet(&mut Command::new(tool("icacls")))
|
||||
.arg(path)
|
||||
.args(["/inheritance:r", "/grant:r", &format!("{user}:F")])
|
||||
.output();
|
||||
let _ = quiet(&mut Command::new(tool("icacls"))).arg(path).args(icacls_lock_args(dir, &user)).output();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The icacls arguments that lock a path to the user. A folder gets an INHERITABLE grant applied to everything
|
||||
/// inside (`(OI)(CI)F`, `/T`): the old non-inheritable `user:F` cut the folder's inheritance and left any file that
|
||||
/// was COPIED in before the engine started with no entry at all (6 October 2026, PC 1: a measurement engine's
|
||||
/// settings.json, machine-id and wallet.json copied by a job read as nothing, so the engine ran on defaults with no
|
||||
/// payout address; the engine's own files, written after the lock, got the creator's default DACL and hid it).
|
||||
pub fn icacls_lock_args(dir: bool, user: &str) -> Vec<String> {
|
||||
if dir {
|
||||
vec!["/inheritance:r".into(), "/grant:r".into(), format!("{user}:(OI)(CI)F"), "/T".into()]
|
||||
} else {
|
||||
vec!["/inheritance:r".into(), "/grant:r".into(), format!("{user}:F")]
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens a URL in the default browser (the fallback when no window host runs).
|
||||
pub fn open_url(url: &str) {
|
||||
#[cfg(target_os = "macos")]
|
||||
|
|
@ -500,6 +509,17 @@ pub fn quiet(cmd: &mut Command) -> &mut Command {
|
|||
cmd
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod lock_tests {
|
||||
#[test]
|
||||
fn a_locked_folder_grants_the_user_inheritably_and_covers_what_is_inside() {
|
||||
let d = super::icacls_lock_args(true, "Admin");
|
||||
assert_eq!(d, vec!["/inheritance:r", "/grant:r", "Admin:(OI)(CI)F", "/T"]);
|
||||
let f = super::icacls_lock_args(false, "Admin");
|
||||
assert_eq!(f, vec!["/inheritance:r", "/grant:r", "Admin:F"]);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ Say ("engine: " + $exe + " (" + $ver + ")")
|
|||
Write-Output ("RESULT TUNE engine " + $ver + " sha256=" + (Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLower())
|
||||
if ($ver -notmatch 'igneum-app (\d+)\.(\d+)\.(\d+)') { Write-Output 'RESULT TUNE error=version_unknown'; exit 2 }
|
||||
|
||||
foreach ($f in @('settings.json', 'machine-id', 'wallet.json', 'tuning.json')) {
|
||||
foreach ($f in @('settings.json', 'machine-id', 'wallet.json', 'tuning.json', 'firewall-rule.json')) { # the firewall flag too: an older kit then asks nothing
|
||||
$src = Join-Path $appDir $f
|
||||
if (Test-Path $src) { Copy-Item -LiteralPath $src -Destination (Join-Path $sApp $f) -Force }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue