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  }