github.com/CycloneDX/sbom-utility@v0.16.0/cmd/vulnerability_test.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 "bufio" 23 "bytes" 24 "io/fs" 25 "testing" 26 27 "github.com/CycloneDX/sbom-utility/common" 28 "github.com/CycloneDX/sbom-utility/schema" 29 "github.com/CycloneDX/sbom-utility/utils" 30 ) 31 32 const ( 33 // Test "vulnerability list" command 34 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM = "test/vex/cdx-1-3-example1-bom.json" 35 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX = "test/vex/cdx-1-3-example1-bom-vex.json" 36 TEST_VULN_CDX_1_4_EXAMPLE_1_VEX = "test/vex/cdx-1-4-example1-vex.json" 37 TEST_VULN_CDX_1_3_EXAMPLE_2_BOM_VEX = "test/vex/cdx-1-3-example2-bom-vex.json" 38 ) 39 40 type VulnTestInfo struct { 41 CommonTestInfo 42 } 43 44 var VULN_TEST_DEFAULT_FLAGS utils.VulnerabilityCommandFlags 45 46 // Stringer interface for ResourceTestInfo (just display subset of key values) 47 func (ti *VulnTestInfo) String() string { 48 buffer, _ := utils.EncodeAnyToDefaultIndentedJSONStr(ti) 49 return buffer.String() 50 } 51 52 func NewVulnTestInfo(inputFile string, listFormat string, listSummary bool, whereClause string, 53 resultExpectedLineCount int) *VulnTestInfo { 54 var ti = new(VulnTestInfo) 55 var pCommon = &ti.CommonTestInfo 56 pCommon.Init( 57 inputFile, 58 listFormat, 59 listSummary, 60 whereClause, 61 nil, 62 resultExpectedLineCount, 63 nil) 64 return ti 65 } 66 67 func NewVulnTestInfoBasic(inputFile string, listFormat string, resultExpectedError error) *VulnTestInfo { 68 var ti = new(VulnTestInfo) 69 var pCommon = &ti.CommonTestInfo 70 pCommon.InitBasic(inputFile, listFormat, resultExpectedError) 71 return ti 72 } 73 74 // ------------------------------------------- 75 // Vuln. list test helper functions 76 // ------------------------------------------- 77 func innerBufferedTestVulnList(testInfo *VulnTestInfo, whereFilters []common.WhereFilter, flags utils.VulnerabilityCommandFlags) (outputBuffer bytes.Buffer, err error) { 78 // Declare an output outputBuffer/outputWriter to use used during tests 79 var outputWriter = bufio.NewWriter(&outputBuffer) 80 // ensure all data is written to buffer before further validation 81 defer outputWriter.Flush() 82 83 // TODO: allow tests to set the flag values 84 // The command looks for the input filename in global flags struct 85 utils.GlobalFlags.PersistentFlags.InputFile = testInfo.InputFile 86 utils.GlobalFlags.PersistentFlags.OutputFormat = testInfo.OutputFormat 87 utils.GlobalFlags.PersistentFlags.OutputFile = testInfo.OutputFile 88 utils.GlobalFlags.PersistentFlags.OutputIndent = testInfo.OutputIndent 89 err = ListVulnerabilities(outputWriter, utils.GlobalFlags.PersistentFlags, flags, whereFilters) 90 return 91 } 92 93 func innerTestVulnList(t *testing.T, testInfo *VulnTestInfo, flags utils.VulnerabilityCommandFlags) (outputBuffer bytes.Buffer, basicTestInfo string, err error) { 94 getLogger().Tracef("TestInfo: %s", testInfo) 95 96 // Parse out --where filters and exit out if error detected 97 whereFilters, err := prepareWhereFilters(t, &testInfo.CommonTestInfo) 98 if err != nil { 99 return 100 } 101 102 // invoke list command with a byte buffer 103 outputBuffer, err = innerBufferedTestVulnList(testInfo, whereFilters, flags) 104 105 // Run all common tests against "result" values in the CommonTestInfo struct 106 err = innerRunReportResultTests(t, &testInfo.CommonTestInfo, outputBuffer, err) 107 108 return 109 } 110 111 // ---------------------------------------- 112 // Command flag tests 113 // ---------------------------------------- 114 115 func TestVulnListInvalidInputFileLoad(t *testing.T) { 116 testInfo := NewVulnTestInfoBasic( 117 TEST_INPUT_FILE_NON_EXISTENT, 118 FORMAT_DEFAULT, 119 &fs.PathError{}) 120 121 // verify correct error is returned 122 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 123 } 124 125 // ------------------------------------------- 126 // Test format unsupported (SPDX) 127 // ------------------------------------------- 128 func TestVulnListFormatUnsupportedSPDXMinReq(t *testing.T) { 129 testInfo := NewVulnTestInfoBasic( 130 TEST_SPDX_2_2_MIN_REQUIRED, 131 FORMAT_DEFAULT, 132 &schema.UnsupportedFormatError{}) 133 134 // verify correct error is returned 135 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 136 } 137 138 func TestVulnListFormatUnsupportedSPDX22(t *testing.T) { 139 testInfo := NewVulnTestInfoBasic( 140 TEST_SPDX_2_2_EXAMPLE_1, 141 FORMAT_DEFAULT, 142 &schema.UnsupportedFormatError{}) 143 144 // verify correct error is returned 145 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 146 } 147 148 // ------------------------------------------- 149 // CDX variants - Test for list (data) errors 150 // ------------------------------------------- 151 152 func TestVulnListTextCdx13BOMNoVulnFound(t *testing.T) { 153 testInfo := NewVulnTestInfoBasic( 154 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM, 155 FORMAT_TEXT, 156 nil) 157 158 // verify there is a (warning) message present when no resources are found 159 testInfo.ResultLineContainsValues = []string{MSG_OUTPUT_NO_VULNERABILITIES_FOUND} 160 testInfo.ResultLineContainsValuesAtLineNum = 2 161 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 162 } 163 164 // ------------------------------------------- 165 // CDX variants - List only 166 // ------------------------------------------- 167 168 func TestVulnListTextCdx13Example1VEXOnly(t *testing.T) { 169 testInfo := NewVulnTestInfoBasic( 170 TEST_VULN_CDX_1_4_EXAMPLE_1_VEX, 171 FORMAT_DEFAULT, 172 nil) 173 174 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 175 } 176 177 // Assure default format listing (report) works 178 func TestVulnListTextCdx13Example1BOMWithVEX(t *testing.T) { 179 testInfo := NewVulnTestInfoBasic( 180 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 181 FORMAT_DEFAULT, 182 nil) 183 184 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 185 } 186 187 // FORMAT_TEXT 188 func TestVulnListCdx13TextFull(t *testing.T) { 189 testInfo := NewVulnTestInfoBasic( 190 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 191 FORMAT_TEXT, 192 nil) 193 194 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 195 } 196 197 // FORMAT_TEXT, --summary 198 func TestVulnListCdx13TextSummary(t *testing.T) { 199 testInfo := NewVulnTestInfoBasic( 200 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 201 FORMAT_TEXT, 202 nil) 203 testInfo.ListSummary = true 204 205 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 206 207 // TODO add extra tests to verify specific rows have correct data for the input test file 208 // e.g., CVE-2020-25649 CVSSv31: 7.5 (high) NVD 2020-12-03 The highest threat from this vulnerability is data integrity. 209 } 210 211 // Assure CSV format listing (report) works 212 func TestVulnListCdx13CSV(t *testing.T) { 213 testInfo := NewVulnTestInfoBasic( 214 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 215 FORMAT_CSV, 216 nil) 217 218 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 219 } 220 221 // Assure markdown format listing (report) works 222 func TestVulnListCdx13Markdown(t *testing.T) { 223 testInfo := NewVulnTestInfoBasic( 224 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 225 FORMAT_MARKDOWN, 226 nil) 227 228 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 229 } 230 231 // Assure json format listing (report) works 232 func TestVulnListCdx13JSONWithIndent(t *testing.T) { 233 testInfo := NewVulnTestInfoBasic( 234 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 235 FORMAT_JSON, 236 nil) 237 // Note: this value will keep going down as we add more custom marshallers for vuln. structs 238 testInfo.ResultExpectedLineCount = 191 239 testInfo.OutputIndent = 2 240 testInfo.ResultExpectedIndentLength = int(testInfo.OutputIndent) 241 testInfo.ResultExpectedIndentAtLineNum = 1 242 buffer, _, _ := innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 243 getLogger().Debugf("result:\n%s", buffer.String()) 244 // Validate output data 245 verifyFileLineCountAndIndentation(t, buffer, &testInfo.CommonTestInfo) 246 } 247 248 // ------------------------------------------- 249 // CDX variants - WHERE clause tests 250 // ------------------------------------------- 251 252 func TestVulnListTextCdx14WhereClauseAndResultsByIdStartsWith(t *testing.T) { 253 TEST_INPUT_WHERE_CLAUSE := "id=CVE" 254 TEST_OUTPUT_CONTAINS := []string{"CVE-2022-42004", "502", "NVD", "jackson-databind"} 255 EXPECTED_OUTPUT_LINE_COUNT := 6 256 257 testInfo := NewVulnTestInfo( 258 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 259 FORMAT_TEXT, 260 TI_LIST_SUMMARY_FALSE, 261 TEST_INPUT_WHERE_CLAUSE, 262 EXPECTED_OUTPUT_LINE_COUNT) 263 testInfo.ResultLineContainsValues = TEST_OUTPUT_CONTAINS 264 testInfo.ResultLineContainsValuesAtLineNum = 2 265 result, _, _ := innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 266 getLogger().Debugf("result:\n%s", result.String()) 267 } 268 269 func TestVulnListTextCdx14WhereClauseDescContains(t *testing.T) { 270 TEST_INPUT_WHERE_CLAUSE := "description=^.*fasterxml.*" 271 TEST_OUTPUT_CONTAINS := []string{"CVE-2020-25649", "611", "7.5", "jackson-databind"} 272 EXPECTED_OUTPUT_LINE_COUNT := 4 273 274 testInfo := NewVulnTestInfo( 275 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 276 FORMAT_TEXT, 277 TI_LIST_SUMMARY_FALSE, 278 TEST_INPUT_WHERE_CLAUSE, 279 EXPECTED_OUTPUT_LINE_COUNT) 280 testInfo.ResultLineContainsValues = TEST_OUTPUT_CONTAINS 281 testInfo.ResultLineContainsValuesAtLineNum = 2 282 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 283 } 284 285 func TestVulnListTextCdx14WhereClauseSourceNameNVD(t *testing.T) { 286 TEST_INPUT_WHERE_CLAUSE := "source-name=NVD" 287 TEST_OUTPUT_CONTAINS := []string{"CVE-2022-42003", "502", "7.5", "NVD", "jackson-databind"} 288 EXPECTED_OUTPUT_LINE_COUNT := 6 289 290 testInfo := NewVulnTestInfo( 291 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 292 FORMAT_TEXT, 293 TI_LIST_SUMMARY_FALSE, 294 TEST_INPUT_WHERE_CLAUSE, 295 EXPECTED_OUTPUT_LINE_COUNT) 296 testInfo.ResultLineContainsValues = TEST_OUTPUT_CONTAINS 297 testInfo.ResultLineContainsValuesAtLineNum = 3 298 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 299 } 300 301 func TestVulnListTextCdx14WhereClauseSourceUrlCVE2022(t *testing.T) { 302 TEST_INPUT_WHERE_CLAUSE := "source-url=CVE-2022" 303 TEST_OUTPUT_CONTAINS := []string{"CVE-2022-42003", "502", "CVSSv31", "7.5", "NVD", "2022-10-02", "FasterXML"} 304 EXPECTED_OUTPUT_LINE_COUNT := 5 305 306 testInfo := NewVulnTestInfo( 307 TEST_VULN_CDX_1_3_EXAMPLE_1_BOM_VEX, 308 FORMAT_TEXT, 309 TI_LIST_SUMMARY_FALSE, 310 TEST_INPUT_WHERE_CLAUSE, 311 EXPECTED_OUTPUT_LINE_COUNT) 312 testInfo.ResultLineContainsValues = TEST_OUTPUT_CONTAINS 313 testInfo.ResultLineContainsValuesAtLineNum = 3 314 innerTestVulnList(t, testInfo, VULN_TEST_DEFAULT_FLAGS) 315 }