diff --git a/lib/salus/scanners/yarn_audit.rb b/lib/salus/scanners/yarn_audit.rb index 1932059c..103b8187 100644 --- a/lib/salus/scanners/yarn_audit.rb +++ b/lib/salus/scanners/yarn_audit.rb @@ -14,6 +14,7 @@ class SemVersion < Gem::Version; end class ExportReportError < StandardError; end # the command was previously 'yarn audit --json', which had memory allocation issues # see https://github.com/yarnpkg/yarn/issues/7404 + LEGACY_YARN_AUDIT_COMMAND_JSON = 'yarn audit --json'.freeze LEGACY_YARN_AUDIT_COMMAND = 'yarn audit --no-color'.freeze LATEST_YARN_AUDIT_ALL_COMMAND = 'yarn npm audit --json'.freeze LATEST_YARN_AUDIT_PROD_COMMAND = 'yarn npm audit --environment'\ @@ -83,7 +84,7 @@ def handle_latest_yarn_audit "Severity" => advisory.dig("severity"), "Title" => advisory.dig("title"), "ID" => advisory_id.to_i, - "DectectedVersions" => versions, + "DetectedVersions" => versions, "Dependency of" => if dependency_of.nil? advisory.dig("module_name") else @@ -145,6 +146,8 @@ def handle_legacy_yarn_audit auto_fix_v2.run_auto_fix end + path_version_map = find_detected_versions + vulns = populate_detected_versions(path_version_map, vulns) vulns = combine_vulns(vulns) log(format_vulns(vulns)) report_stdout(vulns.to_json) @@ -302,6 +305,38 @@ def deep_copy_wo_paths(vulns) vuln_list end + def find_detected_versions + path_to_version = {} + shell_return = run_shell(LEGACY_YARN_AUDIT_COMMAND_JSON) + shell_return.stdout.each_line do |line| + if line.include? ('{"type":"auditAdvisory"') + data = JSON.parse(line) + advisories = data["data"]["advisory"] + unless advisories.nil? + advisories["findings"].each do |finding| + version = finding["version"] + paths = finding["paths"] + paths.each do |path| + path_to_version[path] = version + end + end + end + end + end + path_to_version + end + + def populate_detected_versions(path_version_map, vulns) + @vulns_w_paths.each do |v| + id = v["ID"] + path = v["Path"].gsub(" > ", ">") + vulns.each do |vuln| + vuln["DetectedVersions"] = [path_version_map[path]] if vuln["ID"] == id + end + end + vulns + end + def format_vulns(vulns) str = "" vulns.each do |vul| diff --git a/lib/sarif/yarn_audit_sarif.rb b/lib/sarif/yarn_audit_sarif.rb index a80b2275..fa4931de 100644 --- a/lib/sarif/yarn_audit_sarif.rb +++ b/lib/sarif/yarn_audit_sarif.rb @@ -36,8 +36,8 @@ def parse_issue(issue) uri: "yarn.lock", help_url: issue['More info'] } - unless issue['DectectedVersions'].nil? - parsed_issue[:properties][:detected_versions] = issue['DectectedVersions'] + unless issue['DetectedVersions'].nil? + parsed_issue[:properties][:detected_versions] = issue['DetectedVersions'] end if issue.key?("Line number") diff --git a/spec/fixtures/npm_audit/success_with_exceptions/salus-sarif.yaml b/spec/fixtures/npm_audit/success_with_exceptions/salus-sarif.yaml index b90e1710..f1cab84b 100644 --- a/spec/fixtures/npm_audit/success_with_exceptions/salus-sarif.yaml +++ b/spec/fixtures/npm_audit/success_with_exceptions/salus-sarif.yaml @@ -2,12 +2,12 @@ scanner_configs: NPMAudit: exceptions: - { - advisory_id: "1087834", - changed_by: "ryan.sears", + advisory_id: "1091017", + changed_by: "john.doe", notes: "See https://www.npmjs.com/advisories/39. We're not vulnerable to this because uglify is a transient dev dependency, and we never actually invoke it with our code.", } - { - advisory_id: "1087835", - changed_by: "ryan.sears", + advisory_id: "1091018", + changed_by: "jane.doe", notes: "See https://www.npmjs.com/advisories/48. We're not vulnerable to this because this is a regex dos and we have nothing that puts user input into it. The impact is also minimal.", } diff --git a/spec/lib/salus/scanners/yarn_audit_spec.rb b/spec/lib/salus/scanners/yarn_audit_spec.rb index 0d6016c1..98e282ba 100644 --- a/spec/lib/salus/scanners/yarn_audit_spec.rb +++ b/spec/lib/salus/scanners/yarn_audit_spec.rb @@ -81,19 +81,19 @@ expect(vul["ID"]).to be_kind_of(Integer) end - id_vuls = vulns.find { |v| v['ID'] == 1_085_631 } + id_vuls = vulns.find { |v| v['ID'] == 1_090_101 } # vul has 1 dependency of expected_vul = { "Package" => "lodash", "Patched in" => ">=4.17.12", "Dependency of" => "gulp-modify-file", - "More info" => "https://www.npmjs.com/advisories/1085631", + "More info" => "https://www.npmjs.com/advisories/1090101", "Severity" => "critical", "Title" => "Prototype Pollution in lodash", - "ID" => 1_085_631 } + "ID" => 1_090_101 } expect(id_vuls).to eq(expected_vul) id_vuls_w_paths = scanner.instance_variable_get(:@vulns_w_paths) - .find { |v| v['ID'] == 1_085_631 } + .find { |v| v['ID'] == 1_090_101 } expected_vul['Path'] = "gulp-modify-file > gulp > vinyl-fs > "\ "glob-watcher > gaze > globule > lodash" expect(id_vuls_w_paths).to eq(expected_vul) @@ -195,7 +195,7 @@ after_fix_scan.run expect(after_fix_scan.report.to_h.fetch(:passed)).to eq(false) after_fix_vulns = JSON.parse(after_fix_scan.report.to_h[:info][:stdout]) - expect(after_fix_vulns.size).to eq(22) + expect(after_fix_vulns.size).to eq(23) end end diff --git a/spec/lib/sarif/yarn_audit_sarif_spec.rb b/spec/lib/sarif/yarn_audit_sarif_spec.rb index d5c745a2..8da4c42f 100644 --- a/spec/lib/sarif/yarn_audit_sarif_spec.rb +++ b/spec/lib/sarif/yarn_audit_sarif_spec.rb @@ -68,6 +68,7 @@ issue = JSON.parse(scanner.report.to_h[:info][:stdout])[0] yarn_sarif = Sarif::YarnAuditSarif.new(scanner.report, path) + expect(yarn_sarif.parse_issue(issue)).to include( id: "1005415", name: "Prototype Pollution in merge", @@ -83,7 +84,7 @@ help_url: "https://www.npmjs.com/advisories/1005415", start_line: 21, start_column: 1, - properties: { severity: "high" } + properties: { severity: "high", detected_versions: [nil] } ) end end