Skip to content

Commit

Permalink
Merge pull request #821 from coinbase/cwe_in_sarif
Browse files Browse the repository at this point in the history
Support CWE in SARIF where available
  • Loading branch information
joshuaostrom-cb authored Mar 22, 2023
2 parents a28fc0b + 3b0e934 commit 72dfc55
Show file tree
Hide file tree
Showing 21 changed files with 212 additions and 8 deletions.
21 changes: 20 additions & 1 deletion lib/sarif/bandit_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,29 @@ def parse_error(error)
end

def parse_issue(issue)
# Example issue
# {"code"=>"1 import cPickle\n2 import pickle\n3 import StringIO\n",
# "col_offset"=>0,
# "end_col_offset"=>14,
# "filename"=>"./main.py",
# "issue_confidence"=>"HIGH",
# "issue_cwe"=>{"id"=>502, "link"=>"https://cwe.mitre.org/data/definitions/502.html"},
# "issue_severity"=>"LOW",
# "issue_text"=>"Consider possible security implications associated with cPickle module.",
# "line_number"=>1,
# "line_range"=>[1],
# "more_info"=>"https://bandit.readthedocs.io/en/1.7.5/...",
# "test_id"=>"B403",
# "test_name"=>"blacklist"}

return parse_error(issue) if !issue.key?('issue_text')

key = issue["filename"] + ' ' + issue["line_number"].to_s + ' ' + issue['issue_text']
return nil if @issues.include? key

cwe = issue.dig('issue_cwe', 'id')
cwe = cwe.nil? ? '' : "CWE-#{cwe}"

@issues.add(key)
endline = issue['line_range'][issue['line_range'].size - 1]
{
Expand All @@ -51,7 +69,8 @@ def parse_issue(issue)
level: issue['issue_severity'],
details: (issue['issue_text']).to_s,
messageStrings: { "confidence": { "text": (issue['issue_severity']).to_s },
"severity": { "text": (issue['issue_severity']).to_s } },
"severity": { "text": (issue['issue_severity']).to_s },
"cwe": { "text": [cwe].to_s } },
properties: { 'severity': issue['issue_severity'].to_s },
start_line: issue["line_number"].to_i,
end_line: endline,
Expand Down
21 changes: 20 additions & 1 deletion lib/sarif/brakeman_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ def parse_error(error)
end

def parse_issue(issue)
# Example issue
# {"warning_type"=>"Dangerous Eval",
# "warning_code"=>13,
# "fingerprint"=>"b16e1cd0d952433f80b0403b6a74aab0e98792ea015cc1b1fa5c003cbe7d56eb",
# "check_name"=>"Evaluation",
# "message"=>"User input in eval",
# "file"=>"app/controllers/static_controller_controller.rb",
# "line"=>3,
# "link"=>"https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
# "code"=>"eval(params[:evil])",
# "render_path"=>nil,
# "location"=>{"type"=>"method", "class"=>"StaticControllerController", "method"=>"index"},
# "user_input"=>"params[:evil]",
# "confidence"=>"High",
# "cwe_id"=>[913, 95]}

cwes = issue.fetch('cwe_id', []).map { |cwe| "CWE-#{cwe}" }

return parse_error(issue) if issue.key?('error')

