Skip to content

Security & code audit — repojacking fix, crash fix, hardening#46

Merged
Daolyap merged 1 commit into
Fable/Overhaulfrom
Fable/Security-Code-Review
Jun 11, 2026
Merged

Security & code audit — repojacking fix, crash fix, hardening#46
Daolyap merged 1 commit into
Fable/Overhaulfrom
Fable/Security-Code-Review

Conversation

@Daolyap

@Daolyap Daolyap commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Full security and code audit of the service layer and window code-behind, stacked on top of #45 (Fable/Overhaul).

Security findings (fixed)

Severity Finding Fix
High Repojacking exposure in the auto-updater. The repo was renamed MoneyShotMoney-Shot, but AutoUpdateService still pointed at the old name. GitHub redirects an old repo name only until someone re-registers it — at which point they control the update channel of every installed copy. Constants updated to Daolyap/Money-Shot (verified the new endpoint serves releases + SHA256SUMS.txt); rule documented in CLAUDE.md.
Medium Optional GitHub token was read from Environment.GetEnvironmentVariable("") — a disabled stub — while the 401 error message told users to configure MONEYSHOT_GITHUB_TOKEN. Wired to MONEYSHOT_GITHUB_TOKEN; treated as a secret, never logged.
Medium SaveService system-directory check used a bare StartsWith, so C:\Windows also matched the unrelated C:\WindowsBackup. Prefix comparison now uses trailing separators.
Low A tampered settings.json could set HistoryRetentionCount to arbitrary values. Clamped to the UI range (0–500) in ValidateAndSanitizeSettings.

Crash / correctness bugs (fixed)

  • Second-instance crash: App.OnExit called ReleaseMutex() on the single-instance mutex the second instance never owned → ApplicationException on its way out. Now only the owning instance releases.
  • Hotkey handler exceptions were process-fatal: an exception escaping the WM_HOTKEY hook tears down the app. Now caught and logged. Hotkey registration failures (combination owned by another app) were silently ignored — now logged.
  • Editor Save dialog ignored settings: the configured default save folder and default file format were never applied to the dialog (initial dir, filter, extension, filename all PNG/last-used). Now honoured.
  • IsPointInElement hand-rolled NaN guards instead of using CanvasPosition (explicitly forbidden by the repo''s resize/drag rules — historical source of snap-to-(0,0) bugs).

Optimisations & quality

  • New MemoryTrimmer service shared by both editor-close paths — opening a capture from History previously skipped the working-set trim and left hundreds of MB resident.
  • JPEG saves now use QualityLevel = 90 (the default 75 visibly smears screenshot text).
  • Logger records the full exception + stack trace at Error level (message-only lines were rarely diagnosable).
  • Dead code removed (SetupToolbar, unused usings).

Audited and intentionally unchanged

  • ScreenshotService HBITMAP lifecycle (correct: DeleteObject in finally, Freeze() for cross-thread).
  • Zip extraction in the updater (no zip-slip: entry name is only compared, destination path is fixed).
  • SHA-256 release verification flow and SemVer-only comparison (correct as designed).
  • Settings JSON deserialization (System.Text.Json, no polymorphism — safe defaults).
  • Registry writes (HKCU only, quoted exe path for the Run key).

Follow-ups recorded in Opus-Speaks.md (not fixed here)

  • E2: Region selector is only pixel-accurate at 100 % display scaling (PerMonitorV2 manifest vs. DIP↔pixel 1:1 mapping). Needs per-monitor selector windows — too invasive to bundle here.
  • E3: The self-update swap script runs unelevated and cannot replace a per-machine Program Files install.
  • E (existing): Authenticode signing still open.

Verification

  • dotnet build clean, 109/109 tests (14 new: SaveService path validation, retention clamping).
  • Editor window re-rendered through the off-screen harness after changes — loads and themes correctly.

🤖 Generated with Claude Code

Security:
- Auto-updater now targets the repository current name (Daolyap/Money-Shot).
  The old name only redirects until someone re-registers it, at which point
  they would own the update channel (repojacking).
- Optional GitHub token properly wired to MONEYSHOT_GITHUB_TOKEN (the 401
  guidance already referenced it; the read was a disabled empty-string stub).
- SaveService system-directory check compares with a trailing separator so
  C:\Windows blocks C:\Windows\... but not C:\WindowsBackup siblings.
- HistoryRetentionCount clamped to 0-500 during settings sanitization.

Bugs:
- Second app instance crashed on exit: OnExit called ReleaseMutex on the
  single-instance mutex it never owned (ApplicationException).
- Hotkey actions that throw no longer propagate out of the WndProc hook
  (process-fatal); they are caught and logged. Registration failures
  (combination owned by another app) are now logged instead of silent.
- Editor Save dialog now honours the configured default save folder and
  file format; previously both settings were ignored.
- IsPointInElement used hand-rolled NaN guards instead of CanvasPosition
  (forbidden by the resize/drag design rules).

Improvements:
- MemoryTrimmer service shared by both editor-close paths; opening a
  capture from History now gets the same working-set trim as the capture
  flow (previously left ~hundreds of MB resident).
- JPEG saves use QualityLevel 90 (default 75 smears screenshot text).
- Logger includes full exception + stack trace at Error level.
- Dead SetupToolbar() removed; App.xaml.cs unused usings dropped.
- 14 new regression tests (SaveService path validation, retention clamp);
  109/109 passing.

Audit follow-ups recorded in Opus-Speaks.md: region-selector DPI accuracy
at >100% scaling (E2), auto-update vs per-machine installs (E3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Daolyap Daolyap merged commit 6fe0ec1 into Fable/Overhaul Jun 11, 2026
@Daolyap Daolyap deleted the Fable/Security-Code-Review branch June 11, 2026 22:47
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