fix: correct verify's ciphertext field map against the models

This commit is contained in:
2026-09-07 14:45:36 +00:00
parent 590a85fe3a
commit 7df71ed8d4
3 changed files with 72 additions and 7 deletions
+30 -1
View File
@@ -3,6 +3,7 @@ package backup
import (
"errors"
"fmt"
"strings"
"time"
)
@@ -16,6 +17,10 @@ const ManifestName = "manifest.json"
// ErrUnknownFormat is returned for an archive this build cannot read.
var ErrUnknownFormat = errors.New("unsupported archive format version")
// ErrBadCollectionName is returned for a manifest naming a collection that
// cannot safely be used as a path component.
var ErrBadCollectionName = errors.New("manifest names an unusable collection")
// CollectionEntry describes one collection in the archive. Bytes and SHA256
// cover the uncompressed .bson member, which is what restore verifies before
// writing anything.
@@ -49,6 +54,27 @@ func (m Manifest) Check() error {
return fmt.Errorf("%w: archive is version %d, this build reads version %d",
ErrUnknownFormat, m.FormatVersion, FormatVersion)
}
// Collection names become path components inside the extraction directory,
// and an archive is operator-supplied input that may not be one we wrote.
for _, c := range m.Collections {
if err := checkCollectionName(c.Name); err != nil {
return err
}
}
return nil
}
// checkCollectionName refuses a name that could escape a directory when joined
// as a path component.
func checkCollectionName(name string) error {
switch {
case name == "":
return fmt.Errorf("%w: a collection entry has no name", ErrBadCollectionName)
case name == "." || name == "..":
return fmt.Errorf("%w: %q", ErrBadCollectionName, name)
case strings.ContainsAny(name, "/\\"), strings.Contains(name, ".."):
return fmt.Errorf("%w: %q", ErrBadCollectionName, name)
}
return nil
}
@@ -68,6 +94,9 @@ func (m Manifest) Collection(name string) (CollectionEntry, bool) {
// match the archive, this is the list of what will be unreadable afterwards,
// and an operator deserves to see it before the write rather than discover it
// a week later.
//
// settings is not in the list: it holds no encrypted material. Its ESO read
// token is a SHA-256 hash, not ciphertext.
func CiphertextCollections() []string {
return []string{"keys", "secrets", "auth_providers", "console_sessions", "settings"}
return []string{"keys", "secrets", "auth_providers", "console_sessions"}
}