diff --git a/cmd/cert-bundler/main.go b/cmd/cert-bundler/main.go index 6a7a638..3dd4ef7 100644 --- a/cmd/cert-bundler/main.go +++ b/cmd/cert-bundler/main.go @@ -299,12 +299,18 @@ func prepareArchiveFiles( ) ([]fileEntry, error) { var archiveFiles []fileEntry + // Track used filenames to avoid collisions inside archives + usedNames := make(map[string]int) + // Handle a single bundle file if outputs.IncludeSingle && len(singleFileCerts) > 0 { files, err := encodeCertsToFiles(singleFileCerts, "bundle", encoding, true) if err != nil { return nil, fmt.Errorf("failed to encode single bundle: %w", err) } + for i := range files { + files[i].name = makeUniqueName(files[i].name, usedNames) + } archiveFiles = append(archiveFiles, files...) } @@ -316,6 +322,9 @@ func prepareArchiveFiles( if err != nil { return nil, fmt.Errorf("failed to encode individual cert %s: %w", cp.path, err) } + for i := range files { + files[i].name = makeUniqueName(files[i].name, usedNames) + } archiveFiles = append(archiveFiles, files...) } } @@ -323,8 +332,9 @@ func prepareArchiveFiles( // Generate manifest if requested if outputs.Manifest { manifestContent := generateManifest(archiveFiles) + manifestName := makeUniqueName("MANIFEST", usedNames) archiveFiles = append(archiveFiles, fileEntry{ - name: "MANIFEST", + name: manifestName, content: manifestContent, }) } @@ -573,3 +583,33 @@ func generateHashFile(path string, files []string) error { return nil } + + +// makeUniqueName ensures that each file name within the archive is unique by appending +// an incremental numeric suffix before the extension when collisions occur. +// Example: "root.pem" -> "root-2.pem", "root-3.pem", etc. +func makeUniqueName(name string, used map[string]int) string { + // If unused, mark and return as-is + if _, ok := used[name]; !ok { + used[name] = 1 + return name + } + + ext := filepath.Ext(name) + base := strings.TrimSuffix(name, ext) + // Track a counter per base+ext key + key := base + ext + counter := used[key] + if counter < 1 { + counter = 1 + } + for { + counter++ + candidate := fmt.Sprintf("%s-%d%s", base, counter, ext) + if _, exists := used[candidate]; !exists { + used[key] = counter + used[candidate] = 1 + return candidate + } + } +} diff --git a/cmd/cert-bundler/testdata/pkg/bundle.sha256 b/cmd/cert-bundler/testdata/pkg/bundle.sha256 deleted file mode 100644 index c8bed0c..0000000 --- a/cmd/cert-bundler/testdata/pkg/bundle.sha256 +++ /dev/null @@ -1,4 +0,0 @@ -5ed8bf9ed693045faa8a5cb0edc4a870052e56aef6291ce8b1604565affbc2a4 core_certs.zip -e59eddc590d2f7b790a87c5b56e81697088ab54be382c0e2c51b82034006d308 core_certs.tgz -51b9b63b1335118079e90700a3a5b847c363808e9116e576ca84f301bc433289 google_certs.tgz -3d1910ca8835c3ded1755a8c7d6c48083c2f3ff68b2bfbf932aaf27e29d0a232 lets_encrypt.zip diff --git a/cmd/cert-bundler/testdata/pkg/core_certs.tgz b/cmd/cert-bundler/testdata/pkg/core_certs.tgz deleted file mode 100644 index 7efc31d..0000000 Binary files a/cmd/cert-bundler/testdata/pkg/core_certs.tgz and /dev/null differ diff --git a/cmd/cert-bundler/testdata/pkg/core_certs.zip b/cmd/cert-bundler/testdata/pkg/core_certs.zip deleted file mode 100644 index f26676f..0000000 Binary files a/cmd/cert-bundler/testdata/pkg/core_certs.zip and /dev/null differ diff --git a/cmd/cert-bundler/testdata/pkg/google_certs.tgz b/cmd/cert-bundler/testdata/pkg/google_certs.tgz deleted file mode 100644 index 13a953f..0000000 Binary files a/cmd/cert-bundler/testdata/pkg/google_certs.tgz and /dev/null differ diff --git a/cmd/cert-bundler/testdata/pkg/lets_encrypt.zip b/cmd/cert-bundler/testdata/pkg/lets_encrypt.zip deleted file mode 100644 index 86d4884..0000000 Binary files a/cmd/cert-bundler/testdata/pkg/lets_encrypt.zip and /dev/null differ