github.com/google/osv-scalibr@v0.4.1/veles/validate.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package veles 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 ) 22 23 // ValidationStatus represents the result status of validating a Secret using a 24 // corresponding Validator. 25 type ValidationStatus string 26 27 const ( 28 // ValidationUnspecified is the default value for ValidationStatus. It should 29 // not be returned by a concrete Validator. 30 // 31 // The value of ValidationUnspecified is the empty string instead of a 32 // meaningful value so it is the automatic default. 33 ValidationUnspecified ValidationStatus = "" 34 // ValidationUnsupported occurs only if a ValidationEngine has no Validator 35 // for a given Secret type. 36 ValidationUnsupported ValidationStatus = "VALIDATION_UNSUPPORTED" 37 // ValidationFailed occurs if a Validator was not able to make a validation 38 // decision because an error occurred. 39 // This will be returned alongside the error so calling code can decide 40 // whether it's worth retrying. 41 ValidationFailed ValidationStatus = "VALIDATION_FAILED" 42 // ValidationInvalid occurs if a validation was successful but the result is 43 // negative: the Secret is not valid. 44 ValidationInvalid ValidationStatus = "VALIDATION_INVALID" 45 // ValidationValid occurs if the validation was successful and the result is 46 // positive: the Secret is valid. 47 ValidationValid ValidationStatus = "VALIDATION_VALID" 48 ) 49 50 // Validator is a Validator for the concrete Secret type S. 51 // 52 // It is used to validate Secrets of type S and returns the corresponding 53 // ValidationStatus or an error (in which case the ValidationStatus is 54 // ValidationStatusFailed). 55 type Validator[S Secret] interface { 56 Validate(ctx context.Context, secret S) (ValidationStatus, error) 57 } 58 59 // ValidationEngine bundles a number of Validators together. 60 // 61 // There can only be one Validator[S] for each concrete S. 62 type ValidationEngine struct { 63 vs map[reflect.Type]GenericValidator 64 } 65 66 // ValidationEngineOption is an option that can be used to configure a 67 // ValidationEngine at creation via NewValidationEngine. 68 type ValidationEngineOption func(*ValidationEngine) 69 70 // WithValidator configures the ValidationEngine to use the provided Validator. 71 // 72 // This will fail if a Validator for the given Secret Type S has already been 73 // registered with the ValidationEngine. 74 func WithValidator[S Secret](v Validator[S]) ValidationEngineOption { 75 return func(e *ValidationEngine) { 76 AddValidator(e, v) 77 } 78 } 79 80 // WithGenericValidator configures the ValidationEngine to use the provided 81 // type-erased GenericValidator with the secret type explicitly specified. 82 func WithGenericValidator(v GenericValidator, typ reflect.Type) ValidationEngineOption { 83 return func(e *ValidationEngine) { 84 AddGenericValidator(e, v, typ) 85 } 86 } 87 88 // NewValidationEngine creates a new ValidationEngine that bundles a number of 89 // Validators together. 90 // 91 // Validators are provided via the WithValidator ValidationEngineOption. 92 // 93 // Returns an error if no Validators are provided or if there are multiple 94 // Validators for the same Secret type. 95 func NewValidationEngine(opts ...ValidationEngineOption) *ValidationEngine { 96 e := &ValidationEngine{ 97 vs: make(map[reflect.Type]GenericValidator), 98 } 99 for _, opt := range opts { 100 opt(e) 101 } 102 return e 103 } 104 105 // AddValidator adds a new Validator for a concrete Secret type S to the engine. 106 // 107 // Returns whether there was already a Validator in place that now got replaced. 108 func AddValidator[S Secret](e *ValidationEngine, v Validator[S]) bool { 109 typ := reflect.TypeFor[S]() 110 return AddGenericValidator(e, &wrapped[S]{v: v}, typ) 111 } 112 113 // AddGenericValidator adds a new GenericValidator for a concrete Secret type typ to the engine. 114 // 115 // Returns whether there was already a GenericValidator in place that now got replaced. 116 func AddGenericValidator(e *ValidationEngine, v GenericValidator, typ reflect.Type) bool { 117 _, replaced := e.vs[typ] 118 e.vs[typ] = v 119 return replaced 120 } 121 122 // Validate validates a given Secret using one of the configured Validators. 123 // 124 // If no Validator for the Secret's type is configured, it will return a result 125 // with Status ValidationUnsupported. This is not an error because some Secrets 126 // might just not have corresponding Validators. 127 // 128 // An error is returned if something went wrong during validation, e.g. 129 // connection issues or timeouts. In that case ValidationStatus will be 130 // ValidationStatusFailed. 131 func (e *ValidationEngine) Validate(ctx context.Context, s Secret) (ValidationStatus, error) { 132 if err := ctx.Err(); err != nil { 133 return ValidationFailed, err 134 } 135 v, present := e.vs[reflect.TypeOf(s)] 136 if !present { 137 return ValidationUnsupported, nil 138 } 139 return v.Validate(ctx, s) 140 } 141 142 // GenericValidator is used to type erase type-erase Validator[S] using a shared interface. 143 type GenericValidator interface { 144 Validate(ctx context.Context, s Secret) (ValidationStatus, error) 145 } 146 147 // NewGenericValidator wraps a specific validator around a type-erased one. 148 func NewGenericValidator[S Secret](v Validator[S]) GenericValidator { 149 return &wrapped[S]{v: v} 150 } 151 152 type wrapped[S Secret] struct { 153 v Validator[S] 154 } 155 156 func (w wrapped[S]) Validate(ctx context.Context, s Secret) (ValidationStatus, error) { 157 t, ok := s.(S) 158 if !ok { 159 // The engine makes sure that this should never happen! 160 return ValidationFailed, fmt.Errorf("unexpected Secret of type %T, want %T", s, t) 161 } 162 return w.v.Validate(ctx, t) 163 }