From e60932bd3c8d63a5089bc1e6210dc11d49f1fed3 Mon Sep 17 00:00:00 2001 From: Vincent Shen Date: Mon, 15 Apr 2024 00:09:42 -0700 Subject: [PATCH] Add unique profile ID This commit implements unique profile ID feature, we are adding a unique profile ID to Profile, ComplianceScan, and ComplianceCheckResult CRD. The profile UUID is generated from sha1 of - --- cmd/manager/aggregator.go | 1 + pkg/apis/compliance/v1alpha1/profile_types.go | 6 ++++ .../compliancesuite_controller.go | 5 ++- pkg/profileparser/profileparser.go | 5 +-- pkg/xccdf/tailoring.go | 32 ++++++++++++++++--- 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/cmd/manager/aggregator.go b/cmd/manager/aggregator.go index d1eaf9e40..a0d3e2f84 100644 --- a/cmd/manager/aggregator.go +++ b/cmd/manager/aggregator.go @@ -509,6 +509,7 @@ func getRemediationLabels(scan *compv1alpha1.ComplianceScan, obj runtime.Object) func getCheckResultLabels(pr *utils.ParseResult, resultLabels map[string]string, scan *compv1alpha1.ComplianceScan) map[string]string { labels := make(map[string]string) labels[compv1alpha1.ComplianceScanLabel] = scan.Name + labels[compv1alpha1.ProfileUniqueIDLable] = scan.Labels[compv1alpha1.ProfileUniqueIDLable] labels[compv1alpha1.SuiteLabel] = scan.Labels[compv1alpha1.SuiteLabel] labels[compv1alpha1.ComplianceCheckResultStatusLabel] = string(pr.CheckResult.Status) labels[compv1alpha1.ComplianceCheckResultSeverityLabel] = string(pr.CheckResult.Severity) diff --git a/pkg/apis/compliance/v1alpha1/profile_types.go b/pkg/apis/compliance/v1alpha1/profile_types.go index 0052c95eb..4e6728115 100644 --- a/pkg/apis/compliance/v1alpha1/profile_types.go +++ b/pkg/apis/compliance/v1alpha1/profile_types.go @@ -12,6 +12,12 @@ const ProductTypeAnnotation = "compliance.openshift.io/product-type" // or TailoredProfile is targetting. Example: ocp4, rhcos4, ... const ProductAnnotation = "compliance.openshift.io/product" +// ProfileUniqueIDAnnotation specifies the unique identifier of the Profile +const ProfileUniqueIDAnnotation = "compliance.openshift.io/unique-id" + +// ProfileUniqueIDLable specifies the unique identifier of the Profile +const ProfileUniqueIDLable = "compliance.openshift.io/unique-id" + // ProfileRule defines the name of a specific rule in the profile type ProfileRule string diff --git a/pkg/controller/compliancesuite/compliancesuite_controller.go b/pkg/controller/compliancesuite/compliancesuite_controller.go index 609568997..440111ae2 100644 --- a/pkg/controller/compliancesuite/compliancesuite_controller.go +++ b/pkg/controller/compliancesuite/compliancesuite_controller.go @@ -9,6 +9,7 @@ import ( "github.com/ComplianceAsCode/compliance-operator/pkg/controller/common" "github.com/ComplianceAsCode/compliance-operator/pkg/controller/metrics" "github.com/ComplianceAsCode/compliance-operator/pkg/utils" + "github.com/ComplianceAsCode/compliance-operator/pkg/xccdf" ctrl "sigs.k8s.io/controller-runtime" "github.com/go-logr/logr" @@ -423,8 +424,10 @@ func launchScanForSuite(r *ReconcileComplianceSuite, suite *compv1alpha1.Complia func newScanForSuite(suite *compv1alpha1.ComplianceSuite, scanWrap *compv1alpha1.ComplianceScanSpecWrapper) *compv1alpha1.ComplianceScan { scan := compv1alpha1.ComplianceScanFromWrapper(scanWrap) scan.SetLabels(map[string]string{ - compv1alpha1.SuiteLabel: suite.Name, + compv1alpha1.SuiteLabel: suite.Name, + compv1alpha1.ProfileUniqueIDLable: xccdf.GetProfileUniqueID(scanWrap.Content, scanWrap.Profile), }) + scan.SetNamespace(suite.Namespace) return scan } diff --git a/pkg/profileparser/profileparser.go b/pkg/profileparser/profileparser.go index dd739abc8..3c6bcb1b3 100644 --- a/pkg/profileparser/profileparser.go +++ b/pkg/profileparser/profileparser.go @@ -364,8 +364,9 @@ func parseProfileFromNode(profileRoot *xmlquery.Node, pb *cmpv1alpha1.ProfileBun Name: xccdf.GetProfileNameFromID(id), Namespace: pb.Namespace, Annotations: map[string]string{ - cmpv1alpha1.ProductAnnotation: productName, - cmpv1alpha1.ProductTypeAnnotation: string(productType), + cmpv1alpha1.ProductAnnotation: productName, + cmpv1alpha1.ProductTypeAnnotation: string(productType), + cmpv1alpha1.ProfileUniqueIDAnnotation: xccdf.GetProfileUniqueID(pb.Spec.ContentFile, id), }, }, ProfilePayload: cmpv1alpha1.ProfilePayload{ diff --git a/pkg/xccdf/tailoring.go b/pkg/xccdf/tailoring.go index 82e3da6f5..97db82d1e 100644 --- a/pkg/xccdf/tailoring.go +++ b/pkg/xccdf/tailoring.go @@ -7,15 +7,19 @@ import ( "strings" "time" + "github.com/google/uuid" + cmpv1alpha1 "github.com/ComplianceAsCode/compliance-operator/pkg/apis/compliance/v1alpha1" ) const ( // XMLHeader is the header for the XML doc - XMLHeader string = `` - profileIDPrefix string = "xccdf_org.ssgproject.content_profile_" - ruleIDPrefix string = "xccdf_org.ssgproject.content_rule_" - varIDPrefix string = "xccdf_org.ssgproject.content_value_" + XMLHeader string = `` + profileIDPrefix string = "xccdf_org.ssgproject.content_profile_" + contentFileSuffix string = "-ds.xml" + contentFilePrefix string = "ssg-" + ruleIDPrefix string = "xccdf_org.ssgproject.content_rule_" + varIDPrefix string = "xccdf_org.ssgproject.content_value_" // XCCDFNamespace is the XCCDF namespace of this project. Per the XCCDF // specification, this assiciates the content with the author XCCDFNamespace string = "compliance.openshift.io" @@ -85,6 +89,26 @@ func GetProfileNameFromID(id string) string { return strings.ToLower(strings.ReplaceAll(trimedName, "_", "-")) } +// GetProfileUniqueIDFromBundleName returns the unique identifier of the Profile +func GetProfileUniqueIDFromBundleName(pbName, profileID string) string { + // Use a DNS namespace UUID + namespace := uuid.Must(uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + name := fmt.Sprintf("%s-%s", pbName, profileID) + uuid := uuid.NewSHA1(namespace, []byte(name)) + return uuid.String() +} + +// GetUniqueBundleNameFromContentFileName gets the bundle name from the content file name +func GetUniqueBundleNameFromContentFileName(contentFileName string) string { + trimmedName := strings.TrimPrefix(contentFileName, contentFilePrefix) + return strings.TrimSuffix(trimmedName, contentFileSuffix) +} + +// GetProfileUniqueID gets the unique identifier of the Profile from the content file name and the profile ID +func GetProfileUniqueID(contentFileName string, profileID string) string { + return GetProfileUniqueIDFromBundleName(GetUniqueBundleNameFromContentFileName(contentFileName), profileID) +} + // GetRuleNameFromID gets a rule name from the xccdf ID func GetRuleNameFromID(id string) string { trimedName := strings.TrimPrefix(id, ruleIDPrefix)