pkg/bundle/ruleset/engine/cel/ext/package.go (380 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package ext
import (
"fmt"
"reflect"
"strings"
"github.com/gobwas/glob"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/interpreter/functions"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1"
csov1 "github.com/elastic/harp/pkg/cso/v1"
htypes "github.com/elastic/harp/pkg/sdk/types"
)
// Packages exported package operations.
func Packages() cel.EnvOption {
return cel.Lib(packageLib{})
}
type packageLib struct{}
func (packageLib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{
//nolint:staticcheck // TODO: deprecated usage. Requires an update.
cel.Declarations(
decls.NewVar("p", harpPackageObjectType),
decls.NewFunction("match_label",
decls.NewInstanceOverload("package_match_label_string",
[]*exprpb.Type{harpPackageObjectType, decls.String},
decls.Bool,
),
decls.NewInstanceOverload("package_match_label_string_string",
[]*exprpb.Type{harpPackageObjectType, decls.String, decls.String},
decls.Bool,
),
),
decls.NewFunction("match_annotation",
decls.NewInstanceOverload("package_match_annotation_string",
[]*exprpb.Type{harpPackageObjectType, decls.String},
decls.Bool,
),
decls.NewInstanceOverload("package_match_annotation_string_string",
[]*exprpb.Type{harpPackageObjectType, decls.String, decls.String},
decls.Bool,
),
),
decls.NewFunction("match_path",
decls.NewInstanceOverload("package_match_path_string",
[]*exprpb.Type{harpPackageObjectType, decls.String},
decls.Bool,
),
),
decls.NewFunction("match_secret",
decls.NewInstanceOverload("package_match_secret_string",
[]*exprpb.Type{harpPackageObjectType, decls.String},
decls.Bool,
),
),
decls.NewFunction("has_secret",
decls.NewInstanceOverload("package_has_secret_string",
[]*exprpb.Type{harpPackageObjectType, decls.String},
decls.Bool,
),
),
decls.NewFunction("has_all_secrets",
decls.NewInstanceOverload("package_has_all_secrets_list",
[]*exprpb.Type{harpPackageObjectType, decls.NewListType(decls.String)},
decls.Bool,
),
),
decls.NewFunction("is_cso_compliant",
decls.NewInstanceOverload("package_is_cso_compliant",
[]*exprpb.Type{harpPackageObjectType},
decls.Bool,
),
),
decls.NewFunction("secret",
decls.NewInstanceOverload("package_secret_string",
[]*exprpb.Type{harpPackageObjectType, decls.String},
harpKVObjectType,
),
),
),
}
}
func (packageLib) ProgramOptions() []cel.ProgramOption {
// Register types
reg, err := types.NewRegistry(
&bundlev1.KV{},
)
if err != nil {
panic(fmt.Errorf("unable to register types: %w", err))
}
return []cel.ProgramOption{
//nolint:staticcheck // TODO: refactor for deprecations
cel.Functions(
&functions.Overload{
Operator: "package_match_label_string",
Binary: celPackageMatchLabel,
},
&functions.Overload{
Operator: "package_match_label_string_string",
Function: celPackageMatchLabelValue,
},
&functions.Overload{
Operator: "package_match_annotation_string",
Binary: celPackageMatchAnnotation,
},
&functions.Overload{
Operator: "package_match_annotation_string_string",
Function: celPackageMatchAnnotationValue,
},
&functions.Overload{
Operator: "package_match_path_string",
Binary: celPackageMatchPath,
},
&functions.Overload{
Operator: "package_match_secret_string",
Binary: celPackageMatchSecret,
},
&functions.Overload{
Operator: "package_has_secret_string",
Binary: celPackageHasSecret,
},
&functions.Overload{
Operator: "package_has_all_secrets_list",
Binary: celPackageHasAllSecrets,
},
&functions.Overload{
Operator: "package_is_cso_compliant",
Unary: celPackageIsCSOCompliant,
},
&functions.Overload{
Operator: "package_secret_string",
Binary: celPackageGetSecret(reg),
},
),
}
}
// -----------------------------------------------------------------------------
func celPackageMatchLabel(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
patternTyped, ok := rhs.(types.String)
if !ok {
return types.Bool(false)
}
pattern, ok := patternTyped.Value().(string)
if !ok {
return types.Bool(false)
}
m := glob.MustCompile(pattern)
for k := range p.Labels {
if m.Match(k) {
return types.Bool(true)
}
}
return types.Bool(false)
}
func celPackageMatchLabelValue(values ...ref.Val) ref.Val {
if len(values) != 3 {
return types.Bool(false)
}
lhs := values[0]
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
keyPatternTyped, ok := values[1].(types.String)
if !ok {
return types.Bool(false)
}
keyPattern, ok := keyPatternTyped.Value().(string)
if !ok {
return types.Bool(false)
}
valuePatternTyped, ok := values[2].(types.String)
if !ok {
return types.Bool(false)
}
valuePattern, ok := valuePatternTyped.Value().(string)
if !ok {
return types.Bool(false)
}
km := glob.MustCompile(keyPattern)
vm := glob.MustCompile(valuePattern)
for k, v := range p.Labels {
if km.Match(k) && vm.Match(v) {
return types.Bool(true)
}
}
return types.Bool(false)
}
func celPackageMatchAnnotation(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
patternTyped, ok := rhs.(types.String)
if !ok {
return types.Bool(false)
}
pattern, ok := patternTyped.Value().(string)
if !ok {
return types.Bool(false)
}
m := glob.MustCompile(pattern)
for k := range p.Annotations {
if m.Match(k) {
return types.Bool(true)
}
}
return types.Bool(false)
}
func celPackageMatchAnnotationValue(values ...ref.Val) ref.Val {
if len(values) != 3 {
return types.Bool(false)
}
lhs := values[0]
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
keyPatternTyped, ok := values[1].(types.String)
if !ok {
return types.Bool(false)
}
keyPattern, ok := keyPatternTyped.Value().(string)
if !ok {
return types.Bool(false)
}
valuePatternTyped, ok := values[2].(types.String)
if !ok {
return types.Bool(false)
}
valuePattern, ok := valuePatternTyped.Value().(string)
if !ok {
return types.Bool(false)
}
km := glob.MustCompile(keyPattern)
vm := glob.MustCompile(valuePattern)
for k, v := range p.Annotations {
if km.Match(k) && vm.Match(v) {
return types.Bool(true)
}
}
return types.Bool(false)
}
func celPackageMatchPath(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
pathTyped, ok := rhs.(types.String)
if !ok {
return types.Bool(false)
}
path, ok := pathTyped.Value().(string)
if !ok {
return types.Bool(false)
}
return types.Bool(glob.MustCompile(path).Match(p.Name))
}
func celPackageMatchSecret(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
secretTyped, ok := rhs.(types.String)
if !ok {
return types.Bool(false)
}
secretName, ok := secretTyped.Value().(string)
if !ok {
return types.Bool(false)
}
// No secret data
if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
return types.Bool(false)
}
m := glob.MustCompile(secretName)
// Look for secret name
for _, s := range p.Secrets.Data {
if m.Match(s.Key) {
return types.Bool(true)
}
}
return types.Bool(false)
}
func celPackageHasSecret(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
secretTyped, ok := rhs.(types.String)
if !ok {
return types.Bool(false)
}
secretName, ok := secretTyped.Value().(string)
if !ok {
return types.Bool(false)
}
// No secret data
if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
return types.Bool(false)
}
// Look for secret name
for _, k := range p.Secrets.Data {
if strings.EqualFold(k.Key, secretName) {
return types.Bool(true)
}
}
return types.Bool(false)
}
func celPackageHasAllSecrets(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
secretsTyped, _ := rhs.ConvertToNative(reflect.TypeOf([]string{}))
secretNames, ok := secretsTyped.([]string)
if !ok {
return types.Bool(false)
}
// No secret data
if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
return types.Bool(false)
}
sa := htypes.StringArray(secretNames)
secretMap := map[string]*bundlev1.KV{}
for _, k := range p.Secrets.Data {
if !sa.Contains(k.Key) {
return types.Bool(false)
}
secretMap[k.Key] = k
}
// Look for secret name
for _, k := range secretNames {
if _, ok := secretMap[k]; !ok {
return types.Bool(false)
}
}
return types.Bool(true)
}
func celPackageIsCSOCompliant(lhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
if err := csov1.Validate(p.Name); err != nil {
return types.Bool(false)
}
return types.Bool(true)
}
func celPackageGetSecret(reg types.Adapter) func(lhs, rhs ref.Val) ref.Val {
return func(lhs, rhs ref.Val) ref.Val {
x, _ := lhs.ConvertToNative(reflect.TypeOf(&bundlev1.Package{}))
p, ok := x.(*bundlev1.Package)
if !ok {
return types.Bool(false)
}
secretTyped, ok := rhs.(types.String)
if !ok {
return types.Bool(false)
}
secretName, ok := secretTyped.Value().(string)
if !ok {
return types.Bool(false)
}
// No secret data
if p.Secrets == nil || p.Secrets.Data == nil || len(p.Secrets.Data) == 0 {
return types.Bool(false)
}
// Look for secret name
for _, k := range p.Secrets.Data {
if strings.EqualFold(k.Key, secretName) {
return reg.NativeToValue(k)
}
}
return nil
}
}