github.com/zntrio/harp/v2@v2.0.9/pkg/bundle/ruleset/engine/cel/ext/package.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package ext
    19  
    20  import (
    21  	"reflect"
    22  	"strings"
    23  
    24  	"github.com/gobwas/glob"
    25  	"github.com/google/cel-go/cel"
    26  	"github.com/google/cel-go/common/types"
    27  	"github.com/google/cel-go/common/types/ref"
    28  
    29  	bundlev1 "github.com/zntrio/harp/v2/api/gen/go/harp/bundle/v1"
    30  	csov1 "github.com/zntrio/harp/v2/pkg/cso/v1"
    31  	htypes "github.com/zntrio/harp/v2/pkg/sdk/types"
    32  )
    33  
    34  // Packages exported package operations.
    35  func Packages() cel.EnvOption {
    36  	return cel.Lib(packageLib{})
    37  }
    38  
    39  type packageLib struct{}
    40  
    41  func (packageLib) CompileOptions() []cel.EnvOption {
    42  	reg, err := types.NewRegistry(
    43  		&bundlev1.KV{},
    44  	)
    45  	if err != nil {
    46  		panic(err)
    47  	}
    48  
    49  	return []cel.EnvOption{
    50  		cel.Variable("p", harpPackageObjectType),
    51  		cel.Function(
    52  			"match_label",
    53  			cel.MemberOverload("package_match_label_string", []*cel.Type{harpPackageObjectType, cel.StringType}, cel.BoolType,
    54  				cel.BinaryBinding(celPackageMatchLabel),
    55  			),
    56  			cel.MemberOverload("package_match_label_string_string", []*cel.Type{harpPackageObjectType, cel.StringType, cel.StringType}, cel.BoolType,
    57  				cel.FunctionBinding(celPackageMatchLabelValue),
    58  			),
    59  		),
    60  		cel.Function(
    61  			"match_annotation",
    62  			cel.MemberOverload("package_match_annotation_string", []*cel.Type{harpPackageObjectType, cel.StringType}, cel.BoolType,
    63  				cel.BinaryBinding(celPackageMatchAnnotation),
    64  			),
    65  			cel.MemberOverload("package_match_annotation_string_string", []*cel.Type{harpPackageObjectType, cel.StringType, cel.StringType}, cel.BoolType,
    66  				cel.FunctionBinding(celPackageMatchAnnotationValue),
    67  			),
    68  		),
    69  		cel.Function(
    70  			"match_path",
    71  			cel.MemberOverload("package_match_path_string", []*cel.Type{harpPackageObjectType, cel.StringType}, cel.BoolType,
    72  				cel.BinaryBinding(celPackageMatchPath),
    73  			),
    74  		),
    75  		cel.Function(
    76  			"match_secret",
    77  			cel.MemberOverload("package_match_secret_string", []*cel.Type{harpPackageObjectType, cel.StringType}, cel.BoolType,
    78  				cel.BinaryBinding(celPackageMatchSecret),
    79  			),
    80  		),
    81  		cel.Function(
    82  			"has_secret",
    83  			cel.MemberOverload("package_has_secret_string", []*cel.Type{harpPackageObjectType, cel.StringType}, cel.BoolType,
    84  				cel.BinaryBinding(celPackageHasSecret),
    85  			),
    86  		),
    87  		cel.Function(
    88  			"without_secret",
    89  			cel.MemberOverload("package_without_secret_string", []*cel.Type{harpPackageObjectType}, cel.BoolType,
    90  				cel.UnaryBinding(celPackageWithoutSecret),
    91  			),
    92  		),
    93  		cel.Function(
    94  			"has_all_secrets",
    95  			cel.MemberOverload("package_has_all_secrets_string", []*cel.Type{harpPackageObjectType, cel.ListType(cel.StringType)}, cel.BoolType,
    96  				cel.BinaryBinding(celPackageHasAllSecrets),
    97  			),
    98  		),
    99  		cel.Function(
   100  			"is_cso_compliant",
   101  			cel.MemberOverload("package_is_cso_compliant", []*cel.Type{harpPackageObjectType}, cel.BoolType,
   102  				cel.UnaryBinding(celPackageIsCSOCompliant),
   103  			),
   104  		),
   105  		cel.Function(
   106  			"secret",
   107  			cel.MemberOverload("package_secret_string", []*cel.Type{harpPackageObjectType, cel.StringType}, harpKVObjectType,
   108  				cel.BinaryBinding(celPackageGetSecret(reg)),
   109  			),
   110  		),
   111  	}
   112  }
   113  
   114  func (packageLib) ProgramOptions() []cel.ProgramOption {
   115  	return []cel.ProgramOption{}
   116  }
   117  
   118  // -----------------------------------------------------------------------------
   119  
   120  func celPackageMatchLabel(lhs, rhs ref.Val) ref.Val {
   121  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   122  	p, ok := x.(*bundlev1.Package)
   123  	if !ok {
   124  		return types.Bool(false)
   125  	}
   126  
   127  	patternTyped, ok := rhs.(types.String)
   128  	if !ok {
   129  		return types.Bool(false)
   130  	}
   131  
   132  	pattern, ok := patternTyped.Value().(string)
   133  	if !ok {
   134  		return types.Bool(false)
   135  	}
   136  
   137  	m := glob.MustCompile(pattern)
   138  	for k := range p.Labels {
   139  		if m.Match(k) {
   140  			return types.Bool(true)
   141  		}
   142  	}
   143  
   144  	return types.Bool(false)
   145  }
   146  
   147  func celPackageMatchLabelValue(values ...ref.Val) ref.Val {
   148  	if len(values) != 3 {
   149  		return types.Bool(false)
   150  	}
   151  
   152  	lhs := values[0]
   153  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   154  	p, ok := x.(*bundlev1.Package)
   155  	if !ok {
   156  		return types.Bool(false)
   157  	}
   158  
   159  	keyPatternTyped, ok := values[1].(types.String)
   160  	if !ok {
   161  		return types.Bool(false)
   162  	}
   163  
   164  	keyPattern, ok := keyPatternTyped.Value().(string)
   165  	if !ok {
   166  		return types.Bool(false)
   167  	}
   168  
   169  	valuePatternTyped, ok := values[2].(types.String)
   170  	if !ok {
   171  		return types.Bool(false)
   172  	}
   173  
   174  	valuePattern, ok := valuePatternTyped.Value().(string)
   175  	if !ok {
   176  		return types.Bool(false)
   177  	}
   178  
   179  	km := glob.MustCompile(keyPattern)
   180  	vm := glob.MustCompile(valuePattern)
   181  
   182  	for k, v := range p.Labels {
   183  		if km.Match(k) && vm.Match(v) {
   184  			return types.Bool(true)
   185  		}
   186  	}
   187  
   188  	return types.Bool(false)
   189  }
   190  
   191  func celPackageMatchAnnotation(lhs, rhs ref.Val) ref.Val {
   192  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   193  	p, ok := x.(*bundlev1.Package)
   194  	if !ok {
   195  		return types.Bool(false)
   196  	}
   197  
   198  	patternTyped, ok := rhs.(types.String)
   199  	if !ok {
   200  		return types.Bool(false)
   201  	}
   202  
   203  	pattern, ok := patternTyped.Value().(string)
   204  	if !ok {
   205  		return types.Bool(false)
   206  	}
   207  
   208  	m := glob.MustCompile(pattern)
   209  	for k := range p.Annotations {
   210  		if m.Match(k) {
   211  			return types.Bool(true)
   212  		}
   213  	}
   214  
   215  	return types.Bool(false)
   216  }
   217  
   218  func celPackageMatchAnnotationValue(values ...ref.Val) ref.Val {
   219  	if len(values) != 3 {
   220  		return types.Bool(false)
   221  	}
   222  
   223  	lhs := values[0]
   224  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   225  	p, ok := x.(*bundlev1.Package)
   226  	if !ok {
   227  		return types.Bool(false)
   228  	}
   229  
   230  	keyPatternTyped, ok := values[1].(types.String)
   231  	if !ok {
   232  		return types.Bool(false)
   233  	}
   234  
   235  	keyPattern, ok := keyPatternTyped.Value().(string)
   236  	if !ok {
   237  		return types.Bool(false)
   238  	}
   239  
   240  	valuePatternTyped, ok := values[2].(types.String)
   241  	if !ok {
   242  		return types.Bool(false)
   243  	}
   244  
   245  	valuePattern, ok := valuePatternTyped.Value().(string)
   246  	if !ok {
   247  		return types.Bool(false)
   248  	}
   249  
   250  	km := glob.MustCompile(keyPattern)
   251  	vm := glob.MustCompile(valuePattern)
   252  
   253  	for k, v := range p.Annotations {
   254  		if km.Match(k) && vm.Match(v) {
   255  			return types.Bool(true)
   256  		}
   257  	}
   258  
   259  	return types.Bool(false)
   260  }
   261  
   262  func celPackageMatchPath(lhs, rhs ref.Val) ref.Val {
   263  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   264  	p, ok := x.(*bundlev1.Package)
   265  	if !ok {
   266  		return types.Bool(false)
   267  	}
   268  
   269  	pathTyped, ok := rhs.(types.String)
   270  	if !ok {
   271  		return types.Bool(false)
   272  	}
   273  
   274  	path, ok := pathTyped.Value().(string)
   275  	if !ok {
   276  		return types.Bool(false)
   277  	}
   278  
   279  	return types.Bool(glob.MustCompile(path).Match(p.Name))
   280  }
   281  
   282  func celPackageMatchSecret(lhs, rhs ref.Val) ref.Val {
   283  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   284  	p, ok := x.(*bundlev1.Package)
   285  	if !ok {
   286  		return types.Bool(false)
   287  	}
   288  
   289  	secretTyped, ok := rhs.(types.String)
   290  	if !ok {
   291  		return types.Bool(false)
   292  	}
   293  
   294  	secretName, ok := secretTyped.Value().(string)
   295  	if !ok {
   296  		return types.Bool(false)
   297  	}
   298  
   299  	// No secret data
   300  	if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
   301  		return types.Bool(false)
   302  	}
   303  
   304  	m := glob.MustCompile(secretName)
   305  
   306  	// Look for secret name
   307  	for _, s := range p.Secrets.Data {
   308  		if m.Match(s.Key) {
   309  			return types.Bool(true)
   310  		}
   311  	}
   312  
   313  	return types.Bool(false)
   314  }
   315  
   316  func celPackageHasSecret(lhs, rhs ref.Val) ref.Val {
   317  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   318  	p, ok := x.(*bundlev1.Package)
   319  	if !ok {
   320  		return types.Bool(false)
   321  	}
   322  
   323  	secretTyped, ok := rhs.(types.String)
   324  	if !ok {
   325  		return types.Bool(false)
   326  	}
   327  
   328  	secretName, ok := secretTyped.Value().(string)
   329  	if !ok {
   330  		return types.Bool(false)
   331  	}
   332  
   333  	// No secret data
   334  	if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
   335  		return types.Bool(false)
   336  	}
   337  
   338  	// Look for secret name
   339  	for _, k := range p.Secrets.Data {
   340  		if strings.EqualFold(k.Key, secretName) {
   341  			return types.Bool(true)
   342  		}
   343  	}
   344  
   345  	return types.Bool(false)
   346  }
   347  
   348  func celPackageWithoutSecret(value ref.Val) ref.Val {
   349  	x, _ := value.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   350  	p, ok := x.(*bundlev1.Package)
   351  	if !ok {
   352  		return types.Bool(false)
   353  	}
   354  
   355  	// Look for secret name
   356  	if p.Secrets == nil || len(p.Secrets.Data) == 0 {
   357  		return types.Bool(true)
   358  	}
   359  
   360  	return types.Bool(false)
   361  }
   362  
   363  func celPackageHasAllSecrets(lhs, rhs ref.Val) ref.Val {
   364  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   365  	p, ok := x.(*bundlev1.Package)
   366  	if !ok {
   367  		return types.Bool(false)
   368  	}
   369  
   370  	secretsTyped, _ := rhs.ConvertToNative(reflect.TypeOf([]string{}))
   371  	secretNames, ok := secretsTyped.([]string)
   372  	if !ok {
   373  		return types.Bool(false)
   374  	}
   375  
   376  	// No secret data
   377  	if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
   378  		return types.Bool(false)
   379  	}
   380  
   381  	sa := htypes.StringArray(secretNames)
   382  
   383  	secretMap := map[string]*bundlev1.KV{}
   384  	for _, k := range p.Secrets.Data {
   385  		if !sa.Contains(k.Key) {
   386  			return types.Bool(false)
   387  		}
   388  		secretMap[k.Key] = k
   389  	}
   390  
   391  	// Look for secret name
   392  	for _, k := range secretNames {
   393  		if _, ok := secretMap[k]; !ok {
   394  			return types.Bool(false)
   395  		}
   396  	}
   397  
   398  	return types.Bool(true)
   399  }
   400  
   401  func celPackageIsCSOCompliant(lhs ref.Val) ref.Val {
   402  	x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   403  	p, ok := x.(*bundlev1.Package)
   404  	if !ok {
   405  		return types.Bool(false)
   406  	}
   407  
   408  	if err := csov1.Validate(p.Name); err != nil {
   409  		return types.Bool(false)
   410  	}
   411  
   412  	return types.Bool(true)
   413  }
   414  
   415  func celPackageGetSecret(reg ref.TypeAdapter) func(lhs, rhs ref.Val) ref.Val {
   416  	return func(lhs, rhs ref.Val) ref.Val {
   417  		x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
   418  		p, ok := x.(*bundlev1.Package)
   419  		if !ok {
   420  			return types.Bool(false)
   421  		}
   422  
   423  		secretTyped, ok := rhs.(types.String)
   424  		if !ok {
   425  			return types.Bool(false)
   426  		}
   427  
   428  		secretName, ok := secretTyped.Value().(string)
   429  		if !ok {
   430  			return types.Bool(false)
   431  		}
   432  
   433  		// No secret data
   434  		if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
   435  			return types.Bool(false)
   436  		}
   437  
   438  		// Look for secret name
   439  		for _, k := range p.Secrets.Data {
   440  			if strings.EqualFold(k.Key, secretName) {
   441  				return reg.NativeToValue(k)
   442  			}
   443  		}
   444  
   445  		return nil
   446  	}
   447  }