Files
metin-launcher/src/Metin2Launcher
Jan Nedbal db1f2f435b SECURITY: prune no longer recurses clientRoot — ledger-based only
Post-mortem: the previous implementation of PruneStaleFiles walked
Directory.EnumerateFiles(clientRoot, "*", SearchOption.AllDirectories)
and deleted anything not listed in the current manifest. That made
clientRoot unsafe to set to anything other than a dedicated install
directory. Because clientRoot is Directory.GetCurrentDirectory(), any
user who ran Metin2Launcher.exe from their home directory (or worse,
from a parent directory containing other projects) would have had
every unrelated file in that tree silently deleted — including
.ssh keys, .bashrc, docs, source trees. One of Jan's colleagues
hit exactly this tonight and lost a significant chunk of their home
directory.

The blast radius was enormous and entirely my fault. This commit
switches prune to a strict ledger-based model so the launcher can
ONLY delete files it itself wrote:

  .updates/applied-files.txt  — newline list of relative paths this
                                launcher has ever successfully
                                installed into clientRoot.

On prune:
  1. Read the ledger.
  2. For every entry not in the current manifest's file set (plus
     the top-level launcher.path), delete the file at that relative
     path inside clientRoot — if and only if PathSafety.ResolveInside
     accepts the resolved path (defense against traversal/symlinks).
  3. Rewrite the ledger to exactly match the current manifest.

Files the launcher never wrote are invisible to prune. A fresh
install dir has no ledger, so prune is a no-op on the first run and
subsequent runs only touch files listed in the ledger. Even if a user
points the launcher at ~/ with valuable data, prune cannot touch
anything it didn't put there.

Other hardening:
- PathSafety.ResolveInside is now invoked for every prune target, so
  a maliciously crafted manifest can't name "../../etc/passwd".
- Ledger write happens after apply so a crash mid-apply doesn't
  leave a stale ledger that would prune real user files on the next
  run.

Immediate mitigations taken outside this commit:
- Pulled the published Metin2Launcher.exe off updates.jakubkadlec.dev/
  launcher/ and replaced it with a README.txt warning, so no new user
  can download the dangerous binary.
- Will rebuild and re-upload the safe binary before re-announcing.

Followup:
- Add a Gitea issue documenting the incident and the ledger contract.
- Tests for the ledger read/write and for the "ledger empty = no
  prune" safety case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:13:57 +02:00
..