diff --git a/lib/salus/plugin_manager.rb b/lib/salus/plugin_manager.rb index c26fff0d..466a9528 100644 --- a/lib/salus/plugin_manager.rb +++ b/lib/salus/plugin_manager.rb @@ -18,11 +18,12 @@ def register_listener(listener) @@listners << listener end - def apply_filter(filter_family, filter_method, data) + def apply_filter(filter_family, filter_method, *data) + result = data&.first @@filters[filter_family]&.each do |f| - data = f.__send__(filter_method, data) if f.respond_to?(filter_method) + result = f.__send__(filter_method, *data) if f.respond_to?(filter_method) end - data + result end def send_event(event_name, *data) diff --git a/lib/salus/report.rb b/lib/salus/report.rb index ad277ecd..77512f22 100644 --- a/lib/salus/report.rb +++ b/lib/salus/report.rb @@ -2,6 +2,7 @@ require 'deepsort' require 'salus/formatting' require 'salus/bugsnag' +require 'zlib' # Adding aliases to prevent deep_sort from failing when comparing symbols and strings class Symbol @@ -63,8 +64,8 @@ def apply_report_hash_filters(report_hash) Salus::PluginManager.apply_filter(:salus_report, :filter_report_hash, report_hash) end - def apply_report_sarif_filters(sarif_json) - Salus::PluginManager.apply_filter(:salus_report, :filter_report_sarif, sarif_json) + def apply_report_sarif_filters(sarif_json, config = nil) + Salus::PluginManager.apply_filter(:salus_report, :filter_report_sarif, sarif_json, config) end # Syntatical sugar register salus_report filters @@ -197,6 +198,8 @@ def to_json def to_sarif(config = {}) sarif_json = Sarif::SarifReport.new(@scan_reports, config, @repo_path, @config).to_sarif begin + # This is dangerous in salus as rule mappings use the ruleIndex + # which can change when a deep sort is executed sorted_sarif = JSON.parse(sarif_json).deep_sort rescue StandardError => e bugsnag_notify(e.inspect + "\n" + e.message + "\nResult String: " + to_h.to_s) @@ -205,8 +208,9 @@ def to_sarif(config = {}) # We will validate to ensure the applied filter # doesn't produce any invalid SARIF sarif_json = JSON.pretty_generate(sorted_sarif) - Sarif::SarifReport.validate_sarif(apply_report_sarif_filters(sarif_json)) + Sarif::SarifReport.validate_sarif(apply_report_sarif_filters(sarif_json, config)) rescue StandardError => e + puts "Failure in validing SARIF" bugsnag_notify(e.class.to_s + " " + e.message + "\nBuild Info:" + @builds.to_s) end @@ -300,6 +304,7 @@ def publish_report(directive) else raise ExportReportError, "unknown report format #{directive['format']}" end + if Salus::Config::REMOTE_URI_SCHEME_REGEX.match?(URI(uri).scheme) Salus::ReportRequest.send_report(directive, report_body(directive), uri) else @@ -395,12 +400,19 @@ def write_report_to_file(report_file_path, report_string) "Cannot write file #{report_file_path} - #{e.class}: #{e.message}" end + def compress(data) + Base64.strict_encode64(Zlib::Deflate.deflate(data)) + end + def report_body_hash(config, data) return data unless config&.key?('post') && config['post'].present? body_hash = config['post']['additional_params'] || {} return body_hash unless config['post']['salus_report_param_name'] + compress_sarif = config.dig('post', 'salus_report_options', 'gzip-base64') + data = compress(JSON.pretty_generate(data)) if ["true", true].include?(compress_sarif) + body_hash[config['post']['salus_report_param_name']] = data body_hash end diff --git a/lib/salus/scanners/brakeman.rb b/lib/salus/scanners/brakeman.rb index cacca3a9..95383ca7 100644 --- a/lib/salus/scanners/brakeman.rb +++ b/lib/salus/scanners/brakeman.rb @@ -114,6 +114,8 @@ def merged_ignore_file_contents def ignore_list return [] unless user_supplied_ignore? + return [] unless File.exist?(@config['ignore']) + data = JSON.parse(File.read(@config['ignore'])) return [] unless data.key?('ignored_warnings') diff --git a/lib/sarif/base_sarif.rb b/lib/sarif/base_sarif.rb index 0fb35d2a..c6ca7609 100644 --- a/lib/sarif/base_sarif.rb +++ b/lib/sarif/base_sarif.rb @@ -1,6 +1,7 @@ require 'json' require 'set' require 'sarif/shared_objects' +require 'deepsort' module Sarif class BaseSarif @@ -168,13 +169,25 @@ def build_runs_object(supported) # Salus::ScanReport invocation = build_invocations(@scan_report, supported) - { - "tool" => build_tool(rules: rules), + runs_object = { + "tool" => build_tool(rules: rules.deep_sort), # we deep sort here as + # our SARIF needs to be deep sorted for easier comparisions "conversion" => build_conversion, "results" => results, "invocations" => [invocation], "originalUriBaseIds" => uri_info } + # Ensure our ruleIndex values are correct after the + # prior deep sorting + remap_rule_ids(runs_object) + end + + def remap_rule_ids(run) + rules = run['tool'][:driver]['rules'] + run['results'].each do |r| + r[:ruleIndex] = rules.index { |rule| rule[:id] == r[:ruleId] } + end + run end # Returns the conversion object for the SARIF report 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 2a60ddca..a3cbcc5b 100644 --- a/spec/fixtures/npm_audit/success_with_exceptions/salus-sarif.yaml +++ b/spec/fixtures/npm_audit/success_with_exceptions/salus-sarif.yaml @@ -20,4 +20,14 @@ scanner_configs: advisory_id: "1091018", changed_by: "joshua.ostrom", 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.", - } \ No newline at end of file + } + - { + advisory_id: "1091686", + changed_by: "joshua.ostrom", + notes: "WAGMI", + } + - { + advisory_id: "1091710", + changed_by: "joshua.ostrom", + notes: "BTC $26.5K", + } diff --git a/spec/fixtures/sarifs/diff/git_diff_yarn.txt b/spec/fixtures/sarifs/diff/git_diff_yarn.txt deleted file mode 100644 index 10815378..00000000 --- a/spec/fixtures/sarifs/diff/git_diff_yarn.txt +++ /dev/null @@ -1,36 +0,0 @@ -diff --git a/yarn.lock b/yarn.lock -index 06e7d3ba9ef..e3f8fbb5889 100644 ---- a/yarn.lock -+++ b/yarn.lock -@@ -10599,10 +10599,10 @@ base64-arraybuffer@^0.2.0: - resolved "https://registry-npm.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45" - integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ== - --jspdf@2.5.1: -- version "2.5.1" -- resolved "https://registry.yarnpkg.com/jspdf/-/jspdf-2.5.1.tgz#00c85250abf5447a05f3b32ab9935ab4a56592cc" -- integrity sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA== -+jspdf@2.3.1: -+ version "2.3.1" -+ resolved "https://registry-npm.com/jspdf/-/jspdf-2.3.1.tgz#313d117234b546469694a1fd81a1e02411647576" -+ integrity sha512-1vp0USP1mQi1h7NKpwxjFgQkJ5ncZvtH858aLpycUc/M+r/RpWJT8PixAU7Cw/3fPd4fpC8eB/Bj42LnsR21YQ== - dependencies: -- "@babel/runtime" "^7.14.0" - atob "^2.1.2" - btoa "^1.2.1" - fflate "^0.4.8" -@@ -31897,12 +31889,12 @@ text-encoding@0.7.0: - resolved "https://registry-npm.com/text-encoding/-/text-encoding-0.7.0.tgz#f895e836e45990624086601798ea98e8f36ee643" - integrity sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA== - --text-segmentation@^1.0.2, text-segmentation@^1.0.3: -- version "1.0.3" -- resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" -- integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== -+text-segmentation@^1.0.2: -+ version "1.0.2" -+ resolved "https://registry-npm.com/text-segmentation/-/text-segmentation-1.0.2.tgz#1f828fa14aa101c114ded1bda35ba7dcc17c9858" -+ integrity sha512-uTqvLxdBrVnx/CFQOtnf8tfzSXFm+1Qxau7Xi54j4OPTZokuDOX8qncQzrg2G8ZicAMOM8TgzFAYTb+AqNO4Cw== - dependencies: -- utrie "^1.0.2" -+ utrie "^1.0.1" diff --git a/spec/fixtures/sorted_results/sorted_sarif.json b/spec/fixtures/sorted_results/sorted_sarif.json index e876ce00..5c01847b 100644 --- a/spec/fixtures/sorted_results/sorted_sarif.json +++ b/spec/fixtures/sorted_results/sorted_sarif.json @@ -31,7 +31,7 @@ "text": "Useless equality test.. Pattern 1 == $X is required but not found." }, "ruleId": "Required Pattern Not Found", - "ruleIndex": 1 + "ruleIndex": 0 }, { "level": "error", @@ -53,7 +53,7 @@ "text": "Syntax error at line /home/spec/fixtures/semgrep/invalid/unparsable_py.py:3:\n `print(\"foo\"` was unexpected" }, "ruleId": "SAL002", - "ruleIndex": 0 + "ruleIndex": 1 } ], "tool": { diff --git a/spec/lib/sarif/brakeman_sarif_spec.rb b/spec/lib/sarif/brakeman_sarif_spec.rb index a1b7a10a..d505c140 100644 --- a/spec/lib/sarif/brakeman_sarif_spec.rb +++ b/spec/lib/sarif/brakeman_sarif_spec.rb @@ -186,7 +186,7 @@ # Check result info expect(result['ruleId']).to eq('13') - expect(result['ruleIndex']).to eq(0) + expect(result['ruleIndex']).to eq(2) expect(result['level']).to eq('error') expect(result['locations'][0]['physicalLocation']['region']['startLine']).to eq(3) snippet = result['locations'][0]['physicalLocation']['region']['snippet']['text'].to_s diff --git a/spec/lib/sarif/osv/maven_osv_sarif_spec.rb b/spec/lib/sarif/osv/maven_osv_sarif_spec.rb index 9a28b8f4..6f00fc25 100644 --- a/spec/lib/sarif/osv/maven_osv_sarif_spec.rb +++ b/spec/lib/sarif/osv/maven_osv_sarif_spec.rb @@ -91,7 +91,7 @@ def stub_req_with_valid_response "severity" => "HIGH" }, "ruleId" => "CVE-2018-15756", - "ruleIndex" => 5 + "ruleIndex" => 0 } ) diff --git a/spec/lib/sarif/pattern_search_sarif_spec.rb b/spec/lib/sarif/pattern_search_sarif_spec.rb index 22ff3ee0..176569df 100644 --- a/spec/lib/sarif/pattern_search_sarif_spec.rb +++ b/spec/lib/sarif/pattern_search_sarif_spec.rb @@ -44,7 +44,7 @@ expect(results).to include( { "ruleId": "Forbidden Pattern Found", - "ruleIndex": 0, + "ruleIndex": 1, "level": "error", "message": { "text": "not important string. Pattern Nerv is forbidden." diff --git a/spec/lib/sarif/semgrep_sarif_spec.rb b/spec/lib/sarif/semgrep_sarif_spec.rb index d2939a00..2a6828bb 100644 --- a/spec/lib/sarif/semgrep_sarif_spec.rb +++ b/spec/lib/sarif/semgrep_sarif_spec.rb @@ -146,7 +146,7 @@ expect(result).to include( { "ruleId" => "Required Pattern Not Found", - "ruleIndex" => 1, + "ruleIndex" => 0, "level" => "error", "message" => { "text" => "Useless equality test.. Pattern 1 == $X is required but not found." diff --git a/spec/lib/sarif/trufflehog_sarif_spec.rb b/spec/lib/sarif/trufflehog_sarif_spec.rb index b757bf84..34110920 100644 --- a/spec/lib/sarif/trufflehog_sarif_spec.rb +++ b/spec/lib/sarif/trufflehog_sarif_spec.rb @@ -27,7 +27,7 @@ } }], "message" => { "text" => "Leaked credential detected" }, "properties" => { "severity" => "high" }, - "ruleId" => "FlatIO-PLAIN", "ruleIndex" => 1 } + "ruleId" => "FlatIO-PLAIN", "ruleIndex" => 0 } expected_vul1 = { "level" => "error", "locations" => [{ "physicalLocation" => { "artifactLocation" => { "uri" => "url.txt", @@ -40,7 +40,7 @@ } }], "message" => { "text" => "Leaked credential detected" }, "properties" => { "severity" => "high" }, - "ruleId" => "JDBC-PLAIN", "ruleIndex" => 0 } + "ruleId" => "JDBC-PLAIN", "ruleIndex" => 1 } expected_vul2 = { "level" => "error", "locations" => [{ "physicalLocation" => { "artifactLocation" => { "uri" => "url.txt", @@ -53,7 +53,7 @@ } }], "message" => { "text" => "Leaked credential detected" }, "properties" => { "severity" => "high" }, - "ruleId" => "JDBC-PLAIN", "ruleIndex" => 0 } + "ruleId" => "JDBC-PLAIN", "ruleIndex" => 1 } expect(result.size).to eq(3) [expected_vul0, expected_vul1, expected_vul2].each { |v| expect(result).to include(v) } end