Skip to content

Commit 89c6747

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 89c6747

File tree

9 files changed

+219
-14
lines changed

9 files changed

+219
-14
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 a 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 a 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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
cranev1 "github.com/google/go-containerregistry/pkg/v1"
1313
)
1414

15-
var requiredLabels = []string{"name", "vendor", "version", "release", "summary", "description"}
15+
var requiredLabels = []string{"name", "vendor", "version", "release", "summary", "description", "maintainer"}
16+
17+
var trademarkLabels = []string{"name", "vendor", "maintainer"}
1618

1719
var _ check.Check = &HasRequiredLabelsCheck{}
1820

@@ -37,6 +39,13 @@ func (p *HasRequiredLabelsCheck) getDataForValidate(image cranev1.Image) (map[st
3739
func (p *HasRequiredLabelsCheck) validate(ctx context.Context, labels map[string]string) (bool, error) {
3840
logger := logr.FromContextOrDiscard(ctx)
3941

42+
trademarkViolationLabels := []string{}
43+
for _, label := range trademarkLabels {
44+
if violatesRedHatTrademark(labels[label]) {
45+
trademarkViolationLabels = append(trademarkViolationLabels, label)
46+
}
47+
}
48+
4049
missingLabels := []string{}
4150
for _, label := range requiredLabels {
4251
if labels[label] == "" {
@@ -49,7 +58,11 @@ func (p *HasRequiredLabelsCheck) validate(ctx context.Context, labels map[string
4958
logger.V(log.DBG).Info("expected labels are missing", "missingLabels", missingLabels)
5059
}
5160

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

5568
func (p *HasRequiredLabelsCheck) Name() string {
@@ -58,7 +71,8 @@ func (p *HasRequiredLabelsCheck) Name() string {
5871

5972
func (p *HasRequiredLabelsCheck) Metadata() check.Metadata {
6073
return check.Metadata{
61-
Description: "Checking if the required labels (name, vendor, version, release, summary, description) are present in the container metadata.",
74+
Description: "Checking if the required labels (name, vendor, version, release, summary, description, maintainer) are present in the container metadata." +
75+
"and that they do not violate Red Hat trademark.",
6276
Level: "good",
6377
KnowledgeBaseURL: certDocumentationURL,
6478
CheckURL: certDocumentationURL,
@@ -67,7 +81,8 @@ func (p *HasRequiredLabelsCheck) Metadata() check.Metadata {
6781

6882
func (p *HasRequiredLabelsCheck) Help() check.HelpText {
6983
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",
84+
Message: "Check HasRequiredLabel encountered an error. Please review the preflight.log file for more information.",
85+
Suggestion: "Add the following labels to your Dockerfile or Containerfile: name, vendor, version, release, summary, description" +
86+
"and validate that they do not violate Red Hat trademark.",
7287
}
7388
}

internal/policy/container/has_required_labels_test.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,22 @@ 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": "Something for Red Hat OpenShift",
17+
"maintainer": "maintainer",
1718
"vendor": "vendor",
1819
"version": "version",
1920
"release": "release",
2021
"summary": "summary",
2122
"description": "description",
2223
}
2324

24-
if bad {
25+
switch override {
26+
case "remove-label":
2527
delete(labels, "description")
28+
case "violates-trademark":
29+
labels["name"] = "Red Hat"
2630
}
2731

2832
return labels
@@ -31,15 +35,23 @@ func getLabels(bad bool) map[string]string {
3135
func getConfigFile() (*cranev1.ConfigFile, error) {
3236
return &cranev1.ConfigFile{
3337
Config: cranev1.Config{
34-
Labels: getLabels(false),
38+
Labels: getLabels(""),
3539
},
3640
}, nil
3741
}
3842

39-
func getBadConfigFile() (*cranev1.ConfigFile, error) {
43+
func getRemoveLabelConfigFile() (*cranev1.ConfigFile, error) {
4044
return &cranev1.ConfigFile{
4145
Config: cranev1.Config{
42-
Labels: getLabels(true),
46+
Labels: getLabels("remove-label"),
47+
},
48+
}, nil
49+
}
50+
51+
func getViolatesTrademarkConfigFile() (*cranev1.ConfigFile, error) {
52+
return &cranev1.ConfigFile{
53+
Config: cranev1.Config{
54+
Labels: getLabels("violates-trademark"),
4355
},
4456
}, nil
4557
}
@@ -68,7 +80,7 @@ var _ = Describe("HasRequiredLabels", func() {
6880
Context("When it does not have required labels", func() {
6981
BeforeEach(func() {
7082
fakeImage := fakecranev1.FakeImage{
71-
ConfigFileStub: getBadConfigFile,
83+
ConfigFileStub: getRemoveLabelConfigFile,
7284
}
7385
imageRef.ImageInfo = &fakeImage
7486
})
@@ -78,6 +90,19 @@ var _ = Describe("HasRequiredLabels", func() {
7890
Expect(ok).To(BeFalse())
7991
})
8092
})
93+
Context("When label.name violates Red Hat Trademark", func() {
94+
BeforeEach(func() {
95+
fakeImage := fakecranev1.FakeImage{
96+
ConfigFileStub: getViolatesTrademarkConfigFile,
97+
}
98+
imageRef.ImageInfo = &fakeImage
99+
})
100+
It("should not succeed the check and throw an error", func() {
101+
ok, err := hasRequiredLabelsCheck.Validate(context.TODO(), imageRef)
102+
Expect(err).ToNot(HaveOccurred())
103+
Expect(ok).To(BeFalse())
104+
})
105+
})
81106
})
82107

83108
AssertMetaData(&hasRequiredLabelsCheck)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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(s string) bool {
10+
// string starts with Red Hat variant
11+
startingWithRedHat := regexp.MustCompile("^[^a-z0-9]*red[^a-z0-9]*hat").MatchString(strings.ToLower(s))
12+
13+
// string contain Red Hat variant (not starting with)
14+
containsRedHat := len(regexp.MustCompile("red[^a-z0-9]*hat").FindAllString(strings.ToLower(s), -1))
15+
16+
// string contains "for Red Hat" variant
17+
containsForRedHat := regexp.MustCompile("for[^a-z0-9]*red[^a-z0-9]*hat").MatchString(strings.ToLower(s))
18+
19+
// We explicitly fail for this, so we don't need to count it here.
20+
if startingWithRedHat {
21+
containsRedHat -= 1
22+
}
23+
24+
// This is acceptable, so we don't count it against the string.
25+
if containsForRedHat {
26+
containsRedHat -= 1
27+
}
28+
29+
containsInvalidReference := containsRedHat > 0
30+
31+
return startingWithRedHat || containsInvalidReference
32+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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("`Something for Red Hat OpenShift` should not violate trademark policy", "Something 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+
Entry("`redhat` should violate trademark policy", "redhat", true),
23+
Entry("`something by red hat for red hat` should violate trademark policy", "something by red hat for red hat", true),
24+
Entry("`red hat product for red hat` should violate trademark policy", "red hat product for red hat", true),
25+
)
26+
})

0 commit comments

Comments
 (0)