github.com/CycloneDX/sbom-utility@v0.16.0/cmd/errors.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  /*
     3   * Licensed to the Apache Software Foundation (ASF) under one or more
     4   * contributor license agreements.  See the NOTICE file distributed with
     5   * this work for additional information regarding copyright ownership.
     6   * The ASF licenses this file to You under the Apache License, Version 2.0
     7   * (the "License"); you may not use this file except in compliance with
     8   * the License.  You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package cmd
    20  
    21  import (
    22  	"fmt"
    23  	"reflect"
    24  	"strings"
    25  
    26  	"github.com/CycloneDX/sbom-utility/schema"
    27  	"github.com/xeipuuv/gojsonschema"
    28  )
    29  
    30  const (
    31  	ERROR_APPLICATION = 1
    32  	ERROR_VALIDATION  = 2
    33  )
    34  
    35  // General error messages
    36  const (
    37  	ERR_TYPE_INVALID_JSON_MAP         = "invalid JSON map"
    38  	ERR_TYPE_INVALID_SBOM             = "invalid SBOM"
    39  	ERR_TYPE_SBOM_COMPONENT           = "component error"
    40  	ERR_TYPE_SBOM_LICENSE             = "license error"
    41  	ERR_TYPE_SBOM_COMPOSITION         = "composition error"
    42  	ERR_TYPE_SBOM_METADATA            = "metadata error"
    43  	ERR_TYPE_SBOM_METADATA_PROPERTY   = "metadata property error"
    44  	ERR_TYPE_UNEXPECTED_ERROR         = "unexpected error"
    45  	ERR_TYPE_UNSUPPORTED_OPERATION    = "unsupported operation"
    46  	ERR_TYPE_IETF_RFC6902_TEST_FAILED = "IETF RFC6902 test operation error"
    47  )
    48  
    49  // Custom Validation messages
    50  // TODO: Need to define a profile that supports these validation checks/messages
    51  const (
    52  	MSG_PROPERTY_NOT_UNIQUE                   = "check failed: property not unique"
    53  	MSG_INVALID_METADATA_PROPERTIES           = "field `metadata.properties` is missing or invalid"
    54  	MSG_INVALID_METADATA_COMPONENT_COMPONENTS = "field `metadata.component.components` array should be empty"
    55  	MSG_INVALID_METADATA_COMPONENT            = "field `metadata.component` is missing or invalid"
    56  )
    57  
    58  // Validation messages
    59  const (
    60  	MSG_FORMAT_TYPE                    = "format: `%s`"
    61  	MSG_SCHEMA_ERRORS                  = "schema errors found"
    62  	MSG_PROPERTY_NOT_FOUND             = "property not found"
    63  	MSG_PROPERTY_REGEX_FAILED          = "check failed: property regex mismatch"
    64  	MSG_IETF_RFC6902_OPERATION_SUCCESS = "IETF RFC6902 test operation success"
    65  )
    66  
    67  // License messages
    68  const (
    69  	MSG_LICENSE_INVALID_DATA   = "invalid license data"
    70  	MSG_LICENSE_INVALID_POLICY = "invalid license policy"
    71  	MSG_LICENSE_NOT_FOUND      = "license not found"
    72  	MSG_LICENSES_NOT_FOUND     = "licenses not found"
    73  	MSG_LICENSE_HASH_ERROR     = "hash of license failed"
    74  )
    75  
    76  // Query error details
    77  const (
    78  	MSG_QUERY_ERROR_FROM_KEY_NOT_FOUND         = "key not found in path"
    79  	MSG_QUERY_ERROR_FROM_KEY_SLICE_DEREFERENCE = "key attempts to dereference into an array"
    80  	MSG_QUERY_ERROR_SELECT_WILDCARD            = "wildcard cannot be used with other values"
    81  )
    82  
    83  // formatting Error() interface
    84  const (
    85  	ERR_FORMAT_DETAIL_SEP = ": "
    86  )
    87  
    88  // ------------------------------------------------
    89  // Application (sbom-utility) error types
    90  // ------------------------------------------------
    91  
    92  type BaseError struct {
    93  	Type       string
    94  	Message    string
    95  	InputFile  string
    96  	InnerError error
    97  	Command    string
    98  	Flags      string
    99  	Details    string
   100  }
   101  
   102  // Support the error interface
   103  func (err BaseError) Error() string {
   104  	formattedMessage := fmt.Sprintf("%s: %s (%s)", err.Type, err.Message, err.InputFile)
   105  	if err.Details != "" {
   106  		return fmt.Sprintf("%s: %s", formattedMessage, err.Details)
   107  	}
   108  	return formattedMessage
   109  }
   110  
   111  func (err *BaseError) AppendMessage(addendum string) {
   112  	if addendum != "" {
   113  		err.Message += addendum
   114  	}
   115  }
   116  
   117  // NOTE: use for unsupported features/subfunctions etc.
   118  // Used primarily for "patch" operation implementations currently
   119  type UnsupportedError struct {
   120  	BaseError
   121  	Operation string
   122  }
   123  
   124  func NewUnsupportedError(op string, m string) *UnsupportedError {
   125  	var err = new(UnsupportedError)
   126  	err.Type = ERR_TYPE_UNSUPPORTED_OPERATION
   127  	err.Operation = op
   128  	err.Message = m
   129  	return err
   130  }
   131  
   132  func (err UnsupportedError) Error() string {
   133  	formattedMessage := fmt.Sprintf("%s (%s). %s", err.Type, err.Operation, err.Message)
   134  	return formattedMessage
   135  }
   136  
   137  // IETF RFC6902 "Test" error
   138  type IETFRFC6902TestError struct {
   139  	BaseError
   140  	Operation string
   141  	Record    string
   142  	Value     interface{}
   143  }
   144  
   145  func NewIETFRFC6902TestError(record string, value interface{}) *IETFRFC6902TestError {
   146  	var err = new(IETFRFC6902TestError)
   147  	err.Type = ERR_TYPE_IETF_RFC6902_TEST_FAILED
   148  	err.Record = record
   149  	err.Value = value
   150  	err.Message = fmt.Sprintf("test record: %s, actual value: %v", err.Record, err.Value)
   151  	return err
   152  }
   153  
   154  func (err IETFRFC6902TestError) Error() string {
   155  	formattedMessage := fmt.Sprintf("%s. %s", err.Type, err.Message)
   156  	return formattedMessage
   157  }
   158  
   159  // ------------------------------------------------
   160  // SBOM error types
   161  // ------------------------------------------------
   162  
   163  // Extend the base error type
   164  type InvalidSBOMError struct {
   165  	BaseError
   166  	SBOM         *schema.BOM
   167  	FieldKeys    []string // Keys used to dereference into JSON map where error found
   168  	SchemaErrors []gojsonschema.ResultError
   169  }
   170  
   171  // Define more specific invalid SBOM errors
   172  type SBOMCompositionError struct {
   173  	InvalidSBOMError
   174  }
   175  
   176  // NOTE: Current sub-type is "no license found"; other, more specific subtypes may be created
   177  type SBOMLicenseError struct {
   178  	InvalidSBOMError
   179  }
   180  
   181  // Define more specific invalid SBOM errors
   182  type SBOMMetadataError struct {
   183  	InvalidSBOMError
   184  	Metadata schema.CDXMetadata
   185  }
   186  
   187  type SBOMMetadataPropertyError struct {
   188  	SBOMMetadataError
   189  	Expected *schema.CustomValidationProperty
   190  	Actual   []schema.CDXProperty
   191  }
   192  
   193  func NewInvalidSBOMError(sbom *schema.BOM, m string, errIn error, schemaErrors []gojsonschema.ResultError) *InvalidSBOMError {
   194  	var err = new(InvalidSBOMError)
   195  	err.Type = ERR_TYPE_INVALID_SBOM
   196  	err.Message = m
   197  	err.InnerError = errIn
   198  	err.SBOM = sbom
   199  	if sbom != nil {
   200  		err.InputFile = sbom.GetFilename()
   201  	}
   202  	err.SchemaErrors = schemaErrors
   203  	return err
   204  }
   205  
   206  func NewSbomLicenseNotFoundError(sbom *schema.BOM) *SBOMLicenseError {
   207  	var err = new(SBOMLicenseError)
   208  	err.Type = ERR_TYPE_SBOM_LICENSE
   209  	err.Message = MSG_LICENSES_NOT_FOUND
   210  	err.SBOM = sbom
   211  	if sbom != nil {
   212  		err.InputFile = sbom.GetFilename()
   213  	}
   214  	return err
   215  }
   216  
   217  func NewSbomLicenseDataError() *SBOMLicenseError {
   218  	var err = new(SBOMLicenseError)
   219  	err.Type = ERR_TYPE_SBOM_LICENSE
   220  	err.Message = MSG_LICENSE_INVALID_DATA
   221  	return err
   222  }
   223  
   224  func NewSBOMCompositionError(m string, sbom *schema.BOM, fields []string) *SBOMCompositionError {
   225  	var err = new(SBOMCompositionError)
   226  	err.Type = ERR_TYPE_SBOM_COMPOSITION
   227  	err.Message = m
   228  	err.FieldKeys = fields
   229  	err.SBOM = sbom
   230  	if sbom != nil {
   231  		err.InputFile = sbom.GetFilename()
   232  	}
   233  	return err
   234  }
   235  
   236  // TODO: create Error() (interface) method that displays CDXMetadata
   237  func NewSBOMMetadataError(sbom *schema.BOM, m string, metadata schema.CDXMetadata) *SBOMMetadataError {
   238  	var err = new(SBOMMetadataError)
   239  	err.Type = ERR_TYPE_SBOM_METADATA
   240  	err.Message = m
   241  	err.SBOM = sbom
   242  	err.Metadata = metadata
   243  	if sbom != nil {
   244  		err.InputFile = sbom.GetFilename()
   245  	}
   246  	return err
   247  }
   248  
   249  // TODO: create Error() (interface) method that displays CDXProperty
   250  func NewSbomMetadataPropertyError(sbom *schema.BOM, m string,
   251  	expected *schema.CustomValidationProperty,
   252  	values []schema.CDXProperty) *SBOMMetadataPropertyError {
   253  
   254  	var err = new(SBOMMetadataPropertyError)
   255  	err.Type = ERR_TYPE_SBOM_METADATA_PROPERTY
   256  	err.Message = m
   257  	err.SBOM = sbom
   258  	if sbom != nil {
   259  		err.InputFile = sbom.GetFilename()
   260  	}
   261  	err.Expected = expected
   262  	err.Actual = values
   263  	return err
   264  }
   265  
   266  // Support the error interface
   267  func (err SBOMCompositionError) Error() string {
   268  	text := err.BaseError.Error()
   269  	return fmt.Sprintf("%s: Field(s): %s", text, strings.Join(err.FieldKeys[:], "."))
   270  }
   271  
   272  // ------------------------------------------------
   273  // Error type checks (for convenience)
   274  // ------------------------------------------------
   275  
   276  // NOTE: err = nil will also fail if error was expected
   277  func ErrorTypesMatch(err error, expected error) bool {
   278  	return reflect.TypeOf(err) == reflect.TypeOf(expected)
   279  }
   280  
   281  func IsInvalidBOMError(err error) bool {
   282  	_, ok := err.(*InvalidSBOMError)
   283  	return ok
   284  }
   285  
   286  func IsBOMLicenseError(err error) (*SBOMLicenseError, bool) {
   287  	sbomErr, ok := err.(*SBOMLicenseError)
   288  	return sbomErr, ok
   289  }