Skip to content

Commit 0972357

Browse files
committed
adding method to see if a given string violates Red Hat Trademark(s) and incorporating into container checks
Signed-off-by: Adam D. Cornett <[email protected]>
1 parent 24e32ea commit 0972357

File tree

9 files changed

+203
-13
lines changed

9 files changed

+203
-13
lines changed

container/check_container_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ var _ = Describe("Container Check Execution", func() {
7070
Expect(err).ToNot(HaveOccurred())
7171
Expect(chk.policy).To(Equal("container"))
7272
Expect(chk.resolved).To(Equal(true))
73-
Expect(len(chk.checks)).To(Equal(8))
73+
Expect(len(chk.checks)).To(Equal(9))
7474
})
7575

7676
It("Should list checks without issue", func() {
7777
ctx := context.TODO()
7878
policy, checks, err := chk.List(ctx)
7979
Expect(err).ToNot(HaveOccurred())
8080
Expect(policy).To(Equal("container"))
81-
Expect(len(checks)).To(Equal(8))
81+
Expect(len(checks)).To(Equal(9))
8282
})
8383

8484
It("Should run without issue", func() {

internal/engine/engine.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,7 @@ func InitializeContainerChecks(ctx context.Context, p policy.Policy, cfg Contain
765765
cfg.PyxisAPIToken,
766766
cfg.CertificationProjectID,
767767
&http.Client{Timeout: 60 * time.Second})),
768+
&containerpol.HasProhibitedContainerName{},
768769
}, nil
769770
case policy.PolicyRoot:
770771
return []check.Check{
@@ -779,6 +780,7 @@ func InitializeContainerChecks(ctx context.Context, p policy.Policy, cfg Contain
779780
cfg.PyxisAPIToken,
780781
cfg.CertificationProjectID,
781782
&http.Client{Timeout: 60 * time.Second})),
783+
&containerpol.HasProhibitedContainerName{},
782784
}, nil
783785
case policy.PolicyScratchNonRoot:
784786
return []check.Check{
@@ -787,13 +789,15 @@ func InitializeContainerChecks(ctx context.Context, p policy.Policy, cfg Contain
787789
&containerpol.MaxLayersCheck{},
788790
&containerpol.HasRequiredLabelsCheck{},
789791
&containerpol.RunAsNonRootCheck{},
792+
&containerpol.HasProhibitedContainerName{},
790793
}, nil
791794
case policy.PolicyScratchRoot:
792795
return []check.Check{
793796
&containerpol.HasLicenseCheck{},
794797
containerpol.NewHasUniqueTagCheck(cfg.DockerConfig),
795798
&containerpol.MaxLayersCheck{},
796799
&containerpol.HasRequiredLabelsCheck{},
800+
&containerpol.HasProhibitedContainerName{},
797801
}, nil
798802
}
799803

