fix(sync): harden sync CLI, IPC, and UI paths from CodeRabbit review

- reject option-like tokens as flag values (--snapshot --force wrote a
  file named --force); --flag=-value still works
- PowerShell remote quoting uses single-quoted literals so $() in a
  quoted path cannot expand
- sync-hosts.json written via temp file + rename; a crash mid-write
  truncated it and the reader's corrupt-fallback dropped every host
- cancelled sync child escalates SIGTERM -> SIGKILL after 5s grace
- NDJSON progress events validated field-by-field before casting
- snapshot filenames include milliseconds to avoid same-second overwrite
- syncAutoScheduler.stop() wired into will-quit cleanup
- sync --ui exclusivity also rejects --make-temp/--remove-temp/--json
- document --sync-window in app help; group --make-temp/--remove-temp
  under modes in sync usage
This commit is contained in:
2026-07-12 02:10:04 -07:00
parent 25cca8ce24
commit c9f85473bb
19 changed files with 257 additions and 27 deletions
+14
View File
@@ -160,3 +160,17 @@ test('quoteForRemoteShell quotes per flavor and rejects unsafe Windows values',
assert.throws(() => quoteForRemoteShell('windows-cmd', 'a"b'), /Refusing to quote/);
assert.throws(() => quoteForRemoteShell('windows-powershell', 'a\nb'), /Refusing to quote/);
});
test('quoteForRemoteShell does not let PowerShell expand a quoted value', () => {
// PowerShell expands $(...) and $var inside double quotes, so a single-quoted
// literal is the only safe form; '' is the escape for an embedded quote.
assert.equal(
quoteForRemoteShell('windows-powershell', 'C:/tmp/$(calc.exe)'),
`'C:/tmp/$(calc.exe)'`,
);
assert.equal(quoteForRemoteShell('windows-powershell', "C:/tmp/it's"), `'C:/tmp/it''s'`);
assert.equal(
quoteForRemoteShell('windows-powershell', 'C:/Users/First Last/Temp/subminer-sync-ab'),
`'C:/Users/First Last/Temp/subminer-sync-ab'`,
);
});