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 }