{
Expand All @@ -47,7 +65,8 @@ def parse_issue(issue)
details: (issue['message']).to_s,
messageStrings: { "title": { "text": (issue['check_name']).to_s },
"type": { "text": (issue['warning_type']).to_s },
"warning_code": { "text": (issue['warning_code']).to_s } },
"warning_code": { "text": (issue['warning_code']).to_s },
"cwe": { "text": cwes.to_s } },
properties: { 'fingerprint': issue['fingerprint'].to_s,
'confidence': issue['confidence'].to_s,
'severity': "",
Expand Down
13 changes: 13 additions & 0 deletions lib/sarif/bundle_audit_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ def initialize(scan_report, repo_path = nil, scanner_config = {})
end

def parse_issue(issue)
# Example issue
# {:type=>"InsecureSource", :source=>"http://rubygems.org/", :line_number=>123}

# Another example:
# {:type=>"UnpatchedGem", :cve=>"CVE1234", :url=>"1", :line_number=>456, :name=>"boo"}

# OSV example
# {:osvdb=>"osvd value", :url=>"3", :line_number=>789}

# !!!! Note CVEs are not CWEs but we lack a mapping to CWE
cves = [issue.fetch(:cve, "")]

result = {
id: issue[:cve] || issue[:osvdb].to_s,
name: issue[:advisory_title],
Expand All @@ -21,6 +33,7 @@ def parse_issue(issue)
"unaffected_versions": { "text": (issue[:unaffected_versions]).to_s },
"title": { "text": (issue[:advisory_title]).to_s },
"osvdb": { "text": (issue[:osdvb]).to_s },
"cwe": { "text": cves.to_s },
"type": { "text": (issue[:type]).to_s },
"version": { "text": (issue[:version]).to_s } },
properties: { 'severity': (issue[:cvss]).to_s },
Expand Down
42 changes: 42 additions & 0 deletions lib/sarif/cargo_audit_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,47 @@ def parse_issue(issue)
return parse_yanked(issue) if issue['kind'] == 'yanked'
return nil if @issues.include?(issue.dig('advisory', 'id'))

# rubocop:disable Layout/LineLength

# Example issue
# {"advisory"=>
# {"id"=>"RUSTSEC-2019-0010",
# "package"=>"libflate",
# "title"=>"MultiDecoder::read() drops uninitialized memory of ...",
# "description"=>
# "Affected versions of libflate have set a field of an internalnd revert ...",
# "date"=>"2019-07-04",
# "aliases"=>["CVE-2019-15552"],
# "related"=>[],
# "collection"=>"crates",
# "categories"=>[],
# "keywords"=>["drop", "use-after-free"],
# "cvss"=>"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
# "informational"=>nil,
# "references"=>[],
# "source"=>nil,
# "url"=>"https://github.com/sile/libflate/issues/35",
# "withdrawn"=>nil},
# "versions"=>{"patched"=>[">=0.1.25"], "unaffected"=>["<0.1.14"]},
# "affected"=>{"arch"=>[], "os"=>[], "functions"=>{"libflate::gzip::MultiDecoder::read"=>["<0.1.25, >=0.1.14"]}},
# "package"=>
# {"name"=>"libflate",
# "version"=>"0.1.19",
# "source"=>"registry+https://github.com/rust-lang/crates.io-index",
# "checksum"=>nil,
# "dependencies"=>
# [{"name"=>"adler32", "version"=>"1.1.0", "source"=>"registry+https://github.com/rust-lang/crates.io-index"},
# {"name"=>"crc32fast", "version"=>"1.2.0", "source"=>"registry+https://github.com/rust-lang/crates.io-index"},
# {"name"=>"rle-decode-fast", "version"=>"1.0.1", "source"=>"registry+https://github.com/rust-lang/crates.io-index"},
# {"name"=>"take_mut", "version"=>"0.2.2", "source"=>"registry+https://github.com/rust-lang/crates.io-index"}],
# "replace"=>nil}}

# rubocop:enable Layout/LineLength

# !!!! Note CVEs are not CWEs but we lack a mapping to CWE
cves = issue.dig('advisory', 'aliases')
cves = [] if cves.nil?

@issues.add(issue.dig('advisory', 'id'))
advisory = issue['advisory'] || {}
parsed_issue = {
Expand All @@ -55,6 +96,7 @@ def parse_issue(issue)
messageStrings: { "package": { "text": (advisory['package']).to_s },
"title": { "text": (advisory['title']).to_s },
"severity": { "text": (advisory['cvss']).to_s },
"cwe": { "text": cves.to_s },
"patched_versions": { "text": issue.dig('versions', 'patched').to_s },
"unaffected_versions": { "text": issue.dig('versions',
'unaffected').to_s } },
Expand Down
20 changes: 18 additions & 2 deletions lib/sarif/gosec_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ def parse_issue(issue)
if issue[:id] == SCANNER_ERROR
issue
else
# rubocop:disable Layout/LineLength

# Example issue
# {"severity"=>"MEDIUM",
# "confidence"=>"HIGH",
# "cwe"=>{"ID"=>"78", "URL"=>"https://cwe.mitre.org/data/definitions/78.html"},
# "rule_id"=>"G204",
# "details"=>"Subprocess launched with variable",
# "file"=>"parser.go",
# "code"=>"37: \tbuf := bytes.NewReader(b)\n38: \tcmd := exec.Command(path, \"--format\", \"json\", \"--type\", \"ast\")\n39: \tcmd.Stdin = buf\n",
# "line"=>"38",
# "column"=>"9"}

# rubocop:enable Layout/LineLength

id = issue['details'] + ' ' + issue['file'] + ' ' + issue['line']
return nil if @issues.include?(id)

Expand All @@ -77,16 +92,17 @@ def parse_issue(issue)
filepath.relative_path_from(base_path).to_s
end

cwe = "CWE-#{id}"
@issues.add(id)
{
id: issue['rule_id'],
name: "CWE-#{id}",
name: cwe,
level: issue['severity'],
details: "#{issue['details']} \nSeverity: #{issue['severity']}\nConfidence:"\
" #{issue['confidence']}\nCWE: #{url}",
messageStrings: { "severity": { "text": (issue['severity']).to_s },
"confidence": { "text": (issue['confidence']).to_s },
"cwe": { "text": url.to_s } },
"cwe": { "text": [cwe].to_s } },
properties: { 'severity': (issue['severity']).to_s },
start_line: issue['line'].to_i,
start_column: issue['column'].to_i,
Expand Down
30 changes: 30 additions & 0 deletions lib/sarif/npm_audit_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,36 @@ def parse_issue(issue)
@results.push(id) if !@exceptions.include?(id)
@issues.add(id)

# rubocop:disable Layout/LineLength

# Example issue
# {:findings=>[{:version=>"1.2.0", :paths=>["merge"]}],
# :metadata=>nil,
# :vulnerable_versions=>"<2.1.1",
# :module_name=>"merge",
# :severity=>"high",
# :github_advisory_id=>"GHSA-7wpw-2hjm-89gp",
# :cves=>["CVE-2020-28499"],
# :access=>"public",
# :patched_versions=>">=2.1.1",
# :updated=>"2021-03-18T22:54:26.000Z",
# :recommendation=>"Upgrade to version 2.1.1 or later",
# :cwe=>"CWE-915",
# :found_by=>nil,
# :deleted=>nil,
# :id=>1005415,
# :references=>
# "- https://nvd.nist.gov/vuln/detail/CVE-2020-28499\n- https://github.com/yeikos/js.merge/commit/7b0ddc2701d813f2ba289b32d6a4b9d4cc235fb4\n- https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-1071049\n- https://snyk.io/vuln/SNYK-JS-MERGE-1042987\n- https://vuldb.com/?id.170146\n- https://github.com/yeikos/js.merge/blob/56ca75b2dd0f2820f1e08a49f62f04bbfb8c5f8f/src/index.ts#L64\n- https://github.com/yeikos/js.merge/blob/master/src/index.ts%23L64\n- https://github.com/advisories/GHSA-7wpw-2hjm-89gp",
# :created=>"2021-11-18T16:00:48.538Z",
# :reported_by=>nil,
# :title=>"Prototype Pollution in merge",
# :npm_advisory_id=>nil,
# :overview=>"All versions of package merge <2.1.1 are vulnerable to Prototype Pollution via _recursiveMerge .",
# :url=>"https://github.com/advisories/GHSA-7wpw-2hjm-89gp",
# :line_number=>23}

# rubocop:enable Layout/LineLength

parsed_issue = {
id: id,
name: issue[:title],
Expand Down
14 changes: 14 additions & 0 deletions lib/sarif/osv/base_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ def parse_scan_report!
end

def parse_issue(issue)
# Example issue
# {"Package"=>"github.com/syncthing/syncthing",
# "Vulnerable Version"=>"0",
# "Version Detected"=>"1.14.0",
# "Patched Version"=>"1.15.0",
# "ID"=>"CVE-2021-21404",
# "Database"=>"Github Advisory Database",
# "Summary"=>"Crash due to malformed relay protocol message",
# "References"=>
# "https://github.com/advisories/GHSA-x462-89pf-6r5h, https://nvd.nist.gov...",
# "Source"=>"https://osv.dev/list",
# "Severity"=>"LOW"}

parsed_issue = {
id: issue['ID'],
name: SCANNER_NAME,
Expand All @@ -33,6 +46,7 @@ def parse_issue(issue)
messageStrings: { "package": { "text": issue['Package'].to_s },
"title": { "text": issue['Summary'].to_s },
"severity": { "text": issue['Severity'].to_s },
"cwe": { "text": [issue['ID']].to_s },
"patched_versions": { "text": issue['Patched Version'].to_s },
"vulnerable_versions": {
"text": issue['Vulnerable Version'].to_s
Expand Down
4 changes: 4 additions & 0 deletions lib/sarif/pattern_search_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def parse_issue(issue)
return nil if issue[:required]
return nil if !issue[:forbidden]

# Example issue
# {:regex=>"Nerv", :forbidden=>true, :required=>false, :msg=>"not important string",
# :hit=>"lance.txt:3:Nerv housed the lance."}

url_info = issue[:hit].split(':')

{
Expand Down
3 changes: 3 additions & 0 deletions lib/sarif/repo_not_empty_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def parse_scan_report!
end

def parse_issue(issue)
# Example issue
# {:message=>"Salus was run on a blank directory. This ..."}

{
id: "RNE0001",
name: "RepositoryIsEmpty",
Expand Down
5 changes: 5 additions & 0 deletions lib/sarif/semgrep_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ def message(hit, miss)
def parse_hit(hit)
return nil if !hit[:forbidden]

# Example hit
# {:id=>"11d6bdec931137a1063338f1f80a631f5b1f2fc2", :pattern=>"$X == $X", :config=>nil,
# :forbidden=>true, :required=>false, :msg=>"Useless equality test.",
# :hit=>"examples/trivial2.py:10: if user.id == user.id:", :severity=>"ERROR"}

location = hit[:hit].split(":") # [file_name, line, code_preview]
# location looks like "file.js:123:my_function()"
code = if location.size > 3 # code itself has :, like "abc: [xyz]"
Expand Down
4 changes: 4 additions & 0 deletions lib/sarif/trufflehog_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ def parse_error(error)
def parse_issue(issue)
return parse_error(issue) if issue[:scanner_err]

# Example issue
# {"SHA256 of Leaked Credential"=>"REDACTED-SHA",
# "File"=>"url.txt", "Line Num"=>1, "ID"=>"JDBC-PLAIN", "Verified"=>false}

{
id: issue['ID'],
name: "Leaked credential",
Expand Down
11 changes: 11 additions & 0 deletions lib/sarif/yarn_audit_sarif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ def parse_issue(issue)
return nil if @issues.include?(id)

@issues.add(id)

# Example issue
# {"Package"=>"uglify-js",
# "Patched in"=>">=13.6.6",
# "More info"=>"https://github.com/advisories/GHSA-3p22-ghq8-v749",
# "Severity"=>"low",
# "Title"=>"Renderers can obtain access to random bluetooth device without ...",
# "ID"=>1006709,
# "DectectedVersions"=>["11.5.0"],
# "Dependency of"=>"uglify-js"}

parsed_issue = {
id: issue['ID'].to_s,
name: issue['Title'],
Expand Down
3 changes: 2 additions & 1 deletion spec/lib/sarif/bandit_sarif_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
level: "LOW",
details: "Consider possible security implications associated with cPickle module.",
messageStrings: { "confidence": { "text": "LOW" },
"severity": { "text": "LOW" } },
"severity": { "text": "LOW" },
"cwe": { "text": "[\"CWE-502\"]" } },
properties: { "severity": "LOW" },
start_line: 1,
start_column: 1,
Expand Down
3 changes: 2 additions & 1 deletion spec/lib/sarif/brakeman_sarif_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
details: "User input in eval",
messageStrings: { "title": { "text": "Evaluation" },
"type": { "text": "Dangerous Eval" },
"warning_code": { "text": "13" } },
"warning_code": { "text": "13" },
"cwe": { "text": "[\"CWE-913\", \"CWE-95\"]" } },
start_line: 3,
start_column: 1,
help_url: "https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
Expand Down
11 changes: 10 additions & 1 deletion spec/lib/sarif/bundle_audit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
details = 'There is a possible information disclosure / unintended method'
expect(expected_details).to include(details)

# rubocop:disable Layout/LineLength
expect(bundle_audit_sarif.parse_issue(issue)).to include(
id: "CVE-2021-22885",
name: "Possible Information Disclosure / Unintended Method Execution in Action Pack",
Expand All @@ -61,8 +62,16 @@
start_line: 8,
start_column: 1,
code: 'actionpack',
properties: { severity: "", detected_versions: ["4.1.15"] }
properties: { severity: "", detected_versions: ["4.1.15"] },
messageStrings: { cwe: { text: "[\"CVE-2021-22885\"]" }, osvdb: { text: "" },
package_name: { text: "actionpack" },
patched_versions: { text: "[\"~> 5.2.4.6\", \"~> 5.2.6\", \"~> 6.0.3, >= 6.0.3.7\", \">= 6.1.3.2\"]" },
severity: { text: "" },
title: { text: "Possible Information Disclosure / Unintended Method Execution in Action Pack" },
type: { text: "UnpatchedGem" }, unaffected_versions: { text: "[\"< 2.0.0\"]" },
version: { text: "4.1.15" } }
)
# rubocop:enable Layout/LineLength
else
details = 'There is a possible DoS vulnerability in the Token Authentication logic in'
expect(expected_details).to include(details)
Expand Down
1 change: 1 addition & 0 deletions spec/lib/sarif/cargo_audit_sarif_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"title": { "text": "MultiDecoder::read() drops uninitialized memory of"\
" arbitrary type on panic in client code" },
"severity": { "text": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" },
"cwe": { "text": "[\"CVE-2019-15552\"]" },
"patched_versions": { "text": "[\">=0.1.25\"]" },
"unaffected_versions": { "text": "[\"<0.1.14\"]" } },
help_url: "https://github.com/sile/libflate/issues/35",
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/sarif/gosec_sarif_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"https://cwe.mitre.org/data/definitions/798.html",
messageStrings: { "severity": { "text": "HIGH" },
"confidence": { "text": "LOW" },
"cwe": { "text": "https://cwe.mitre.org/data/definitions/798.html" } },
"cwe": { "text": "[\"CWE-798\"]" } },
start_line: 8,
start_column: 2,
help_url: "https://cwe.mitre.org/data/definitions/798.html",
Expand Down
3 changes: 3 additions & 0 deletions spec/lib/sarif/osv/go_osv_sarif_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def stub_req_with_valid_response
"helpUri" => "https://osv.dev/list",
"id" => "CVE-2021-21404",
"messageStrings" => {
"cwe" => {
"text" => "[\"CVE-2021-21404\"]"
},
"package" => {
"text" => "github.com/syncthing/syncthing"
},
Expand Down
Loading

0 comments on commit 72dfc55

Please sign in to comment.