feat(restore): cross-platform restore via dump engine rebuild (#606) (#611)

* feat(restore): cross-platform restore via dump engine rebuild (#606)

Restore previously required the dump's origin OS, overlaying keys onto locally-discovered browsers. It now rebuilds Chromium engines from the dump's vaults (v2 adds engine kind), so copied data or an archive zip decrypts on any OS.

* fix(restore): polish help text, drop dead check, dedup dump kinds

pflag treats backticked words in flag usage as the value placeholder,
so --data-zip rendered as "--data-zip archive" in help output.
This commit is contained in:
Roger
2026-06-12 20:53:00 +08:00
committed by GitHub
parent 8d8bd81790
commit bf96ba8c80
11 changed files with 493 additions and 189 deletions
+6 -3
View File
@@ -10,7 +10,7 @@ import (
"time"
)
const DumpVersion = "1"
const DumpVersion = "2"
// Dump is the portable, cross-host container for Chromium master keys — produce it on one host to
// decrypt copied profile data on another without DPAPI / ABE / Keychain / D-Bus.
@@ -30,8 +30,11 @@ type Host struct {
}
// Vault groups profiles sharing master keys (master keys are per-installation, not per-profile).
// Browser is the lookup key (e.g. "chrome"); Kind is the engine ("chromium"|"chromium-yandex"|
// "chromium-opera") so a consumer can rebuild the engine without the local browser table.
type Vault struct {
Browser string `json:"browser"`
Kind string `json:"kind"`
UserDataDir string `json:"user_data_dir"`
Profiles []string `json:"profiles"`
Keys MasterKeys `json:"keys"`
@@ -66,8 +69,8 @@ func (d Dump) WriteJSON(w io.Writer) error {
return nil
}
// ReadJSON parses a Dump and rejects versions this build can't interpret — a silent misparse of a
// future v2 schema is worse than a clear error.
// ReadJSON parses a Dump and rejects any version this build can't interpret — a silent misparse of an
// unrecognized schema is worse than a clear error.
func ReadJSON(r io.Reader) (Dump, error) {
var d Dump
dec := json.NewDecoder(r)
+29
View File
@@ -39,3 +39,32 @@ func TestReadJSON_AcceptsCurrentVersion(t *testing.T) {
t.Errorf("Version = %q, want %q", parsed.Version, DumpVersion)
}
}
func TestDump_VaultKindRoundTrip(t *testing.T) {
d := NewDump()
d.Vaults = append(d.Vaults, Vault{
Browser: "chrome",
Kind: "chromium",
UserDataDir: "/p",
Profiles: []string{"Default"},
Keys: MasterKeys{V10: []byte{0x01}},
})
var buf bytes.Buffer
if err := d.WriteJSON(&buf); err != nil {
t.Fatalf("WriteJSON: %v", err)
}
parsed, err := ReadJSON(&buf)
if err != nil {
t.Fatalf("ReadJSON: %v", err)
}
if len(parsed.Vaults) != 1 {
t.Fatalf("Vaults len = %d, want 1", len(parsed.Vaults))
}
if parsed.Vaults[0].Kind != "chromium" {
t.Errorf("Vault.Kind round-trip: got %q, want %q", parsed.Vaults[0].Kind, "chromium")
}
if parsed.Vaults[0].Browser != "chrome" {
t.Errorf("Vault.Browser round-trip: got %q, want %q", parsed.Vaults[0].Browser, "chrome")
}
}