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  }