7b5dfeac3f
F2 (security): the base-file removal in materializeStackVersion used the unclamped
filepath.Join while the read (GetFileContent) and write
(StoreStackFileFromBytesByVersion) in the same function are clamped. An
attacker-influenceable fileName (stack.EntryPoint/AdditionalFiles via DB
restore/import) could escape basePath in this file-deleting path. Use the clamped
filesystem.JoinPaths, consistent with the read/write.
F1 (test): the adopt branch (complete v{N} on disk but stale DB metadata -> repoint
ProjectPath + set StackFileVersion + seed Versions) is exactly the post-crash state
the crash-safety guarantee relies on (crash between materialize and DB persist), but
no test hit it — existing complete-v-dir tests have consistent metadata and take the
no-op short-circuit. Add TestMigrateStackFileVersions_2_44_0_AdoptCompleteVDirStaleMetadata:
seed a complete v1 on disk, stack with stale flat ProjectPath + empty Versions, no
base copies; assert the migration repoints ProjectPath to v1, sets StackFileVersion=1,
seeds Versions, and leaves the v1 files intact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>