Repository navigation
Installer: never overwrite runtime user data, and clean up on uninstall - #34
Conversation
… uninstall - [Files]: exclude config.json / logs\\* / modules\\PW.txt from the ignoreversion wildcard
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 24aa2fa9de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ; 卸载前先清理右键菜单注册项与 PATH 条目,避免残留(原 .iss 缺此段,卸载后会留注册表垃圾)。 | ||
| ; 实际删除统一走 context-menu\RegistryDeleteSafety.ps1:仅显式允许列表内的键可删, | ||
| ; 且关键系统根(如 ...\Windows NT\CurrentVersion)一律拒绝。 | ||
| Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\context-menu\Uninstall-ContextMenu.ps1"" -InstallRoot ""{app}"""; Flags: runhidden; RunOnceId: "RemoveContextMenu" |
There was a problem hiding this comment.
Preserve context-menu registrations from other installs
Because context-menu registration remains an explicit/manual step, this command executes even when the {app} copy was never registered. If a portable mirror (or another copy at a different path) has registered the shared HKCR...\Steganographier and CommandStore keys, uninstalling this installer runs its generic cleanup script and deletes those keys, leaving the other installation’s right-click integration broken. Gate cleanup on registrations that point to {app} (or record installation ownership) instead of running it unconditionally.
AGENTS.md reference: AGENTS.md:L10-L10
Useful? React with 👍 / 👎.
…ent mode
Found while preparing an installer for the author to test: this branch's [Files]
entry was a bare `Source: "{#SourceDir}\*"`, so installing it over an existing
installation would have replaced modules\PW.txt - a real password book - with the
2-byte tracked placeholder, and overwritten config.json. On this machine those are
1450 bytes (last written 2026-03-18) and 518 bytes. AGENTS.md forbids exactly this,
and PR cenglin123#34 exists to fix it; that fix was not on this branch.
- [Files] now excludes config.json, logs\* and modules\PW.txt, and installs the
placeholder password book only with onlyifdoesntexist + uninsneveruninstall.
- [UninstallRun] removes the context-menu entries and the PATH entry before the
files go. Its path is {app}\Uninstall-ContextMenu.ps1: build-release.ps1 copies
the CONTENTS of context-menu\ into the stage root, so an installed tree has no
context-menu subdirectory. PR cenglin123#34's draft of this step used the subdirectory form
and would never have found the script - flagged there.
- The trailing ShellExec that opens the install folder is now skipped when
WizardSilent, so a silent or scripted install does not pop a window.
Verified end to end, not by inspection: a throwaway copy of the script with a fresh
AppId was compiled against the real stage and installed twice into a temp directory
with /TASKS="!contextmenu,!desktopicon" so no HKLM or machine PATH writes occurred.
The first install laid down the 2-byte placeholder; a 46-byte marker was then
written into PW.txt plus a config.json; the second install left both intact
(PW.txt preserved: True, config.json preserved: True). Uninstall removed the Start
Menu group and its own registry entry, and left the real installation's entry
pointing at C:\Program Files\SteganographierGUI untouched. The real install's
PW.txt and config.json were re-checked afterwards and are unchanged.
…tidy
The distribution root had 6 loose right-click files. Only the three the user
actually double-clicks (01-…, 02-…, InstallElevated.cmd) are entry points; the
three implementation scripts now live in context-menu\, which is also where they
sit in the repository. Combined with the _internal change, the root went from 73
loose files to 8, and the six directory names are now _internal, context-menu,
B站视频下载工具-DownKyi-1.6.1, cover_video, modules and tools.
The entry scripts probe for their script rather than hard-coding a path, because
the repository keeps them side by side with the .ps1 files while an installed tree
keeps them one level up. Probing makes one file correct in both layouts instead of
relying on the two trees staying identical.
Updated every consumer: build-release.ps1 now copies *.cmd to the stage root and
*.ps1 into context-menu\; the installer's [Code] pre-clean and install steps and
its [UninstallRun] entry point at {app}\context-menu\; smoke-test.ps1 expects the
new paths; and test-context-menu-path.ps1's fixture mirrors the shipped layout,
since a flattened fixture would not exercise the relative path the entry scripts
use - the same class of mistake that made PR cenglin123#34's [UninstallRun] draft point at a
directory that does not exist.
Not done, and deliberately: v1.3.9 shipped _internal with the hidden attribute, and
the earlier commit's guard now rejects hidden items in the stage. Hiding it was
rejected on evidence - Inno's [Files] skips hidden files, so a hidden _internal
installs nothing and the program cannot start. v1.3.9 got away with it only
because a RAR SFX was delivering the files. Measured, not assumed.
Verified without touching anything machine-wide: a throwaway AppId installed the
build to a temp directory with the contextmenu task unchecked, confirming the three
scripts land under context-menu\, the three entry points stay at the root, the
installed root holds nothing unexpected (8 files plus Inno's own unins000.*), and
that running 01-… with -WhatIf from the installed tree reaches
Install-ContextMenu.ps1 (exit 0). Registering the menu for real writes HKLM and the
machine PATH, so that step is left to the author's own install.
The branch was cut from 47c188e, before PR cenglin123#33 (security baseline: runtime secret hygiene, PW.txt placeholder enforced by exact content, registry deletion routed through an explicit allowlist) and PR cenglin123#34 (installer never overwrites runtime user data) landed on main. It was 15 ahead and 7 behind, so pushing it as-is would have produced a release branch that silently drops the registry-deletion guardrail. Four files conflicted. Resolution principle: my layout work and main's security work are both kept, neither replaces the other. - .github/workflows/ci.yml: main adds test-registry-delete-safety.ps1, my side adds the registry-write, CLI temp-archive and PowerShell 5.1 suites. Kept all of them. - installer/SteganographierGUI.iss: both sides independently added the same Excludes/onlyifdoesntexist protection. Kept one copy of the Source line and the English comments (the rest of the file is English). My [UninstallRun] and [Code] pre-clean are kept - main has no [Code] - and main's note that deletion goes through RegistryDeleteSafety.ps1 is folded into the [UninstallRun] comment. - scripts/smoke-test.ps1 and scripts/test-context-menu-path.ps1: my _internal\ and context-menu\ layout against main's flat paths. Kept my layout and added main's RegistryDeleteSafety.ps1 to both the required-file list and the fixture. The merge auto-resolved context-menu/Uninstall-ContextMenu.ps1 and scripts/validate-release.ps1 correctly: the uninstaller now dot-sources RegistryDeleteSafety.ps1 and calls Remove-RegistryKeyTolerant, and the validator carries main's exact-bytes/SHA-256 placeholder check. One merge-created break, found by main's own suite rather than by inspection: test-registry-delete-safety.ps1 asserts that every key the uninstaller deletes is allowlisted, and three keys my side restored are not - the v1.3.9-era HKCR\Directory\Background\shell\SteganographierAndHashModifier, plus the CommandStore reveal and openSteganographier entries. Without them Remove-RegistryKeyTolerant would have refused and left exactly the pre-1.3.10 orphans that restoring those names was meant to clean up. Added the three to the allowlist; all are Steganographier-owned leaf keys shaped like their neighbours. Also restored the UTF-8 BOM on smoke-test.ps1 and test-context-menu-path.ps1, which the conflict edits stripped; scripts/test-script-encoding.ps1 caught both. Verified: test-registry-delete-safety, test-context-menu-path (pwsh 7 and Windows PowerShell 5.1), test-context-menu-registry-write (5.1, 39 values over 13 hostile roots), test-cli-temp-archive, test-script-encoding (24 scripts) and validate-release all pass.
What this changes
Fixes the installer defects that put runtime user data at risk. Stacked on the security baseline (
rebuild/security-v1.3.11-20260915), which this PR targets.1. The installer no longer overwrites runtime user data
[Files]used a single{#SourceDir}\*entry withignoreversion recursesubdirs. On an upgrade that overwrotemodules\PW.txt,config.jsonandlogs\inside the install directory — andmodules\PW.txtthere is the user's real password book, not the repository placeholder.Now:
2. Uninstalling now cleans up what was installed
The
.isshad no[UninstallRun], so uninstalling left the context-menu registry entries and the machinePATHentry behind. Added:[UninstallRun]runs before files are deleted, so the script is still present. The deletion itself goes throughcontext-menu\RegistryDeleteSafety.ps1, so only allowlisted keys can be removed and critical system roots are refused.Deliberately not changed
[Registry]section and no automatic context-menu registration. Registration is performed bycontext-menu\01-安装隐写者到右键菜单.cmdtoday. Making the installer register the menu automatically changes user-visible behaviour, so it is left as an explicit follow-up decision rather than folded into a defect fix.build-release.ps1already runsscripts/validate-release.ps1, which now rejects anymodules/PW.txtthat is not the known placeholder (by exact bytes or SHA-256, never by size). So a real password book cannot be staged in the first place.Verification and limitations
.iss: expected sections present and in valid order ([Setup] [Languages] [Tasks] [Files] [Icons] [Run] [UninstallRun]);Excludes/onlyifdoesntexist/uninsneveruninstall/RunOnceIdall present; the original wildcard entry retained.ISCC.exe) is not installed on this machine, so the installer could not be built locally. This should be compiled and smoke-tested in an environment that has it before release.