fix(archive): correct flat-layout path and entry-count wording

Drop the phantom <basename>/ level for flat-layout installs (profileDir == root), say "entries" not "files" (count is per source path), and add ZipDir/Unzip (>1MB, empty, Zip-Slip) + flat-layout tests.
This commit is contained in:
moonD4rk
2026-06-06 21:04:24 +08:00
parent 96ed041fa0
commit 8851ef63ba
5 changed files with 126 additions and 3 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ type Archivable interface {
// BuildArchive packs each browser's decryption-relevant files into a zip whose internal layout is
// <browser-key>/<User Data layout>, so a restore can re-expand it and decrypt with a keys.json. Files
// are staged through a locked-file session first because Windows holds exclusive SQLite locks. Returns
// the number of files captured.
// the number of source entries staged (a directory source counts once).
func BuildArchive(browsers []Browser, categories []types.Category, outPath string) (int, error) {
session, err := filemanager.NewSession()
if err != nil {
+6 -1
View File
@@ -32,7 +32,12 @@ func (b *Browser) ArchiveSources(categories []types.Category) []ArchiveSource {
}
}
for _, p := range b.profiles {
profileRel := filepath.Base(p.profileDir)
// Flat-layout installs hold data directly under UserDataDir (profileDir == root); skip the
// basename so the archive matches the real layout instead of inserting a phantom level.
profileRel := ""
if p.profileDir != b.cfg.UserDataDir {
profileRel = filepath.Base(p.profileDir)
}
for _, marker := range profileMarkers {
abs := filepath.Join(p.profileDir, marker)
if fileutil.FileExists(abs) {
+30
View File
@@ -56,3 +56,33 @@ func TestArchiveSources_ForwardSlashLayout(t *testing.T) {
t.Errorf("missing Local State entry (User Data root file), got %+v", srcs)
}
}
func TestArchiveSources_FlatLayoutNoExtraLevel(t *testing.T) {
// Flat-layout install: data lives directly under UserDataDir with no Default/ subdir, so
// discoverProfiles falls back to UserDataDir itself as the profile (profileDir == root).
udd := t.TempDir()
if err := os.WriteFile(filepath.Join(udd, "History"), []byte("x"), 0o600); err != nil {
t.Fatal(err)
}
b, err := NewBrowser(types.BrowserConfig{Key: "opera", Name: "opera", Kind: types.Chromium, UserDataDir: udd})
if err != nil || b == nil {
t.Fatalf("NewBrowser: b=%v err=%v", b, err)
}
srcs := b.ArchiveSources([]types.Category{types.History})
phantom := filepath.Base(udd) + "/"
var gotHistory bool
for _, s := range srcs {
if strings.HasPrefix(s.LayoutRel, phantom) {
t.Errorf("flat layout must not insert a %q level, got %q", phantom, s.LayoutRel)
}
if s.LayoutRel == "History" {
gotHistory = true
}
}
if !gotHistory {
t.Errorf("expected History at archive root, got %+v", srcs)
}
}