Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(report): fix error with unmarshal of ExperimentalModifiedFindings #7463

Merged
merged 7 commits into from
Sep 11, 2024
26 changes: 23 additions & 3 deletions integration/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import (

func TestConvert(t *testing.T) {
type args struct {
input string
format string
scanners string
input string
format string
scanners string
showSuppressed bool
listAllPkgs bool
}
tests := []struct {
name string
Expand All @@ -37,6 +39,16 @@ func TestConvert(t *testing.T) {
},
golden: "testdata/npm-cyclonedx.json.golden",
},
{
name: "npm with suppressed vulnerability",
args: args{
input: "testdata/fixtures/convert/npm-with-suppressed.json.golden",
format: "json",
showSuppressed: true,
listAllPkgs: true,
},
golden: "testdata/fixtures/convert/npm-with-suppressed.json.golden",
},
}

for _, tt := range tests {
Expand All @@ -50,6 +62,14 @@ func TestConvert(t *testing.T) {
tt.args.format,
}

if tt.args.showSuppressed {
osArgs = append(osArgs, "--show-suppressed")
}

if tt.args.listAllPkgs {
osArgs = append(osArgs, "--list-all-pkgs")
}

// Set up the output file
outputFile := filepath.Join(t.TempDir(), "output.json")
if *update {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
{
"SchemaVersion": 2,
"CreatedAt": "2024-09-09T13:21:09.230231+06:00",
"ArtifactName": "package-lock.json",
"ArtifactType": "filesystem",
"Metadata": {
"ImageConfig": {
"architecture": "",
"created": "0001-01-01T00:00:00Z",
"os": "",
"rootfs": {
"type": "",
"diff_ids": null
},
"config": {}
}
},
"Results": [
{
"Target": "package-lock.json",
"Class": "lang-pkgs",
"Type": "npm",
"Packages": [
{
"ID": "[email protected]",
"Name": "debug",
"Identifier": {
"PURL": "pkg:npm/[email protected]",
"UID": "45acc377fa09cc3"
},
"Version": "3.0.1",
"Relationship": "direct",
"DependsOn": [
"[email protected]"
],
"Layer": {},
"Locations": [
{
"StartLine": 11,
"EndLine": 19
}
]
},
{
"ID": "[email protected]",
"Name": "ms",
"Identifier": {
"PURL": "pkg:npm/[email protected]",
"UID": "f51af0181daf2ced"
},
"Version": "2.0.0",
"Indirect": true,
"Relationship": "indirect",
"Layer": {},
"Locations": [
{
"StartLine": 20,
"EndLine": 25
}
]
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2017-20165",
"PkgID": "[email protected]",
"PkgName": "debug",
"PkgIdentifier": {
"PURL": "pkg:npm/[email protected]",
"UID": "45acc377fa09cc3"
},
"InstalledVersion": "3.0.1",
"FixedVersion": "3.1.0, 2.6.9",
"Status": "fixed",
"Layer": {},
"SeveritySource": "ghsa",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2017-20165",
"DataSource": {
"ID": "ghsa",
"Name": "GitHub Security Advisory npm",
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Anpm"
},
"Title": "A vulnerability classified as problematic has been found in debug-js d ...",
"Description": "A vulnerability classified as problematic has been found in debug-js debug up to 3.0.x. This affects the function useColors of the file src/node.js. The manipulation of the argument str leads to inefficient regular expression complexity. Upgrading to version 3.1.0 is able to address this issue. The identifier of the patch is c38a0166c266a679c8de012d4eaccec3f944e685. It is recommended to upgrade the affected component. The identifier VDB-217665 was assigned to this vulnerability.",
"Severity": "HIGH",
"CweIDs": [
"CWE-1333"
],
"VendorSeverity": {
"ghsa": 3,
"nvd": 3
},
"CVSS": {
"ghsa": {
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"V3Score": 7.5
},
"nvd": {
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"V3Score": 7.5
}
},
"References": [
"https://github.com/debug-js/debug",
"https://github.com/debug-js/debug/commit/c38a0166c266a679c8de012d4eaccec3f944e685",
"https://github.com/debug-js/debug/commit/f53962e944a87e6ca9bb622a2a12dffc22a9bb5a",
"https://github.com/debug-js/debug/pull/504",
"https://github.com/debug-js/debug/releases/tag/2.6.9",
"https://github.com/debug-js/debug/releases/tag/3.1.0",
"https://nvd.nist.gov/vuln/detail/CVE-2017-20165",
"https://vuldb.com/?ctiid.217665",
"https://vuldb.com/?id.217665"
],
"PublishedDate": "2023-01-09T10:15:10.447Z",
"LastModifiedDate": "2024-05-17T01:17:24.28Z"
}
],
"ExperimentalModifiedFindings": [
{
"Type": "vulnerability",
"Status": "not_affected",
"Statement": "vulnerable_code_not_in_execute_path",
"Source": "./vex.json",
"Finding": {
"VulnerabilityID": "CVE-2017-16137",
"PkgID": "[email protected]",
"PkgName": "debug",
"PkgIdentifier": {
"PURL": "pkg:npm/[email protected]",
"UID": "45acc377fa09cc3"
},
"InstalledVersion": "3.0.1",
"FixedVersion": "2.6.9, 3.1.0, 3.2.7, 4.3.1",
"Status": "fixed",
"Layer": {},
"SeveritySource": "ghsa",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2017-16137",
"DataSource": {
"ID": "ghsa",
"Name": "GitHub Security Advisory npm",
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Anpm"
},
"Title": "nodejs-debug: Regular expression Denial of Service",
"Description": "The debug module is vulnerable to regular expression denial of service when untrusted user input is passed into the o formatter. It takes around 50k characters to block for 2 seconds making this a low severity issue.",
"Severity": "LOW",
"CweIDs": [
"CWE-400"
],
"VendorSeverity": {
"ghsa": 1,
"nvd": 2,
"redhat": 2
},
"CVSS": {
"ghsa": {
"V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L",
"V3Score": 3.7
},
"nvd": {
"V2Vector": "AV:N/AC:L/Au:N/C:N/I:N/A:P",
"V3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"V2Score": 5,
"V3Score": 5.3
},
"redhat": {
"V3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"V3Score": 5.3
}
},
"References": [
"https://access.redhat.com/security/cve/CVE-2017-16137",
"https://github.com/debug-js/debug/commit/4e2150207c568adb9ead8f4c4528016081c88020",
"https://github.com/debug-js/debug/commit/71169065b5262f9858ac78cc0b688c84a438f290",
"https://github.com/debug-js/debug/commit/b6d12fdbc63b483e5c969da33ea6adc09946b5ac",
"https://github.com/debug-js/debug/commit/f53962e944a87e6ca9bb622a2a12dffc22a9bb5a",
"https://github.com/debug-js/debug/issues/797",
"https://github.com/visionmedia/debug",
"https://github.com/visionmedia/debug/issues/501",
"https://github.com/visionmedia/debug/pull/504",
"https://lists.apache.org/thread.html/r8ba4c628fba7181af58817d452119481adce4ba92e889c643e4c7dd3%40%3Ccommits.netbeans.apache.org%3E",
"https://lists.apache.org/thread.html/r8ba4c628fba7181af58817d452119481adce4ba92e889c643e4c7dd3@%3Ccommits.netbeans.apache.org%3E",
"https://lists.apache.org/thread.html/rb5ac16fad337d1f3bb7079549f97d8166d0ef3082629417c39f12d63%40%3Cnotifications.netbeans.apache.org%3E",
"https://lists.apache.org/thread.html/rb5ac16fad337d1f3bb7079549f97d8166d0ef3082629417c39f12d63@%3Cnotifications.netbeans.apache.org%3E",
"https://nodesecurity.io/advisories/534",
"https://nvd.nist.gov/vuln/detail/CVE-2017-16137",
"https://www.cve.org/CVERecord?id=CVE-2017-16137"
],
"PublishedDate": "2018-06-07T02:29:03.817Z",
"LastModifiedDate": "2023-11-07T02:40:28.13Z"
}
}
]
}
]
}
99 changes: 99 additions & 0 deletions pkg/types/finding.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package types

import (
"encoding/json"

"golang.org/x/xerrors"
)

type FindingType string
type FindingStatus string

Expand Down Expand Up @@ -45,3 +51,96 @@ func NewModifiedFinding(f finding, status FindingStatus, statement, source strin
Finding: f,
}
}

// MarshalJSON correctly marshals ModifiedFinding.Finding given the type and `MarshalJSON` functions of struct fields
func (m *ModifiedFinding) MarshalJSON() ([]byte, error) {
var raw struct {
Type FindingType `json:"Type"`
Status FindingStatus `json:"Status"`
Statement string `json:"Statement"`
Source string `json:"Source"`
Finding json.RawMessage `json:"Finding"`
}
raw.Type = m.Type
raw.Status = m.Status
raw.Statement = m.Statement
raw.Source = m.Source

// Define a `Finding` type and marshal as a struct of that type.
// This is necessary to run the `MarshalJSON` functions on the struct fields.
var err error
switch val := m.Finding.(type) {
case DetectedVulnerability:
if raw.Finding, err = json.Marshal(&val); err != nil {
return nil, xerrors.Errorf("unable to marshal `DetectedVulnerability` Findings: %w", err)
}
case DetectedMisconfiguration:
if raw.Finding, err = json.Marshal(&val); err != nil {
return nil, xerrors.Errorf("unable to marshal `DetectedMisconfiguration` Findings: %w", err)
}
case DetectedSecret:
if raw.Finding, err = json.Marshal(&val); err != nil {
return nil, xerrors.Errorf("unable to marshal `DetectedSecret` Findings: %w", err)
}
case DetectedLicense:
if raw.Finding, err = json.Marshal(&val); err != nil {
return nil, xerrors.Errorf("unable to marshal `DetectedLicense` Findings: %w", err)
}
default:
return nil, xerrors.Errorf("invalid Finding type: %T", val)
}

return json.Marshal(&raw)
}

// UnmarshalJSON unmarshals ModifiedFinding given the type and `UnmarshalJSON` functions of struct fields
func (m *ModifiedFinding) UnmarshalJSON(data []byte) error {
raw := struct {
Type FindingType `json:"Type"`
Status FindingStatus `json:"Status"`
Statement string `json:"Statement"`
Source string `json:"Source"`
Finding json.RawMessage `json:"Finding"`
}{}

if err := json.Unmarshal(data, &raw); err != nil {
return err
}

m.Type = raw.Type
m.Status = raw.Status
m.Statement = raw.Statement
m.Source = raw.Source
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we refactor as below?

    type Alias ModifiedFinding
    aux := &struct {
        Finding json.RawMessage `json:"Finding"`
        *Alias
    }{
        Alias: (*Alias)(m),
    }

cf.

// UnmarshalJSON customizes the JSON decoding of PkgIdentifier.
func (id *PkgIdentifier) UnmarshalJSON(data []byte) error {
type Alias PkgIdentifier
aux := &struct {
PURL string `json:",omitempty"`
*Alias
}{
Alias: (*Alias)(id),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.PURL != "" {
p, err := packageurl.FromString(aux.PURL)
if err != nil {
return err
} else if len(p.Qualifiers) == 0 {
p.Qualifiers = nil
}
id.PURL = &p
}
return nil
}

Copy link
Contributor Author

@DmitriyLewen DmitriyLewen Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 38b0d2b


// Select struct by m.Type to avoid errors with Unmarshal
switch m.Type {
case FindingTypeVulnerability:
rawFinding := DetectedVulnerability{}
if err := json.Unmarshal(raw.Finding, &rawFinding); err != nil {
return xerrors.Errorf("unable to unmarshal %q type: %w", m.Type, err)
}
m.Finding = rawFinding
case FindingTypeMisconfiguration:
rawFinding := DetectedMisconfiguration{}
if err := json.Unmarshal(raw.Finding, &rawFinding); err != nil {
return xerrors.Errorf("unable to unmarshal %q type: %w", m.Type, err)
}
m.Finding = rawFinding
case FindingTypeSecret:
rawFinding := DetectedSecret{}
if err := json.Unmarshal(raw.Finding, &rawFinding); err != nil {
return xerrors.Errorf("unable to unmarshal %q type: %w", m.Type, err)
}
m.Finding = rawFinding
case FindingTypeLicense:
rawFinding := DetectedLicense{}
if err := json.Unmarshal(raw.Finding, &rawFinding); err != nil {
return xerrors.Errorf("unable to unmarshal %q type: %w", m.Type, err)
}
m.Finding = rawFinding
default:
return xerrors.Errorf("invalid Finding type: %s", m.Type)
}

return nil
}