fix(cli): harden restore preflight validation
This commit is contained in:
@@ -233,6 +233,91 @@ func TestPreflightRejectsTraversalAndSymlinkInsideVolumeTar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreflightRejectsArchivesThatExceedConfiguredProcessingLimits(t *testing.T) {
|
||||
installation := preflightTestInstallation(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
spec preflightArchiveSpec
|
||||
limits PreflightLimits
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "member count",
|
||||
spec: preflightArchiveSpec{entries: []preflightArchiveEntry{{path: "one", body: []byte("one")}}},
|
||||
limits: PreflightLimits{MaxMembers: 1, MaxUncompressedBytes: 1 << 20, MaxCompressionRatio: 100},
|
||||
wantErr: "member limit",
|
||||
},
|
||||
{
|
||||
name: "uncompressed bytes",
|
||||
spec: preflightArchiveSpec{entries: []preflightArchiveEntry{{path: "large", body: []byte("123456789")}}},
|
||||
limits: PreflightLimits{MaxMembers: 10, MaxUncompressedBytes: 1, MaxCompressionRatio: 100},
|
||||
wantErr: "uncompressed-size limit",
|
||||
},
|
||||
{
|
||||
name: "compression ratio",
|
||||
spec: preflightArchiveSpec{entries: []preflightArchiveEntry{{
|
||||
path: "compressed", body: []byte(strings.Repeat("A", 4096)), method: zip.Deflate,
|
||||
}}},
|
||||
limits: PreflightLimits{MaxMembers: 10, MaxUncompressedBytes: 1 << 20, MaxCompressionRatio: 2},
|
||||
wantErr: "compression-ratio limit",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
archive := filepath.Join(t.TempDir(), "limited.zip")
|
||||
writePreflightArchive(t, archive, test.spec)
|
||||
_, err := Preflight(context.Background(), installation, PreflightRequest{
|
||||
Archive: archive, Confirm: true, Limits: test.limits,
|
||||
}, permissivePreflightDependencies())
|
||||
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Fatalf("Preflight() error = %v, want %q", err, test.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreflightRevalidationRefusesAnArchivePathThatWasReplaced(t *testing.T) {
|
||||
installation := preflightTestInstallation(t)
|
||||
archive := filepath.Join(t.TempDir(), "checked.zip")
|
||||
writePreflightArchive(t, archive, preflightArchiveSpec{
|
||||
entries: []preflightArchiveEntry{{path: "configuration/operator.env", body: []byte("validated")}},
|
||||
})
|
||||
|
||||
result, err := Preflight(context.Background(), installation, PreflightRequest{Archive: archive, Confirm: true}, permissivePreflightDependencies())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer result.CloseArchive()
|
||||
|
||||
replacement := filepath.Join(t.TempDir(), "replacement.zip")
|
||||
writePreflightArchive(t, replacement, preflightArchiveSpec{
|
||||
entries: []preflightArchiveEntry{{path: "configuration/operator.env", body: []byte("unchecked")}},
|
||||
})
|
||||
if err := os.Rename(replacement, archive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := result.RevalidateArchive(); err == nil || !strings.Contains(err.Error(), "changed") {
|
||||
t.Fatalf("RevalidateArchive() error = %v, want replaced path refusal", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreflightRejectsArchiveEntryWithModeDifferentFromManifest(t *testing.T) {
|
||||
installation := preflightTestInstallation(t)
|
||||
archive := filepath.Join(t.TempDir(), "mode-mismatch.zip")
|
||||
manifestMode := uint32(0o600)
|
||||
writePreflightArchive(t, archive, preflightArchiveSpec{
|
||||
entries: []preflightArchiveEntry{{
|
||||
path: "configuration/operator.env", body: []byte("safe"), mode: 0o644, manifestMode: &manifestMode,
|
||||
}},
|
||||
})
|
||||
|
||||
_, err := Preflight(context.Background(), installation, PreflightRequest{Archive: archive, Confirm: true}, permissivePreflightDependencies())
|
||||
if err == nil || !strings.Contains(err.Error(), "mode mismatch") {
|
||||
t.Fatalf("Preflight() error = %v, want mode mismatch", err)
|
||||
}
|
||||
}
|
||||
|
||||
type preflightArchiveSpec struct {
|
||||
installationID string
|
||||
schemaVersion int
|
||||
@@ -243,12 +328,15 @@ type preflightArchiveSpec struct {
|
||||
}
|
||||
|
||||
type preflightArchiveEntry struct {
|
||||
path string
|
||||
body []byte
|
||||
checksum string
|
||||
kind string
|
||||
sensitive bool
|
||||
symlink bool
|
||||
path string
|
||||
body []byte
|
||||
checksum string
|
||||
kind string
|
||||
sensitive bool
|
||||
symlink bool
|
||||
mode os.FileMode
|
||||
manifestMode *uint32
|
||||
method uint16
|
||||
}
|
||||
|
||||
type preflightRawArchiveEntry struct {
|
||||
@@ -302,7 +390,14 @@ func writePreflightArchive(t *testing.T, archivePath string, spec preflightArchi
|
||||
sourcePath = "/protected/secret"
|
||||
owner = "external-secret"
|
||||
}
|
||||
manifest.Entries = append(manifest.Entries, Entry{Path: entry.path, Kind: kind, Owner: owner, SourcePath: sourcePath, SHA256: checksum, Size: int64(len(entry.body)), Archived: true, Sensitive: entry.sensitive})
|
||||
mode := uint32(entry.mode.Perm())
|
||||
if mode == 0 {
|
||||
mode = 0o600
|
||||
}
|
||||
if entry.manifestMode != nil {
|
||||
mode = *entry.manifestMode
|
||||
}
|
||||
manifest.Entries = append(manifest.Entries, Entry{Path: entry.path, Kind: kind, Owner: owner, SourcePath: sourcePath, SHA256: checksum, Size: int64(len(entry.body)), Mode: mode, Archived: true, Sensitive: entry.sensitive})
|
||||
}
|
||||
manifestBytes, err := manifest.JSON()
|
||||
if err != nil {
|
||||
@@ -322,7 +417,16 @@ func writePreflightArchive(t *testing.T, archivePath string, spec preflightArchi
|
||||
defer file.Close()
|
||||
writer := zip.NewWriter(file)
|
||||
for _, entry := range spec.entries {
|
||||
header := &zip.FileHeader{Name: entry.path, Method: zip.Store}
|
||||
method := entry.method
|
||||
if method == 0 {
|
||||
method = zip.Store
|
||||
}
|
||||
header := &zip.FileHeader{Name: entry.path, Method: method}
|
||||
mode := entry.mode
|
||||
if mode == 0 {
|
||||
mode = 0o600
|
||||
}
|
||||
header.SetMode(mode)
|
||||
if entry.symlink {
|
||||
header.SetMode(os.ModeSymlink | 0o777)
|
||||
}
|
||||
@@ -343,7 +447,9 @@ func writePreflightArchive(t *testing.T, archivePath string, spec preflightArchi
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
created, err := writer.Create(ManifestPath)
|
||||
manifestHeader := &zip.FileHeader{Name: ManifestPath, Method: zip.Store}
|
||||
manifestHeader.SetMode(0o600)
|
||||
created, err := writer.CreateHeader(manifestHeader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user