Skip to content

Commit 983280c

Browse files
authored
Add image name filters to src sbom fetch command (#1172)
* Added --image and --exclude-image filters to `src sbom fetch` command
1 parent e992150 commit 983280c

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ All notable changes to `src-cli` are documented in this file.
1111

1212
## Unreleased
1313

14+
### Added
15+
16+
- SBOM support: Added `--image` and `--exclude-image` flags to `src sbom fetch` for filtering which docker images SBOMs are fetched for. Both flags support glob patterns (e.g., `frontend`, `*api*`) and comma-separated lists. The `sourcegraph/` image name prefix is optional.
17+
1418
## 6.4.0
1519

1620
## 6.3.0

cmd/src/sbom_fetch.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,32 @@ func init() {
2525
2626
Usage:
2727
28-
src sbom fetch -v <version>
28+
src sbom fetch -v <version> [--image <image-patterns>] [--exclude-image <exclude-patterns>]
2929
3030
Examples:
3131
32-
$ src sbom fetch -v 5.8.0 # Fetch all SBOMs for the 5.8.0 release
32+
$ src sbom fetch -v 5.8.0 # Fetch all SBOMs for the 5.8.0 release
3333
34-
$ src sbom fetch -v 5.8.123 -internal -d /tmp/sboms # Fetch all SBOMs for the internal 5.8.123 release and store them in /tmp/sboms
34+
$ src sbom fetch -v 5.8.0 --image frontend # Fetch SBOM only for the frontend image
35+
36+
$ src sbom fetch -v 5.8.0 --image "redis*" # Fetch SBOMs for all images with names beginning with 'redis'
37+
38+
$ src sbom fetch -v 5.8.0 --image "frontend,redis*" # Fetch SBOMs for frontend, and all redis images
39+
40+
$ src sbom fetch -v 5.8.0 --exclude-image "sg,*redis*" # Fetch SBOMs for all images, except sg and redis
41+
42+
$ src sbom fetch -v 5.8.0 --image "postgres*" --exclude-image "*exporter*" # Fetch SBOMs for all postgres images, except exporters
43+
44+
$ src sbom fetch -v 5.8.123 -internal -d /tmp/sboms # Fetch all SBOMs for the internal 5.8.123 release and store them in /tmp/sboms
3545
`
3646

3747
flagSet := flag.NewFlagSet("fetch", flag.ExitOnError)
3848
versionFlag := flagSet.String("v", "", "The version of Sourcegraph to fetch SBOMs for.")
3949
outputDirFlag := flagSet.String("d", "sourcegraph-sboms", "The directory to store validated SBOMs in.")
4050
internalReleaseFlag := flagSet.Bool("internal", false, "Fetch SBOMs for an internal release. Defaults to false.")
4151
insecureIgnoreTransparencyLogFlag := flagSet.Bool("insecure-ignore-tlog", false, "Disable transparency log verification. Defaults to false.")
52+
imageFlag := flagSet.String("image", "", "Filter list of image names, to only fetch SBOMs for Docker images with names matching these patterns. Supports literal names, like frontend, and glob patterns like '*postgres*'. Multiple patterns can be specified as a comma-separated list (e.g., 'frontend,*postgres-1?-*'). The 'sourcegraph/' prefix is optional. If not specified, SBOMs for all images are fetched.")
53+
excludeImageFlag := flagSet.String("exclude-image", "", "Exclude Docker images with names matching these patterns from being fetched. Supports the same formats as --image. Takes precedence over --image filters.")
4254

4355
handler := func(args []string) error {
4456
c := cosignConfig{
@@ -73,6 +85,24 @@ Examples:
7385
c.insecureIgnoreTransparencyLog = true
7486
}
7587

88+
if imageFlag != nil && *imageFlag != "" {
89+
// Parse comma-separated patterns
90+
patterns := strings.Split(*imageFlag, ",")
91+
for i, pattern := range patterns {
92+
patterns[i] = strings.TrimSpace(pattern)
93+
}
94+
c.imageFilters = patterns
95+
}
96+
97+
if excludeImageFlag != nil && *excludeImageFlag != "" {
98+
// Parse comma-separated exclude patterns
99+
patterns := strings.Split(*excludeImageFlag, ",")
100+
for i, pattern := range patterns {
101+
patterns[i] = strings.TrimSpace(pattern)
102+
}
103+
c.excludeImageFilters = patterns
104+
}
105+
76106
out := output.NewOutput(flagSet.Output(), output.OutputOpts{Verbose: *verbose})
77107

78108
if err := verifyCosign(); err != nil {

cmd/src/sbom_utils.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"net/http"
1111
"os/exec"
1212
"path"
13+
"path/filepath"
1314
"regexp"
1415
"strings"
1516
"time"
@@ -26,6 +27,8 @@ type cosignConfig struct {
2627
version string
2728
internalRelease bool
2829
insecureIgnoreTransparencyLog bool
30+
imageFilters []string
31+
excludeImageFilters []string
2932
}
3033

3134
// TokenResponse represents the JSON response from dockerHub's token service
@@ -251,7 +254,21 @@ func (c cosignConfig) getImageList() ([]string, error) {
251254
if image != "" {
252255
// Strip off a version suffix if present
253256
parts := strings.SplitN(image, ":", 2)
254-
images = append(images, parts[0])
257+
imageName := parts[0]
258+
259+
// If the --image arg was provided, and if the image name doesn't match any of the filters
260+
// then skip this image
261+
if len(c.imageFilters) > 0 && !matchesImageFilter(c.imageFilters, imageName) {
262+
continue
263+
}
264+
265+
// If the --exclude-image arg was provided, and if the image name does match any of the filters
266+
// then skip this image
267+
if len(c.excludeImageFilters) > 0 && matchesImageFilter(c.excludeImageFilters, imageName) {
268+
continue
269+
}
270+
271+
images = append(images, imageName)
255272
}
256273
}
257274

@@ -270,3 +287,24 @@ func (c *cosignConfig) getImageReleaseListURL() string {
270287
return fmt.Sprintf("%s/release/%s/%s", imageListBaseURL, c.version, imageListFilename)
271288
}
272289
}
290+
291+
// matchesImageFilter checks if the image name from the list of published images
292+
// matches any user-provided --image or --exclude-image glob patterns
293+
// It matches against both the full image name, and the image name without the "sourcegraph/" prefix.
294+
func matchesImageFilter(patterns []string, imageName string) bool {
295+
for _, pattern := range patterns {
296+
// Try matching against the full image name
297+
if matched, _ := filepath.Match(pattern, imageName); matched {
298+
return true
299+
}
300+
301+
// Try matching against the image name without "sourcegraph/" prefix
302+
if strings.HasPrefix(imageName, "sourcegraph/") {
303+
shortName := strings.TrimPrefix(imageName, "sourcegraph/")
304+
if matched, _ := filepath.Match(pattern, shortName); matched {
305+
return true
306+
}
307+
}
308+
}
309+
return false
310+
}

0 commit comments

Comments
 (0)