Skip to content

Installer: never overwrite runtime user data, and clean up on uninstall - #34

Merged
cenglin123 merged 1 commit into
rebuild/security-v1.3.11-20260915from
fix/installer-user-data-20260919
Sep 20, 2026
Merged

cenglin123 merged 1 commit into
rebuild/security-v1.3.11-20260915from
fix/installer-user-data-20260919

Conversation

@cenglin123

Copy link
Copy Markdown
Owner

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 with ignoreversion recursesubdirs. On an upgrade that overwrote modules\PW.txt, config.json and logs\ inside the install directory — and modules\PW.txt there is the user's real password book, not the repository placeholder.

Now:

Source: "{#SourceDir}\*"; DestDir: "{app}"; Excludes: "config.json,logs\*,modules\PW.txt"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#SourceDir}\modules\PW.txt"; DestDir: "{app}\modules"; Flags: onlyifdoesntexist uninsneveruninstall
  • the wildcard entry excludes the three runtime user-data paths;
  • the placeholder is installed only when the target does not already exist, and is never removed by the uninstaller.

2. Uninstalling now cleans up what was installed

The .iss had no [UninstallRun], so uninstalling left the context-menu registry entries and the machine PATH entry behind. Added:

[UninstallRun]
Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\context-menu\Uninstall-ContextMenu.ps1"" -InstallRoot ""{app}"""; Flags: runhidden; RunOnceId: "RemoveContextMenu"

[UninstallRun] runs before files are deleted, so the script is still present. The deletion itself goes through context-menu\RegistryDeleteSafety.ps1, so only allowlisted keys can be removed and critical system roots are refused.

Deliberately not changed

  • No [Registry] section and no automatic context-menu registration. Registration is performed by context-menu\01-安装隐写者到右键菜单.cmd today. 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.
  • The placeholder check is not duplicated in the installer: build-release.ps1 already runs scripts/validate-release.ps1, which now rejects any modules/PW.txt that 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

  • Structural self-check of the edited .iss: expected sections present and in valid order ([Setup] [Languages] [Tasks] [Files] [Icons] [Run] [UninstallRun]); Excludes / onlyifdoesntexist / uninsneveruninstall / RunOnceId all present; the original wildcard entry retained.
  • Not compile-tested: Inno Setup 6 (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.

… uninstall

- [Files]: exclude config.json / logs\\* / modules\\PW.txt from the ignoreversion wildcard

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@cenglin123
cenglin123 merged commit 3d2c6c5 into rebuild/security-v1.3.11-20260915 Sep 20, 2026
1 check passed
AzureXuanVerse pushed a commit to AzureXuanVerse/SteganographierGUI that referenced this pull request Sep 21, 2026
…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.
AzureXuanVerse pushed a commit to AzureXuanVerse/SteganographierGUI that referenced this pull request Sep 21, 2026
…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.
AzureXuanVerse pushed a commit to AzureXuanVerse/SteganographierGUI that referenced this pull request Sep 21, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant