owasp-glue 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +27 -0
- data/FEATURES +19 -0
- data/README.md +117 -0
- data/bin/glue +67 -0
- data/lib/glue.rb +317 -0
- data/lib/glue/event.rb +14 -0
- data/lib/glue/filters.rb +41 -0
- data/lib/glue/filters/base_filter.rb +19 -0
- data/lib/glue/filters/jira_one_time_filter.rb +57 -0
- data/lib/glue/filters/remove_all_filter.rb +16 -0
- data/lib/glue/filters/zap_consdensing_filter.rb +76 -0
- data/lib/glue/finding.rb +52 -0
- data/lib/glue/mounters.rb +55 -0
- data/lib/glue/mounters/base_mounter.rb +31 -0
- data/lib/glue/mounters/docker_mounter.rb +44 -0
- data/lib/glue/mounters/filesystem_mounter.rb +20 -0
- data/lib/glue/mounters/git_mounter.rb +52 -0
- data/lib/glue/mounters/iso_mounter.rb +42 -0
- data/lib/glue/mounters/url_mounter.rb +28 -0
- data/lib/glue/options.rb +269 -0
- data/lib/glue/reporters.rb +50 -0
- data/lib/glue/reporters/base_reporter.rb +21 -0
- data/lib/glue/reporters/csv_reporter.rb +19 -0
- data/lib/glue/reporters/jira_reporter.rb +59 -0
- data/lib/glue/reporters/json_reporter.rb +20 -0
- data/lib/glue/reporters/text_reporter.rb +19 -0
- data/lib/glue/scanner.rb +28 -0
- data/lib/glue/tasks.rb +124 -0
- data/lib/glue/tasks/av.rb +42 -0
- data/lib/glue/tasks/base_task.rb +80 -0
- data/lib/glue/tasks/brakeman.rb +58 -0
- data/lib/glue/tasks/bundle-audit.rb +95 -0
- data/lib/glue/tasks/checkmarx.rb +60 -0
- data/lib/glue/tasks/dawnscanner.rb +55 -0
- data/lib/glue/tasks/eslint.rb +69 -0
- data/lib/glue/tasks/fim.rb +60 -0
- data/lib/glue/tasks/findsecbugs.rb +90 -0
- data/lib/glue/tasks/npm.rb +58 -0
- data/lib/glue/tasks/nsp.rb +65 -0
- data/lib/glue/tasks/owasp-dep-check.rb +117 -0
- data/lib/glue/tasks/patterns.json +394 -0
- data/lib/glue/tasks/pmd.rb +63 -0
- data/lib/glue/tasks/retirejs.rb +107 -0
- data/lib/glue/tasks/scanjs-eslintrc +106 -0
- data/lib/glue/tasks/scanjs.rb +31 -0
- data/lib/glue/tasks/sfl.rb +67 -0
- data/lib/glue/tasks/snyk.rb +81 -0
- data/lib/glue/tasks/test.rb +47 -0
- data/lib/glue/tasks/zap.rb +99 -0
- data/lib/glue/tracker.rb +47 -0
- data/lib/glue/util.rb +36 -0
- data/lib/glue/version.rb +3 -0
- metadata +294 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
class Glue::PMD < Glue::BaseTask
|
7
|
+
|
8
|
+
Glue::Tasks.add self
|
9
|
+
include Glue::Util
|
10
|
+
|
11
|
+
def initialize(trigger, tracker)
|
12
|
+
super(trigger, tracker)
|
13
|
+
@name = "PMD"
|
14
|
+
@description = "PMD Source Code Analyzer"
|
15
|
+
@stage = :code
|
16
|
+
@labels << "code"
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
@tracker.options[:pmd_checks] ||= "java-basic,java-sunsecure"
|
21
|
+
Dir.chdir @tracker.options[:pmd_path] do
|
22
|
+
@results = Nokogiri::XML(runsystem(true,'bin/run.sh', 'pmd', '-d', "#{@trigger.path}", '-f', 'xml', '-R', "#{@tracker.options[:pmd_checks]}")).xpath('//file')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def analyze
|
27
|
+
begin
|
28
|
+
@results.each do |result|
|
29
|
+
attributes = result.at_xpath('violation').attributes
|
30
|
+
description = result.children.children.to_s.strip
|
31
|
+
detail = "Ruleset: #{attributes['ruleset']}"
|
32
|
+
source = {:scanner => @name, :file => result.attributes['name'].to_s.split(Pathname.new(@trigger.path).cleanpath.to_s)[1][1..-1], :line => attributes['beginline'].to_s, :code => "package: #{attributes['package'].to_s}\nclass: #{attributes['class'].to_s}\nmethod: #{attributes['method'].to_s}" }
|
33
|
+
case attributes['priority'].value.to_i
|
34
|
+
when 3
|
35
|
+
sev = 1
|
36
|
+
when 2
|
37
|
+
sev = 2
|
38
|
+
when 1
|
39
|
+
sev = 3
|
40
|
+
else
|
41
|
+
sev = 0
|
42
|
+
end
|
43
|
+
fprint = fingerprint("#{description}#{detail}#{source}#{sev}")
|
44
|
+
|
45
|
+
report description, detail, source, sev, fprint
|
46
|
+
end
|
47
|
+
rescue Exception => e
|
48
|
+
Glue.warn e.message
|
49
|
+
Glue.warn e.backtrace
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def supported?
|
54
|
+
unless @tracker.options.has_key?(:pmd_path) and File.exist?("#{@tracker.options[:pmd_path]}/bin/run.sh")
|
55
|
+
Glue.notify "#{@tracker.options[:pmd_path]}"
|
56
|
+
Glue.notify "Install PMD from: https://pmd.github.io/"
|
57
|
+
return false
|
58
|
+
else
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'json'
|
3
|
+
require 'glue/util'
|
4
|
+
require 'jsonpath'
|
5
|
+
require 'pathname'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
class Glue::RetireJS < Glue::BaseTask
|
9
|
+
|
10
|
+
Glue::Tasks.add self
|
11
|
+
include Glue::Util
|
12
|
+
|
13
|
+
def initialize(trigger, tracker)
|
14
|
+
super(trigger, tracker)
|
15
|
+
@name = "RetireJS"
|
16
|
+
@description = "Dependency analysis for JavaScript"
|
17
|
+
@stage = :code
|
18
|
+
@labels << "code" << "javascript"
|
19
|
+
@results = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
exclude_dirs = ['node_modules','bower_components']
|
24
|
+
exclude_dirs = exclude_dirs.concat(@tracker.options[:exclude_dirs]).uniq if @tracker.options[:exclude_dirs]
|
25
|
+
directories_with?('package.json', exclude_dirs).each do |dir|
|
26
|
+
Glue.notify "#{@name} scanning: #{dir}"
|
27
|
+
@results << runsystem(false, 'retire', '-c', '--outputformat', 'json', '--path', "#{dir}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def analyze
|
32
|
+
begin
|
33
|
+
@results.each do |result|
|
34
|
+
parsed_json = JSON.parse(result)
|
35
|
+
vulnerabilities = parse_retire_json(parsed_json) if parsed_json
|
36
|
+
|
37
|
+
vulnerabilities.each do |vuln|
|
38
|
+
report "Package #{vuln[:package]} has known security issues", vuln[:detail], vuln[:source], vuln[:severity], fingerprint("#{vuln[:package]}#{vuln[:source]}#{vuln[:severity]}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue JSON::ParserError => e
|
42
|
+
Glue.debug e.message
|
43
|
+
rescue Exception => e
|
44
|
+
Glue.warn e.message
|
45
|
+
Glue.warn e.backtrace
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_retire_json result
|
50
|
+
Glue.debug "Retire JSON Raw Result: #{result}"
|
51
|
+
vulnerabilities = []
|
52
|
+
# This is very ugly, but so is the json retire.js spits out
|
53
|
+
# Loop through each component/version combo and pull all results for it
|
54
|
+
JsonPath.on(result, '$..component').uniq.each do |comp|
|
55
|
+
JsonPath.on(result, "$..results[?(@.component == \'#{comp}\')].version").uniq.each do |version|
|
56
|
+
vuln_hash = {}
|
57
|
+
vuln_hash[:package] = "#{comp}-#{version}"
|
58
|
+
|
59
|
+
version_results = JsonPath.on(result, "$..results[?(@.component == \'#{comp}\')]").select { |r| r['version'] == version }.uniq
|
60
|
+
|
61
|
+
# If we see the parent-->component relationship, dig through the dependency tree to try and make a dep map
|
62
|
+
deps = []
|
63
|
+
obj = version_results[0]
|
64
|
+
while !obj['parent'].nil?
|
65
|
+
deps << obj['parent']['component']
|
66
|
+
obj = obj['parent']
|
67
|
+
end
|
68
|
+
if deps.length > 0
|
69
|
+
vuln_hash[:source] = { :scanner => @name, :file => "#{deps.reverse.join('->')}->#{comp}-#{version}", :line => nil, :code => nil }
|
70
|
+
end
|
71
|
+
|
72
|
+
vuln_hash[:severity] = 'unknown'
|
73
|
+
# pull detail/severity
|
74
|
+
version_results.each do |version_result|
|
75
|
+
JsonPath.on(version_result, '$..vulnerabilities').uniq.each do |vuln|
|
76
|
+
vuln_hash[:severity] = severity(vuln[0]['severity'])
|
77
|
+
vuln_hash[:detail] = vuln[0]['info'].join('\n')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
vulnerabilities << vuln_hash
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Loop through the separately reported 'file' findings so we can tag the source (no dep map here)
|
86
|
+
result.select { |r| !r['file'].nil? }.each do |file_result|
|
87
|
+
JsonPath.on(file_result, '$..component').uniq.each do |comp|
|
88
|
+
JsonPath.on(file_result, "$..results[?(@.component == \'#{comp}\')].version").uniq.each do |version|
|
89
|
+
source_path = relative_path(file_result['file'], @trigger.path)
|
90
|
+
vulnerabilities.select { |v| v[:package] == "#{comp}-#{version}" }.first[:source] = { :scanner => @name, :file => source_path.to_s, :line => nil, :code => nil }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return vulnerabilities
|
95
|
+
end
|
96
|
+
|
97
|
+
def supported?
|
98
|
+
supported=runsystem(false, "retire", "--help")
|
99
|
+
if supported =~ /command not found/
|
100
|
+
Glue.notify "Install RetireJS"
|
101
|
+
return false
|
102
|
+
else
|
103
|
+
return true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
{ "ecmaFeatures" : {
|
2
|
+
"modules" : true
|
3
|
+
},
|
4
|
+
"env" : {
|
5
|
+
"browser" : true,
|
6
|
+
"es6" : true /** all es6 features except modules */
|
7
|
+
},
|
8
|
+
"plugins" : [
|
9
|
+
"scanjs-rules",
|
10
|
+
"no-unsafe-innerhtml"
|
11
|
+
],
|
12
|
+
"rules" : {
|
13
|
+
/** useful rules from eslint **/
|
14
|
+
|
15
|
+
/** no-unsafe-innerhtml rule **/
|
16
|
+
"no-unsafe-innerhtml/no-unsafe-innerhtml" : 2,
|
17
|
+
|
18
|
+
/** ScanJS rules **/
|
19
|
+
"scanjs-rules/assign_to_hostname" : 1,
|
20
|
+
"scanjs-rules/assign_to_href" : 1,
|
21
|
+
"scanjs-rules/assign_to_location" : 1,
|
22
|
+
"scanjs-rules/assign_to_mozAudioChannel" : 1,
|
23
|
+
"scanjs-rules/assign_to_mozAudioChannelType" : 1,
|
24
|
+
"scanjs-rules/assign_to_onmessage" : 1,
|
25
|
+
"scanjs-rules/assign_to_pathname" : 1,
|
26
|
+
"scanjs-rules/assign_to_protocol" : 1,
|
27
|
+
"scanjs-rules/assign_to_search" : 1,
|
28
|
+
"scanjs-rules/assign_to_src" : 1,
|
29
|
+
"scanjs-rules/call_Function" : 1,
|
30
|
+
"scanjs-rules/call_addEventListener" : 1,
|
31
|
+
"scanjs-rules/call_addEventListener_cellbroadcastmsgchanged" : 1,
|
32
|
+
"scanjs-rules/call_addEventListener_deviceproximity" : 1,
|
33
|
+
"scanjs-rules/call_addEventListener_message" : 1,
|
34
|
+
"scanjs-rules/call_addEventListener_moznetworkdownload" : 1,
|
35
|
+
"scanjs-rules/call_addEventListener_moznetworkupload" : 1,
|
36
|
+
"scanjs-rules/call_connect" : 1,
|
37
|
+
"scanjs-rules/call_eval" : 1,
|
38
|
+
"scanjs-rules/call_execScript" : 1,
|
39
|
+
"scanjs-rules/call_generateCRMFRequest" : 1,
|
40
|
+
"scanjs-rules/call_getDeviceStorage_apps" : 1,
|
41
|
+
"scanjs-rules/call_getDeviceStorage_crashes" : 1,
|
42
|
+
"scanjs-rules/call_getDeviceStorage_music" : 1,
|
43
|
+
"scanjs-rules/call_getDeviceStorage_pictures" : 1,
|
44
|
+
"scanjs-rules/call_getDeviceStorage_sdcard" : 1,
|
45
|
+
"scanjs-rules/call_getDeviceStorage_videos" : 1,
|
46
|
+
"scanjs-rules/call_hide" : 1,
|
47
|
+
"scanjs-rules/call_mozSetMessageHandler" : 1,
|
48
|
+
"scanjs-rules/call_mozSetMessageHandler_activity" : 1,
|
49
|
+
"scanjs-rules/call_mozSetMessageHandler_wappush_received" : 1,
|
50
|
+
"scanjs-rules/call_open_attention" : 1,
|
51
|
+
"scanjs-rules/call_open_remote=true" : 1,
|
52
|
+
"scanjs-rules/call_parseFromString" : 1,
|
53
|
+
"scanjs-rules/call_setAttribute_mozapp" : 1,
|
54
|
+
"scanjs-rules/call_setAttribute_mozbrowser" : 1,
|
55
|
+
"scanjs-rules/call_setImmediate" : 1,
|
56
|
+
"scanjs-rules/call_setInterval" : 1,
|
57
|
+
"scanjs-rules/call_setMessageHandler_connect" : 1,
|
58
|
+
"scanjs-rules/call_setTimeout" : 1,
|
59
|
+
"scanjs-rules/call_write" : 1,
|
60
|
+
"scanjs-rules/call_writeln" : 1,
|
61
|
+
"scanjs-rules/identifier_indexedDB" : 1,
|
62
|
+
"scanjs-rules/identifier_localStorage" : 1,
|
63
|
+
"scanjs-rules/identifier_sessionStorage" : 1,
|
64
|
+
"scanjs-rules/new_Function" : 1,
|
65
|
+
"scanjs-rules/new_MozActivity" : 1,
|
66
|
+
"scanjs-rules/new_MozSpeakerManager" : 1,
|
67
|
+
"scanjs-rules/new_Notification" : 1,
|
68
|
+
"scanjs-rules/object_mozSystem" : 1,
|
69
|
+
"scanjs-rules/property_addIdleObserver" : 1,
|
70
|
+
"scanjs-rules/property_createContextualFragment" : 1,
|
71
|
+
"scanjs-rules/property_geolocation" : 1,
|
72
|
+
"scanjs-rules/property_getDataStores" : 1,
|
73
|
+
"scanjs-rules/property_getDeviceStorage" : 1,
|
74
|
+
"scanjs-rules/property_getUserMedia" : 1,
|
75
|
+
"scanjs-rules/property_indexedDB" : 1,
|
76
|
+
"scanjs-rules/property_lastKnownHomeNetwork" : 1,
|
77
|
+
"scanjs-rules/property_lastKnownNetwork" : 1,
|
78
|
+
"scanjs-rules/property_localStorage" : 1,
|
79
|
+
"scanjs-rules/property_mgmt" : 1,
|
80
|
+
"scanjs-rules/property_mozAlarms" : 1,
|
81
|
+
"scanjs-rules/property_mozBluetooth" : 1,
|
82
|
+
"scanjs-rules/property_mozCameras" : 1,
|
83
|
+
"scanjs-rules/property_mozCellBroadcast" : 1,
|
84
|
+
"scanjs-rules/property_mozContacts" : 1,
|
85
|
+
"scanjs-rules/property_mozDownloadManager" : 1,
|
86
|
+
"scanjs-rules/property_mozFMRadio" : 1,
|
87
|
+
"scanjs-rules/property_mozInputMethod" : 1,
|
88
|
+
"scanjs-rules/property_mozKeyboard" : 1,
|
89
|
+
"scanjs-rules/property_mozMobileConnection" : 1,
|
90
|
+
"scanjs-rules/property_mozMobileConnections" : 1,
|
91
|
+
"scanjs-rules/property_mozMobileMessage" : 1,
|
92
|
+
"scanjs-rules/property_mozNetworkStats" : 1,
|
93
|
+
"scanjs-rules/property_mozNfc" : 1,
|
94
|
+
"scanjs-rules/property_mozNotification" : 1,
|
95
|
+
"scanjs-rules/property_mozPermissionSettings" : 1,
|
96
|
+
"scanjs-rules/property_mozPhoneNumberService" : 1,
|
97
|
+
"scanjs-rules/property_mozPower" : 1,
|
98
|
+
"scanjs-rules/property_mozSettings" : 1,
|
99
|
+
"scanjs-rules/property_mozTCPSocket" : 1,
|
100
|
+
"scanjs-rules/property_mozTelephony" : 1,
|
101
|
+
"scanjs-rules/property_mozTime" : 1,
|
102
|
+
"scanjs-rules/property_mozVoicemail" : 1,
|
103
|
+
"scanjs-rules/property_mozWifiManager" : 1,
|
104
|
+
"scanjs-rules/property_sessionStorage" : 1
|
105
|
+
}
|
106
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
|
3
|
+
class Glue::ScanJS < Glue::BaseTask
|
4
|
+
|
5
|
+
# WIP
|
6
|
+
# Glue::Tasks.add self
|
7
|
+
|
8
|
+
def initialize(trigger, tracker)
|
9
|
+
super(trigger)
|
10
|
+
@name = "ScanJS"
|
11
|
+
@description = "Source analysis for JavaScript"
|
12
|
+
@stage = :code
|
13
|
+
@labels << "code" << "javascript"
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
Glue.notify "#{@name}"
|
18
|
+
rootpath = @trigger.path
|
19
|
+
@result=`scanner.js -t "#{rootpath}"`
|
20
|
+
end
|
21
|
+
|
22
|
+
def analyze
|
23
|
+
puts @result
|
24
|
+
end
|
25
|
+
|
26
|
+
def supported?
|
27
|
+
# In future, verify tool is available.
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'json'
|
3
|
+
require 'glue/util'
|
4
|
+
require 'find'
|
5
|
+
|
6
|
+
class Glue::SFL < Glue::BaseTask
|
7
|
+
|
8
|
+
Glue::Tasks.add self
|
9
|
+
include Glue::Util
|
10
|
+
|
11
|
+
def initialize(trigger, tracker)
|
12
|
+
super(trigger,tracker)
|
13
|
+
@name = "SFL"
|
14
|
+
@description = "Sentive Files Lookup"
|
15
|
+
@stage = :code
|
16
|
+
@labels << "code"
|
17
|
+
# Glue.debug "initialized SFL"
|
18
|
+
@patterns = read_patterns_file!
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
# Glue.notify "#{@name}"
|
23
|
+
@files = Find.find(@trigger.path)
|
24
|
+
Glue.debug "Found #{@files.count} files"
|
25
|
+
end
|
26
|
+
|
27
|
+
def analyze
|
28
|
+
begin
|
29
|
+
@files.each do |file|
|
30
|
+
@patterns.each do |pattern|
|
31
|
+
case pattern['part']
|
32
|
+
when 'filename'
|
33
|
+
if pattern_matched?(File.basename(file), pattern)
|
34
|
+
report pattern['caption'], pattern['description'], @name + ":" + file, 'unknown', 'TBD'
|
35
|
+
end
|
36
|
+
when 'extension'
|
37
|
+
if pattern_matched?(File.extname(file), pattern)
|
38
|
+
report pattern['caption'], pattern['description'], @name + ":" + file, 'unknown', 'TBD'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
rescue Exception => e
|
44
|
+
Glue.warn e.message
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def supported?
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def pattern_matched?(txt, pattrn)
|
53
|
+
case pattrn['type']
|
54
|
+
when 'match'
|
55
|
+
return txt == pattrn['pattern']
|
56
|
+
when 'regex'
|
57
|
+
regex = Regexp.new(pattrn['pattern'], Regexp::IGNORECASE)
|
58
|
+
return !regex.match(txt).nil?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def read_patterns_file!
|
63
|
+
JSON.parse(File.read("#{File.dirname(__FILE__)}/patterns.json"))
|
64
|
+
rescue JSON::ParserError => e
|
65
|
+
Glue.warn "Cannot parse pattern file: #{e.message}"
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
require 'redcarpet'
|
4
|
+
|
5
|
+
class Glue::Snyk < Glue::BaseTask
|
6
|
+
|
7
|
+
Glue::Tasks.add self
|
8
|
+
include Glue::Util
|
9
|
+
|
10
|
+
def initialize(trigger, tracker)
|
11
|
+
super(trigger, tracker)
|
12
|
+
@name = "Snyk"
|
13
|
+
@description = "Snyk.io JS dependency checker"
|
14
|
+
@stage = :code
|
15
|
+
@labels << "code" << "javascript"
|
16
|
+
@results = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
exclude_dirs = ['node_modules','bower_components']
|
21
|
+
exclude_dirs = exclude_dirs.concat(@tracker.options[:exclude_dirs]).uniq if @tracker.options[:exclude_dirs]
|
22
|
+
directories_with?('package.json', exclude_dirs).each do |dir|
|
23
|
+
Glue.notify "#{@name} scanning: #{dir}"
|
24
|
+
Dir.chdir(dir) do
|
25
|
+
@results << JSON.parse(runsystem(true, "snyk", "test", "--json"))["vulnerabilities"]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def analyze
|
31
|
+
begin
|
32
|
+
markdown = Redcarpet::Markdown.new Redcarpet::Render::HTML.new(link_attributes: {target: "_blank"}), autolink: true, tables: true
|
33
|
+
|
34
|
+
@results.each do |dir_result|
|
35
|
+
# We build a single finding for each uniq result ID, adding the unique info (upgrade path and files) as a list
|
36
|
+
dir_result.uniq {|r| r['id']}.each do |result|
|
37
|
+
description = "#{result['name']}@#{result['version']} - #{result['title']}"
|
38
|
+
|
39
|
+
# Use Redcarpet to render the Markdown details to something pretty for web display
|
40
|
+
detail = markdown.render(result['description']).gsub('h2>','strong>').gsub('h3>', 'strong>')
|
41
|
+
upgrade_paths = [ "Upgrade Path:\n" ]
|
42
|
+
files = []
|
43
|
+
|
44
|
+
# Pull the list of files and upgrade paths from all results matching this ID
|
45
|
+
# This uses the same form as the retirejs task so it all looks nice together
|
46
|
+
dir_result.select{|r| r['id'] == result['id']}.each do |res|
|
47
|
+
res['upgradePath'].each_with_index do |upgrade, i|
|
48
|
+
upgrade_paths << "#{res['from'][i]} -> #{upgrade}"
|
49
|
+
end
|
50
|
+
files << res['from'].join('->')
|
51
|
+
end
|
52
|
+
|
53
|
+
source = {
|
54
|
+
:scanner => @name,
|
55
|
+
:file => files.join('<br>'),
|
56
|
+
:line => nil,
|
57
|
+
:code => upgrade_paths.uniq.join("\n"),
|
58
|
+
}
|
59
|
+
sev = severity(result['severity'])
|
60
|
+
fprint = fingerprint("#{description}#{detail}#{source}#{sev}")
|
61
|
+
|
62
|
+
report description, detail, source, sev, fprint
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rescue Exception => e
|
66
|
+
Glue.warn e.message
|
67
|
+
Glue.warn e.backtrace
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def supported?
|
72
|
+
supported = find_executable0('snyk')
|
73
|
+
unless supported
|
74
|
+
Glue.notify "Install Snyk: 'npm install -g snyk'"
|
75
|
+
return false
|
76
|
+
else
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|