2021-01-25 13:04:03 -05:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
2021-12-03 22:50:58 -05:00
|
|
|
"zotregistry.io/zot/pkg/storage"
|
2021-01-25 13:04:03 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-09-13 09:20:44 -05:00
|
|
|
// See https://github.com/opencontainers/image-spec/blob/main/annotations.md#back-compatibility-with-label-schema
|
|
|
|
AnnotationLabels = "org.label-schema.labels"
|
|
|
|
LabelAnnotationCreated = "org.label-schema.build-date"
|
|
|
|
LabelAnnotationVendor = "org.label-schema.vendor"
|
|
|
|
LabelAnnotationDescription = "org.label-schema.description"
|
|
|
|
LabelAnnotationTitle = "org.label-schema.name"
|
|
|
|
LabelAnnotationDocumentation = "org.label-schema.usage"
|
|
|
|
LabelAnnotationSource = "org.label-schema.vcs-url"
|
2021-01-25 13:04:03 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type TagInfo struct {
|
|
|
|
Name string
|
|
|
|
Digest string
|
|
|
|
Timestamp time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetRootDir(image string, storeController storage.StoreController) string {
|
|
|
|
var rootDir string
|
|
|
|
|
|
|
|
prefixName := GetRoutePrefix(image)
|
|
|
|
|
|
|
|
subStore := storeController.SubStore
|
|
|
|
|
|
|
|
if subStore != nil {
|
|
|
|
imgStore, ok := storeController.SubStore[prefixName]
|
|
|
|
if ok {
|
|
|
|
rootDir = imgStore.RootDir()
|
|
|
|
} else {
|
|
|
|
rootDir = storeController.DefaultStore.RootDir()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rootDir = storeController.DefaultStore.RootDir()
|
|
|
|
}
|
|
|
|
|
|
|
|
return rootDir
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetRepo(image string) string {
|
|
|
|
if strings.Contains(image, ":") {
|
2021-12-13 14:23:31 -05:00
|
|
|
splitString := strings.SplitN(image, ":", 2) //nolint:gomnd
|
|
|
|
if len(splitString) != 2 { //nolint:gomnd
|
2021-01-25 13:04:03 -05:00
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
|
|
|
return splitString[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
2022-09-30 12:32:32 -05:00
|
|
|
func GetImageDirAndTag(imageName string) (string, string) {
|
|
|
|
var imageDir string
|
|
|
|
|
|
|
|
var imageTag string
|
|
|
|
|
|
|
|
if strings.Contains(imageName, ":") {
|
|
|
|
imageDir, imageTag, _ = strings.Cut(imageName, ":")
|
|
|
|
} else {
|
|
|
|
imageDir = imageName
|
|
|
|
}
|
|
|
|
|
|
|
|
return imageDir, imageTag
|
|
|
|
}
|
|
|
|
|
2022-09-28 13:39:54 -05:00
|
|
|
func GetFixedTags(allTags, vulnerableTags []TagInfo) []TagInfo {
|
2021-01-25 13:04:03 -05:00
|
|
|
sort.Slice(allTags, func(i, j int) bool {
|
|
|
|
return allTags[i].Timestamp.Before(allTags[j].Timestamp)
|
|
|
|
})
|
|
|
|
|
2022-09-28 13:39:54 -05:00
|
|
|
earliestVulnerable := vulnerableTags[0]
|
|
|
|
vulnerableTagMap := make(map[string]TagInfo, len(vulnerableTags))
|
2021-01-25 13:04:03 -05:00
|
|
|
|
2022-09-28 13:39:54 -05:00
|
|
|
for _, tag := range vulnerableTags {
|
|
|
|
vulnerableTagMap[tag.Name] = tag
|
|
|
|
|
|
|
|
if tag.Timestamp.Before(earliestVulnerable.Timestamp) {
|
|
|
|
earliestVulnerable = tag
|
2021-01-25 13:04:03 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var fixedTags []TagInfo
|
|
|
|
|
2022-09-28 13:39:54 -05:00
|
|
|
// There are some downsides to this logic
|
|
|
|
// We assume there can't be multiple "branches" of the same
|
|
|
|
// image built at different times containing different fixes
|
|
|
|
// There may be older images which have a fix or
|
|
|
|
// newer images which don't
|
2021-01-25 13:04:03 -05:00
|
|
|
for _, tag := range allTags {
|
2022-09-28 13:39:54 -05:00
|
|
|
if tag.Timestamp.Before(earliestVulnerable.Timestamp) {
|
|
|
|
// The vulnerability did not exist at the time this
|
|
|
|
// image was built
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// If the image is old enough for the vulnerability to
|
|
|
|
// exist, but it was not detected, it means it contains
|
|
|
|
// the fix
|
|
|
|
if _, ok := vulnerableTagMap[tag.Name]; !ok {
|
2021-01-25 13:04:03 -05:00
|
|
|
fixedTags = append(fixedTags, tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fixedTags
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetLatestTag(allTags []TagInfo) TagInfo {
|
|
|
|
sort.Slice(allTags, func(i, j int) bool {
|
|
|
|
return allTags[i].Timestamp.Before(allTags[j].Timestamp)
|
|
|
|
})
|
|
|
|
|
|
|
|
return allTags[len(allTags)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetRoutePrefix(name string) string {
|
2021-12-13 14:23:31 -05:00
|
|
|
names := strings.SplitN(name, "/", 2) //nolint:gomnd
|
2021-01-25 13:04:03 -05:00
|
|
|
|
2022-10-05 05:21:14 -05:00
|
|
|
if len(names) != 2 { //nolint:gomnd
|
2021-01-25 13:04:03 -05:00
|
|
|
// it means route is of global storage e.g "centos:latest"
|
|
|
|
if len(names) == 1 {
|
|
|
|
return "/"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("/%s", names[0])
|
|
|
|
}
|
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
type ImageAnnotations struct {
|
|
|
|
Description string
|
|
|
|
Licenses string
|
|
|
|
Title string
|
|
|
|
Documentation string
|
|
|
|
Source string
|
|
|
|
Labels string
|
|
|
|
Vendor string
|
|
|
|
}
|
|
|
|
|
2022-10-05 05:21:14 -05:00
|
|
|
/*
|
|
|
|
OCI annotation/label with backwards compatibility
|
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
arg can be either lables or annotations
|
2022-10-05 05:21:14 -05:00
|
|
|
https://github.com/opencontainers/image-spec/blob/main/annotations.md.
|
|
|
|
*/
|
2022-09-13 09:20:44 -05:00
|
|
|
func GetAnnotationValue(annotations map[string]string, annotationKey, labelKey string) string {
|
|
|
|
value, ok := annotations[annotationKey]
|
|
|
|
if !ok || value == "" {
|
|
|
|
value, ok = annotations[labelKey]
|
2021-01-25 13:04:03 -05:00
|
|
|
if !ok {
|
2022-09-13 09:20:44 -05:00
|
|
|
value = ""
|
2021-01-25 13:04:03 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
return value
|
2021-01-25 13:04:03 -05:00
|
|
|
}
|
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
func GetDescription(annotations map[string]string) string {
|
|
|
|
return GetAnnotationValue(annotations, ispec.AnnotationDescription, LabelAnnotationDescription)
|
|
|
|
}
|
2021-01-25 13:04:03 -05:00
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
func GetVendor(annotations map[string]string) string {
|
|
|
|
return GetAnnotationValue(annotations, ispec.AnnotationVendor, LabelAnnotationVendor)
|
2021-01-25 13:04:03 -05:00
|
|
|
}
|
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
func GetTitle(annotations map[string]string) string {
|
|
|
|
return GetAnnotationValue(annotations, ispec.AnnotationTitle, LabelAnnotationTitle)
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetDocumentation(annotations map[string]string) string {
|
|
|
|
return GetAnnotationValue(annotations, ispec.AnnotationDocumentation, LabelAnnotationDocumentation)
|
|
|
|
}
|
2021-01-25 13:04:03 -05:00
|
|
|
|
2022-09-13 09:20:44 -05:00
|
|
|
func GetSource(annotations map[string]string) string {
|
|
|
|
return GetAnnotationValue(annotations, ispec.AnnotationSource, LabelAnnotationSource)
|
2021-01-25 13:04:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func GetCategories(labels map[string]string) string {
|
|
|
|
categories := labels[AnnotationLabels]
|
|
|
|
|
|
|
|
return categories
|
|
|
|
}
|
2022-09-13 09:20:44 -05:00
|
|
|
|
|
|
|
func GetLicenses(annotations map[string]string) string {
|
|
|
|
licenses := annotations[ispec.AnnotationLicenses]
|
|
|
|
|
|
|
|
return licenses
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetAnnotations(annotations, labels map[string]string) ImageAnnotations {
|
|
|
|
description := GetDescription(annotations)
|
|
|
|
if description == "" {
|
|
|
|
description = GetDescription(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
title := GetTitle(annotations)
|
|
|
|
if title == "" {
|
|
|
|
title = GetTitle(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
documentation := GetDocumentation(annotations)
|
|
|
|
if documentation == "" {
|
|
|
|
documentation = GetDocumentation(annotations)
|
|
|
|
}
|
|
|
|
|
|
|
|
source := GetSource(annotations)
|
|
|
|
if source == "" {
|
|
|
|
source = GetSource(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
licenses := GetLicenses(annotations)
|
|
|
|
if licenses == "" {
|
|
|
|
licenses = GetLicenses(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
categories := GetCategories(annotations)
|
|
|
|
if categories == "" {
|
|
|
|
categories = GetCategories(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
vendor := GetVendor(annotations)
|
|
|
|
if vendor == "" {
|
|
|
|
vendor = GetVendor(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ImageAnnotations{
|
|
|
|
Description: description,
|
|
|
|
Title: title,
|
|
|
|
Documentation: documentation,
|
|
|
|
Source: source,
|
|
|
|
Licenses: licenses,
|
|
|
|
Labels: categories,
|
|
|
|
Vendor: vendor,
|
|
|
|
}
|
|
|
|
}
|