internal/engine/engine_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ var _ = Describe("Check Name Queries", func() {
345345
"RunAsNonRoot",
346346
"HasModifiedFiles",
347347
"BasedOnUbi",
348+
"HasProhibitedContainerName",
348349
}),
349350
Entry("default operator policy", OperatorPolicy, []string{
350351
"ScorecardBasicSpecCheck",
@@ -363,12 +364,14 @@ var _ = Describe("Check Name Queries", func() {
363364
"LayerCountAcceptable",
364365
"HasRequiredLabel",
365366
"RunAsNonRoot",
367+
"HasProhibitedContainerName",
366368
}),
367369
Entry("scratch container policy", ScratchRootContainerPolicy, []string{
368370
"HasLicense",
369371
"HasUniqueTag",
370372
"LayerCountAcceptable",
371373
"HasRequiredLabel",
374+
"HasProhibitedContainerName",
372375
}),
373376
Entry("root container policy", RootExceptionContainerPolicy, []string{
374377
"HasLicense",
@@ -378,6 +381,7 @@ var _ = Describe("Check Name Queries", func() {
378381
"HasRequiredLabel",
379382
"HasModifiedFiles",
380383
"BasedOnUbi",
384+
"HasProhibitedContainerName",
381385
}),
382386
)
383387

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package container
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/go-logr/logr"
8+
9+
"github.com/redhat-openshift-ecosystem/openshift-preflight/internal/check"
10+
"github.com/redhat-openshift-ecosystem/openshift-preflight/internal/image"
11+
"github.com/redhat-openshift-ecosystem/openshift-preflight/internal/log"
12+
)
13+
14+
var _ check.Check = &HasProhibitedContainerName{}
15+
16+
type HasProhibitedContainerName struct{}
17+
18+
func (p HasProhibitedContainerName) Validate(ctx context.Context, imageReference image.ImageReference) (result bool, err error) {
19+
return p.validate(ctx, p.getDataForValidate(imageReference.ImageRepository))
20+
}
21+
22+
func (p HasProhibitedContainerName) getDataForValidate(imageRepository string) string {
23+
// splitting on '/' to get container name, at this point we know that
24+
// crane's ParseReference has set ImageReference.imageRepository in a valid format
25+
return strings.Split(imageRepository, "/")[1]
26+
}
27+
28+
func (p HasProhibitedContainerName) validate(ctx context.Context, containerName string) (bool, error) {
29+
logger := logr.FromContextOrDiscard(ctx)
30+
31+
if ViolatesRedHatTrademark(containerName) {
32+
logger.V(log.DBG).Info("container-name violate Red Hat trademark", "container-name", containerName)
33+
return false, nil
34+
}
35+
36+
return true, nil
37+
}
38+
39+
func (p HasProhibitedContainerName) Name() string {
40+
return "HasProhibitedContainerName"
41+
}
42+
43+
func (p HasProhibitedContainerName) Metadata() check.Metadata {
44+
return check.Metadata{
45+
Description: "Checking if the container-name violates Red Hat trademark.",
46+
Level: "good",
47+
KnowledgeBaseURL: certDocumentationURL,
48+
CheckURL: certDocumentationURL,
49+
}
50+
}
51+
52+
func (p HasProhibitedContainerName) Help() check.HelpText {
53+
return check.HelpText{
54+
Message: "Check HasProhibitedContainerName encountered an error. Please review the preflight.log file for more information.",
55+
Suggestion: "Update container-name ie (quay.io/repo-name/container-name) to not violate Red Hat trademark.",
56+
}
57+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package container
2+
3+
import (
4+
"context"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
9+
"github.com/redhat-openshift-ecosystem/openshift-preflight/internal/image"
10+
)
11+
12+
var _ = Describe("HasProhibitedContainerName", func() {
13+
var (
14+
hasProhibitedContainerName HasProhibitedContainerName
15+
imageRef image.ImageReference
16+
)
17+
18+
Describe("Checking for trademark violations", func() {
19+
Context("When container-name does not violate trademark", func() {
20+
BeforeEach(func() {
21+
imageRef.ImageRepository = "opdev/simple-demo-operator"
22+
})
23+
It("should pass Validate", func() {
24+
ok, err := hasProhibitedContainerName.Validate(context.TODO(), imageRef)
25+
Expect(err).ToNot(HaveOccurred())
26+
Expect(ok).To(BeTrue())
27+
})
28+
})
29+
Context("When container-name violates trademark", func() {
30+
BeforeEach(func() {
31+
imageRef.ImageRepository = "opdev/red-hat-container"
32+
})
33+
It("should not pass Validate", func() {
34+
ok, err := hasProhibitedContainerName.Validate(context.TODO(), imageRef)
35+
Expect(err).ToNot(HaveOccurred())
36+
Expect(ok).To(BeFalse())
37+
})
38+
})
39+
})
40+
41+
AssertMetaData(&hasProhibitedContainerName)
42+
})

internal/policy/container/has_required_labels.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import (
1414

1515
var requiredLabels = []string{"name", "vendor", "version", "release", "summary", "description"}
1616

17+
// todo-adam this may include maintainer, but we do not require that today, so need clarity if this is changing.
18+
var trademarkLabels = []string{"name", "vendor"}
19+
1720
var _ check.Check = &HasRequiredLabelsCheck{}
1821

1922
// HasRequiredLabelsCheck evaluates the image manifest to ensure that the appropriate metadata
@@ -37,6 +40,13 @@ func (p *HasRequiredLabelsCheck) getDataForValidate(image cranev1.Image) (map[st
3740
func (p *HasRequiredLabelsCheck) validate(ctx context.Context, labels map[string]string) (bool, error) {
3841
logger := logr.FromContextOrDiscard(ctx)
3942

43+
trademarkViolationLabels := []string{}
44+
for _, label := range trademarkLabels {
45+
if ViolatesRedHatTrademark(labels[label]) {
46+
trademarkViolationLabels = append(trademarkViolationLabels, label)
47+
}
48+
}
49+
4050
missingLabels := []string{}
4151
for _, label := range requiredLabels {
4252
if labels[label] == "" {
@@ -49,7 +59,11 @@ func (p *HasRequiredLabelsCheck) validate(ctx context.Context, labels map[string
4959
logger.V(log.DBG).Info("expected labels are missing", "missingLabels", missingLabels)
5060
}
5161

52-
return len(missingLabels) == 0, nil
62+
if len(trademarkViolationLabels) > 0 {
63+
logger.V(log.DBG).Info("labels violate Red Hat trademark", "trademarkViolationLabels", trademarkViolationLabels)
64+
}
65+
66+
return len(missingLabels) == 0 && len(trademarkViolationLabels) == 0, nil
5367
}
5468

5569
func (p *HasRequiredLabelsCheck) Name() string {
@@ -58,7 +72,8 @@ func (p *HasRequiredLabelsCheck) Name() string {
5872

5973
func (p *HasRequiredLabelsCheck) Metadata() check.Metadata {
6074
return check.Metadata{
61-
Description: "Checking if the required labels (name, vendor, version, release, summary, description) are present in the container metadata.",
75+
Description: "Checking if the required labels (name, vendor, version, release, summary, description) are present in the container metadata." +
76+
"and that they do not violate Red Hat trademark.",
6277
Level: "good",
6378
KnowledgeBaseURL: certDocumentationURL,
6479
CheckURL: certDocumentationURL,
@@ -67,7 +82,8 @@ func (p *HasRequiredLabelsCheck) Metadata() check.Metadata {
6782

6883
func (p *HasRequiredLabelsCheck) Help() check.HelpText {
6984
return check.HelpText{
70-
Message: "Check Check HasRequiredLabel encountered an error. Please review the preflight.log file for more information.",
71-
Suggestion: "Add the following labels to your Dockerfile or Containerfile: name, vendor, version, release, summary, description",
85+
Message: "Check HasRequiredLabel encountered an error. Please review the preflight.log file for more information.",
86+
Suggestion: "Add the following labels to your Dockerfile or Containerfile: name, vendor, version, release, summary, description" +
87+
"and validate that they do not violate Red Hat trademark.",
7288
}
7389
}

internal/policy/container/has_required_labels_test.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,21 @@ import (
1111
"github.com/redhat-openshift-ecosystem/openshift-preflight/internal/image"
1212
)
1313

14-
func getLabels(bad bool) map[string]string {
14+
func getLabels(override string) map[string]string {
1515
labels := map[string]string{
16-
"name": "name",
16+
"name": "Dynatrace for Red Hat OpenShift",
1717
"vendor": "vendor",
1818
"version": "version",
1919
"release": "release",
2020
"summary": "summary",
2121
"description": "description",
2222
}
2323

24-
if bad {
24+
switch override {
25+
case "remove-label":
2526
delete(labels, "description")
27+
case "violates-trademark":
28+
labels["name"] = "Red Hat"
2629
}
2730

2831
return labels
@@ -31,15 +34,23 @@ func getLabels(bad bool) map[string]string {
3134
func getConfigFile() (*cranev1.ConfigFile, error) {
3235
return &cranev1.ConfigFile{
3336
Config: cranev1.Config{
34-
Labels: getLabels(false),
37+
Labels: getLabels(""),
3538
},
3639
}, nil
3740
}
3841

39-
func getBadConfigFile() (*cranev1.ConfigFile, error) {
42+
func getRemoveLabelConfigFile() (*cranev1.ConfigFile, error) {
4043
return &cranev1.ConfigFile{
4144
Config: cranev1.Config{
42-
Labels: getLabels(true),
45+
Labels: getLabels("remove-label"),
46+
},
47+
}, nil
48+
}
49+
50+
func getViolatesTrademarkConfigFile() (*cranev1.ConfigFile, error) {
51+
return &cranev1.ConfigFile{
52+
Config: cranev1.Config{
53+
Labels: getLabels("violates-trademark"),
4354
},
4455
}, nil
4556
}
@@ -68,7 +79,7 @@ var _ = Describe("HasRequiredLabels", func() {
6879
Context("When it does not have required labels", func() {
6980
BeforeEach(func() {
7081
fakeImage := fakecranev1.FakeImage{
71-
ConfigFileStub: getBadConfigFile,
82+
ConfigFileStub: getRemoveLabelConfigFile,
7283
}
7384
imageRef.ImageInfo = &fakeImage
7485
})
@@ -78,6 +89,19 @@ var _ = Describe("HasRequiredLabels", func() {
7889
Expect(ok).To(BeFalse())
7990
})
8091
})
92+
Context("When label.name violates Red Hat Trademark", func() {
93+
BeforeEach(func() {
94+
fakeImage := fakecranev1.FakeImage{
95+
ConfigFileStub: getViolatesTrademarkConfigFile,
96+
}
97+
imageRef.ImageInfo = &fakeImage
98+
})
99+
It("should not succeed the check and throw an error", func() {
100+
ok, err := hasRequiredLabelsCheck.Validate(context.TODO(), imageRef)
101+
Expect(err).ToNot(HaveOccurred())
102+
Expect(ok).To(BeFalse())
103+
})
104+
})
81105
})
82106

83107
AssertMetaData(&hasRequiredLabelsCheck)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package container
2+
3+
import (
4+
"regexp"
5+
"strings"
6+
)
7+
8+
// ViolatesRedHatTrademark validates if a string meets specific "Red Hat" naming criteria
9+
func ViolatesRedHatTrademark(nameLabel string) bool {
10+
// string starts with Red Hat variant
11+
startingWithRedHat := regexp.MustCompile("^[^a-z0-9]*red[^a-z0-9]*hat").MatchString(strings.ToLower(nameLabel))
12+
13+
// string contain Red Hat variant (not starting with)
14+
containsRedHat := regexp.MustCompile("red[^a-z0-9]*hat").MatchString(strings.ToLower(nameLabel))
15+
16+
// string contains "for Red Hat" variant
17+
containsForRedHat := regexp.MustCompile("for[^a-z0-9]*red[^a-z0-9]*hat").MatchString(strings.ToLower(nameLabel))
18+
19+
return startingWithRedHat && containsRedHat && !containsForRedHat
20+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package container
2+
3+
import (
4+
. "github.com/onsi/ginkgo/v2"
5+
. "github.com/onsi/gomega"
6+
)
7+
8+
var _ = Describe("TrademarkValidator", func() {
9+
DescribeTable("Test all presentations of `Red Hat`",
10+
func(trademarkText string, expected bool) {
11+
result := ViolatesRedHatTrademark(trademarkText)
12+
Expect(result).To(Equal(expected))
13+
},
14+
15+
Entry("`Red Hat` should violate trademark policy", "Red Hat", true),
16+
Entry("`Dynatrace for Red Hat OpenShift` should not violate trademark policy", "Dynatrace for Red Hat OpenShift", false),
17+
Entry("`Red-Hat` should violate trademark policy", "Red-Hat", true),
18+
Entry("`Red_Hat` should violate trademark policy", "Red_Hat", true),
19+
Entry("`For-Red-Hat` should not violate trademark policy", "For-Red-Hat", false),
20+
Entry("`For_Red_Hat` should not violate trademark policy", "For_Red_Hat", false),
21+
Entry("`RED HAT ` should violate trademark policy", "RED HAT ", true),
22+
)
23+
})

0 commit comments

Comments
 (0)