Skip to content

Commit 3a4cdb7

Browse files
committed
Fix unsafe local mode to load rotated roots from disk
Previously, unsafe local mode would only use the initial trusted root and wouldn't load any rotated roots from disk. This meant that if roots were rotated while online, then switching to unsafe local mode would fail to recognize the newer root versions. This fix adds: - loadRootFromDisk(): New method that loads rotated root metadata from local disk, similar to how online mode loads from remote - Updated unsafeLocalRefresh(): Now calls loadRootFromDisk() to load any rotated roots before loading other metadata - Updated loadRoot(): Persists versioned root files (N.root.json) to disk for offline use by unsafe local mode This allows unsafe local mode to properly handle root rotations when operating offline, as long as the rotated roots were previously downloaded in online mode.
1 parent 999f81a commit 3a4cdb7

File tree

1 file changed

+45
-1
lines changed

1 file changed

+45
-1
lines changed

metadata/updater/updater.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,12 @@ func (update *Updater) onlineRefresh() error {
148148
// The metadata on disk are verified against the provided root though,
149149
// and expiration dates are verified.
150150
func (update *Updater) unsafeLocalRefresh() error {
151-
// Root is already loaded
151+
// Load any rotated roots from disk
152+
err := update.loadRootFromDisk()
153+
if err != nil {
154+
return err
155+
}
156+
152157
// load timestamp
153158
var p = filepath.Join(update.cfg.LocalMetadataDir, metadata.TIMESTAMP)
154159
data, err := update.loadLocalMetadata(p)
@@ -511,6 +516,45 @@ func (update *Updater) loadRoot() error {
511516
if err != nil {
512517
return err
513518
}
519+
// also persist versioned root for offline use (e.g., unsafe local mode)
520+
versionedRootName := fmt.Sprintf("%d.%s", nextVersion, metadata.ROOT)
521+
err = update.persistMetadata(versionedRootName, data)
522+
if err != nil {
523+
return err
524+
}
525+
}
526+
}
527+
return nil
528+
}
529+
530+
// loadRootFromDisk loads root metadata from local disk. Sequentially loads and
531+
// verifies every newer root metadata version available on disk.
532+
// This is used by unsafe local mode to support root rotation when operating offline.
533+
func (update *Updater) loadRootFromDisk() error {
534+
// calculate boundaries
535+
lowerBound := update.trusted.Root.Signed.Version + 1
536+
upperBound := lowerBound + update.cfg.MaxRootRotations
537+
538+
// loop until we find the latest available version of root on disk
539+
for nextVersion := lowerBound; nextVersion < upperBound; nextVersion++ {
540+
versionedRootName := fmt.Sprintf("%d.%s", nextVersion, metadata.ROOT)
541+
rootPath := filepath.Join(update.cfg.LocalMetadataDir, fmt.Sprintf("%s.json", url.PathEscape(versionedRootName)))
542+
543+
// try to load versioned root from disk
544+
data, err := os.ReadFile(rootPath)
545+
if err != nil {
546+
if os.IsNotExist(err) {
547+
// no more root versions available on disk, stop the loop
548+
break
549+
}
550+
// some other error occurred
551+
return err
552+
}
553+
554+
// verify and load the root metadata
555+
_, err = update.trusted.UpdateRoot(data)
556+
if err != nil {
557+
return err
514558
}
515559
}
516560
return nil

0 commit comments

Comments
 (0)