diff --git a/README.md b/README.md index 252602c..9079ac2 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ Windows has a mess when it comes to managing program lifecycles. Developers can This packager aims to take a different approach by -- Observing all the places a program installs to during installation and during any post-install scripts/operations and then writing a journal.json to the same directory the application installs to. This file is referenced during uninstall to return the machine back to the state it was before the install with any files and registry entries associated with that program. +- Recording the install actions it applies (files, directories, registry entries, shortcuts, and script execution) into a `journal.json` written alongside the installed application. This journal is then used during uninstall to reverse those recorded actions and clean up associated state. - Take a "leave the campground better than you found it" approach - this Eagle Scout practices Leave No Trace. -- Taking a "trust but verify model" to program installs and uninstalls, observing program behavior during install and uninstall in order to respect the user. -- Using the `journal.json` as a manifest of everything the program did during the install and post install process. +- Taking a "trust but verify model" to program installs and uninstalls by journaling engine-applied mutations and logging script execution in order to respect the user. +- Using the `journal.json` as a manifest of the actions the installer performed during install and post-install processing. Its current shape is: @@ -29,7 +29,7 @@ Its current shape is: - Creates an installed uninstaller executable in the app root - Uses a C# WinForms presentation process for GUI progress and prompts - Sends GUI state over named-pipe IPC from the Rust engine to the C# UI -- Uses Win32 APIs through the `windows` crate with unsafe isolated in [`src/win.rs`](C:\Users\jasonross\workspace\covenant-setup\src\win.rs) +- Uses Win32 APIs through the `windows` crate with unsafe isolated in [`src/win.rs`](src/win.rs) - Logs every unsafe boundary transition ## Packaging Model diff --git a/project_mvp.md b/project_mvp.md index 21c5ca5..d656272 100644 --- a/project_mvp.md +++ b/project_mvp.md @@ -8,7 +8,7 @@ ## MVP Requirements & Feature List ### 1. The Rust CLI Interface & IPC Readiness -* **CLI Framework:** Utilize `clap` for robust argument parsing with standard subcommands (e.g., `glassbox install manifest.toml`, `glassbox uninstall journal.json`). +* **CLI Framework:** Utilize `clap` for robust argument parsing with standard subcommands (e.g., `covenant-setup install manifest.toml`, `covenant-setup uninstall journal.json`). * **Structured Output Protocol:** The engine must accept a `--json` flag. When active, all standard text logs, progress percentages, and error stack traces must be suppressed and replaced with single-line serialized JSON objects emitted to `stdout`. * **UAC Handling:** The CLI must detect if it has administrative privileges via token inspection. If elevation is required for target paths, it must gracefully exit with a specific error code or auto-relaunch itself using the `runas` verb. diff --git a/src/win.rs b/src/win.rs index b26cb26..2d9569e 100644 --- a/src/win.rs +++ b/src/win.rs @@ -157,17 +157,18 @@ pub fn is_elevated(logger: &Logger) -> Result { let mut elevation = TOKEN_ELEVATION::default(); let mut returned = 0u32; logger.unsafe_enter("GetTokenInformation", json!({"class":"TokenElevation"})); - unsafe { + let info_result = unsafe { GetTokenInformation( token, TokenElevation, Some((&mut elevation as *mut TOKEN_ELEVATION).cast::()), std::mem::size_of::() as u32, &mut returned, - )? + ) }; logger.unsafe_exit("GetTokenInformation", json!({"returned": returned})); close_handle(token, logger)?; + info_result?; if returned < std::mem::size_of::() as u32 { return Err(AppError::Message("Short TOKEN_ELEVATION payload".into())); } @@ -493,11 +494,11 @@ fn known_folder(id: &windows::core::GUID, logger: &Logger) -> Result Result { diff --git a/ui/Covenant.Setup.Ui.Tests/ProgramTests.cs b/ui/Covenant.Setup.Ui.Tests/ProgramTests.cs index 45213ac..e1da1d2 100644 --- a/ui/Covenant.Setup.Ui.Tests/ProgramTests.cs +++ b/ui/Covenant.Setup.Ui.Tests/ProgramTests.cs @@ -8,9 +8,16 @@ public class ProgramTests { [Fact] public void ReadPipeName_returns_value_following_pipe_flag() + { + var name = Program.ReadPipeName(new[] { "--pipe", "foo" }); + Assert.Equal("foo", name); + } + + [Fact] + public void ReadPipeName_strips_full_pipe_path_prefix() { var name = Program.ReadPipeName(new[] { "--pipe", @"\\.\pipe\foo" }); - Assert.Equal(@"\\.\pipe\foo", name); + Assert.Equal("foo", name); } [Fact] diff --git a/ui/Covenant.Setup.Ui/Program.cs b/ui/Covenant.Setup.Ui/Program.cs index d70e3f6..cfcfdca 100644 --- a/ui/Covenant.Setup.Ui/Program.cs +++ b/ui/Covenant.Setup.Ui/Program.cs @@ -31,7 +31,13 @@ internal static class Program { if (string.Equals(args[i], "--pipe", StringComparison.OrdinalIgnoreCase)) { - return args[i + 1]; + var value = args[i + 1]; + const string pipePrefix = @"\\.\pipe\"; + if (value.StartsWith(pipePrefix, StringComparison.OrdinalIgnoreCase)) + { + value = value[pipePrefix.Length..]; + } + return value; } }