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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +27 -0
  3. data/FEATURES +19 -0
  4. data/README.md +117 -0
  5. data/bin/glue +67 -0
  6. data/lib/glue.rb +317 -0
  7. data/lib/glue/event.rb +14 -0
  8. data/lib/glue/filters.rb +41 -0
  9. data/lib/glue/filters/base_filter.rb +19 -0
  10. data/lib/glue/filters/jira_one_time_filter.rb +57 -0
  11. data/lib/glue/filters/remove_all_filter.rb +16 -0
  12. data/lib/glue/filters/zap_consdensing_filter.rb +76 -0
  13. data/lib/glue/finding.rb +52 -0
  14. data/lib/glue/mounters.rb +55 -0
  15. data/lib/glue/mounters/base_mounter.rb +31 -0
  16. data/lib/glue/mounters/docker_mounter.rb +44 -0
  17. data/lib/glue/mounters/filesystem_mounter.rb +20 -0
  18. data/lib/glue/mounters/git_mounter.rb +52 -0
  19. data/lib/glue/mounters/iso_mounter.rb +42 -0
  20. data/lib/glue/mounters/url_mounter.rb +28 -0
  21. data/lib/glue/options.rb +269 -0
  22. data/lib/glue/reporters.rb +50 -0
  23. data/lib/glue/reporters/base_reporter.rb +21 -0
  24. data/lib/glue/reporters/csv_reporter.rb +19 -0
  25. data/lib/glue/reporters/jira_reporter.rb +59 -0
  26. data/lib/glue/reporters/json_reporter.rb +20 -0
  27. data/lib/glue/reporters/text_reporter.rb +19 -0
  28. data/lib/glue/scanner.rb +28 -0
  29. data/lib/glue/tasks.rb +124 -0
  30. data/lib/glue/tasks/av.rb +42 -0
  31. data/lib/glue/tasks/base_task.rb +80 -0
  32. data/lib/glue/tasks/brakeman.rb +58 -0
  33. data/lib/glue/tasks/bundle-audit.rb +95 -0
  34. data/lib/glue/tasks/checkmarx.rb +60 -0
  35. data/lib/glue/tasks/dawnscanner.rb +55 -0
  36. data/lib/glue/tasks/eslint.rb +69 -0
  37. data/lib/glue/tasks/fim.rb +60 -0
  38. data/lib/glue/tasks/findsecbugs.rb +90 -0
  39. data/lib/glue/tasks/npm.rb +58 -0
  40. data/lib/glue/tasks/nsp.rb +65 -0
  41. data/lib/glue/tasks/owasp-dep-check.rb +117 -0
  42. data/lib/glue/tasks/patterns.json +394 -0
  43. data/lib/glue/tasks/pmd.rb +63 -0
  44. data/lib/glue/tasks/retirejs.rb +107 -0
  45. data/lib/glue/tasks/scanjs-eslintrc +106 -0
  46. data/lib/glue/tasks/scanjs.rb +31 -0
  47. data/lib/glue/tasks/sfl.rb +67 -0
  48. data/lib/glue/tasks/snyk.rb +81 -0
  49. data/lib/glue/tasks/test.rb +47 -0
  50. data/lib/glue/tasks/zap.rb +99 -0
  51. data/lib/glue/tracker.rb +47 -0
  52. data/lib/glue/util.rb +36 -0
  53. data/lib/glue/version.rb +3 -0
  54. metadata +294 -0
@@ -0,0 +1,20 @@
1
+ require 'glue/finding'
2
+ require 'glue/reporters/base_reporter'
3
+
4
+ require 'json'
5
+
6
+ class Glue::JSONReporter < Glue::BaseReporter
7
+
8
+ Glue::Reporters.add self
9
+
10
+ attr_accessor :name, :format
11
+
12
+ def initialize()
13
+ @name = "JSONReporter"
14
+ @format = :to_json
15
+ end
16
+
17
+ def out(finding)
18
+ finding.to_json
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ require 'glue/finding'
2
+ require 'glue/reporters/base_reporter'
3
+
4
+ class Glue::TextReporter < Glue::BaseReporter
5
+
6
+ Glue::Reporters.add self
7
+
8
+ attr_accessor :name, :format
9
+
10
+ def initialize()
11
+ @name = "TextReporter"
12
+ @format = :to_s
13
+ end
14
+
15
+ def out(finding)
16
+ finding.to_string << "\n"
17
+ end
18
+
19
+ end
@@ -0,0 +1,28 @@
1
+ require 'glue/event'
2
+ require 'glue/tracker'
3
+ require 'glue/tasks'
4
+
5
+ class Glue::Scanner
6
+ attr_reader :tracker
7
+ attr_reader :mounter
8
+
9
+ #Pass in path to the root of the Rails application
10
+ def initialize
11
+ @stage = :wait
12
+ @stages = [ :wait, :mount, :file, :code, :live, :done]
13
+ end
14
+
15
+ #Process everything in the Rails application
16
+ def process target, tracker
17
+ @stages.each do |stage|
18
+ Glue.notify "Running tasks in stage: #{stage}"
19
+ @stage = stage
20
+ begin
21
+ Glue::Tasks.run_tasks(target, stage, tracker)
22
+ rescue Exception => e
23
+ Glue.warn e.message
24
+ raise e
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,124 @@
1
+ require 'thread'
2
+
3
+ #Collects up results from running different tasks.
4
+ #
5
+ #tasks can be added with +task.add(task_class)+
6
+ #
7
+ #All .rb files in tasks/ will be loaded.
8
+ class Glue::Tasks
9
+ @tasks = []
10
+ @optional_tasks = []
11
+
12
+ attr_reader :tasks_run
13
+
14
+ #Add a task. This will call +_klass_.new+ when running tests
15
+ def self.add klass
16
+ @tasks << klass unless @tasks.include? klass
17
+ end
18
+
19
+ #Add an optional task
20
+ def self.add_optional klass
21
+ @optional_tasks << klass unless @tasks.include? klass
22
+ end
23
+
24
+ def self.tasks
25
+ @tasks + @optional_tasks
26
+ end
27
+
28
+ def self.optional_tasks
29
+ @optional_tasks
30
+ end
31
+
32
+ def self.initialize_tasks task_directory = ""
33
+ #Load all files in task_directory
34
+ Dir.glob(File.join(task_directory, "*.rb")).sort.each do |f|
35
+ require f
36
+ end
37
+ end
38
+
39
+ #No need to use this directly.
40
+ def initialize options = { }
41
+ @warnings = []
42
+ @tasks_run = []
43
+ end
44
+
45
+ #Add Warning to list of warnings to report.
46
+ def add_warning warning
47
+ @warnings << warning
48
+ end
49
+
50
+ #Run all the tasks on the given Tracker.
51
+ #Returns a new instance of tasks with the results.
52
+ def self.run_tasks(target, stage, tracker)
53
+ task_runner = self.new
54
+
55
+ trigger = Glue::Event.new(tracker.options[:appname])
56
+ trigger.path = target
57
+
58
+ self.tasks_to_run(tracker).each do |c|
59
+ task_name = get_task_name c
60
+
61
+ #Run or don't run task based on options
62
+ #Now case-insensitive specifiers: nodesecurityproject = Glue::NodeSecurityProject
63
+
64
+ if tracker.options[:skip_tasks]
65
+ skip_tasks = tracker.options[:skip_tasks].map {|task| task.downcase}
66
+ end
67
+ if (tracker.options[:run_tasks])
68
+ run_tasks = tracker.options[:run_tasks].map {|task| task.downcase}
69
+ end
70
+
71
+ unless skip_tasks.include? task_name.downcase or
72
+ (run_tasks and not run_tasks.include? task_name.downcase)
73
+
74
+ task = c.new(trigger, tracker)
75
+ begin
76
+ if task.supported? and task.stage == stage
77
+ if task.labels.intersect? tracker.options[:labels] or # Only run tasks with labels
78
+ ( run_tasks and run_tasks.include? task_name.downcase ) # or that are explicitly requested.
79
+ Glue.notify "#{stage} - #{task_name} - #{task.labels}"
80
+ task.run
81
+ task.analyze
82
+ task.findings.each do | finding |
83
+ tracker.report finding
84
+ end
85
+ end
86
+ end
87
+ rescue => e
88
+ Glue.notify e.message
89
+ tracker.error e
90
+ end
91
+
92
+ task.warnings.each do |w|
93
+ task_runner.add_warning w
94
+ end
95
+
96
+ #Maintain list of which tasks were run
97
+ #mainly for reporting purposes
98
+ task_runner.tasks_run << task_name[5..-1]
99
+ end
100
+ end
101
+
102
+ task_runner
103
+ end
104
+
105
+
106
+ private
107
+
108
+ def self.get_task_name task_class
109
+ task_class.to_s.split("::").last
110
+ end
111
+
112
+ def self.tasks_to_run tracker
113
+ if tracker.options[:run_all_tasks] or tracker.options[:run_tasks]
114
+ @tasks + @optional_tasks
115
+ else
116
+ @tasks
117
+ end
118
+ end
119
+ end
120
+
121
+ #Load all files in tasks/ directory
122
+ Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/tasks/*.rb").sort.each do |f|
123
+ require f.match(/(glue\/tasks\/.*)\.rb$/)[0]
124
+ end
@@ -0,0 +1,42 @@
1
+ # https://gist.github.com/paulspringett/8802240
2
+
3
+ require 'glue/tasks/base_task'
4
+
5
+ class Glue::AV < Glue::BaseTask
6
+
7
+ Glue::Tasks.add self
8
+
9
+ def initialize(trigger, tracker)
10
+ super(trigger,tracker)
11
+ @name = "AV"
12
+ @description = "Test for virus/malware"
13
+ @stage = :file
14
+ @labels << "filesystem"
15
+ end
16
+
17
+ def run
18
+ # Update AV
19
+ `freshclam`
20
+ # Run AV
21
+ # TODO: Circle back and use runsystem.
22
+ Glue.notify "Malware/Virus Check"
23
+ rootpath = @trigger.path
24
+ @result=`clamscan --no-summary -i -r "#{rootpath}"`
25
+ end
26
+
27
+ def analyze
28
+ list = @result.split(/\n/)
29
+ list.each do |v|
30
+ # v.slice! installdir
31
+ Glue.notify v
32
+ report "Malicious file identified.", v, @name, :medium
33
+ end
34
+ end
35
+
36
+ def supported?
37
+ # TODO verify.
38
+ # In future, verify tool is available.
39
+ return true
40
+ end
41
+
42
+ end
@@ -0,0 +1,80 @@
1
+ require 'glue/finding'
2
+ require 'set'
3
+ require 'digest'
4
+
5
+ class Glue::BaseTask
6
+ attr_reader :findings, :warnings, :trigger, :labels
7
+ attr_accessor :name
8
+ attr_accessor :description
9
+ attr_accessor :stage
10
+ attr_accessor :appname
11
+
12
+ def initialize(trigger, tracker)
13
+ @findings = []
14
+ @warnings = []
15
+ @labels = Set.new
16
+ @trigger = trigger
17
+ @tracker = tracker
18
+ @severity_filter = {
19
+ :low => ['low','weak'],
20
+ :medium => ['medium','med','average'],
21
+ :high => ['high','severe','critical']
22
+ }
23
+ end
24
+
25
+ def report description, detail, source, severity, fingerprint
26
+ finding = Glue::Finding.new( @trigger.appname, description, detail, source, severity, fingerprint )
27
+ @findings << finding
28
+ end
29
+
30
+ def warn warning
31
+ @warnings << warning
32
+ end
33
+
34
+ def name
35
+ @name
36
+ end
37
+
38
+ def description
39
+ @description
40
+ end
41
+
42
+ def stage
43
+ @stage
44
+ end
45
+
46
+ def directories_with? file, exclude_dirs = []
47
+ exclude_dirs = @tracker.options[:exclude_dirs] if exclude_dirs == [] and @tracker.options[:exclude_dirs]
48
+ results = []
49
+
50
+ Find.find(@trigger.path) do |path|
51
+ if FileTest.directory? path
52
+ Find.prune if exclude_dirs.include? File.basename(path) or exclude_dirs.include? File.basename(path) + '/'
53
+ next
54
+ end
55
+
56
+ Find.prune unless File.basename(path) == file
57
+
58
+ results << File.dirname(path)
59
+ end
60
+ return results
61
+ end
62
+
63
+ def run
64
+ end
65
+
66
+ def analyze
67
+ end
68
+
69
+ def supported?
70
+ end
71
+
72
+ def severity sev
73
+ sev = '' if sev.nil?
74
+ return 1 if @severity_filter[:low].include?(sev.strip.chomp.downcase)
75
+ return 2 if @severity_filter[:medium].include?(sev.strip.chomp.downcase)
76
+ return 3 if @severity_filter[:high].include?(sev.strip.chomp.downcase)
77
+ return 0
78
+ end
79
+
80
+ end
@@ -0,0 +1,58 @@
1
+ require 'glue/tasks/base_task'
2
+ require 'json'
3
+ require 'glue/util'
4
+ require 'pathname'
5
+
6
+ class Glue::Brakeman < Glue::BaseTask
7
+
8
+ Glue::Tasks.add self
9
+ include Glue::Util
10
+
11
+ def initialize(trigger, tracker)
12
+ super(trigger, tracker)
13
+ @name = "Brakeman"
14
+ @description = "Source analysis for Ruby"
15
+ @stage = :code
16
+ @labels << "code" << "ruby" << "rails"
17
+ end
18
+
19
+ def run
20
+ rootpath = @trigger.path
21
+ @result=runsystem(true, "brakeman", "-A", "-q", "-f", "json", "#{rootpath}")
22
+ end
23
+
24
+ def analyze
25
+ # puts @result
26
+ begin
27
+ parsed = JSON.parse(@result)
28
+ parsed["warnings"].each do |warning|
29
+ file = relative_path(warning['file'], @trigger.path)
30
+
31
+ detail = "#{warning['message']}\n#{warning['link']}"
32
+ if ! warning['line']
33
+ warning['line'] = "0"
34
+ end
35
+ if ! warning['code']
36
+ warning['code'] = ""
37
+ end
38
+ source = { :scanner => @name, :file => file, :line => warning['line'], :code => warning['code'].lstrip }
39
+
40
+ report warning["warning_type"], detail, source, severity(warning["confidence"]), fingerprint("#{warning['message']}#{warning['link']}#{severity(warning["confidence"])}#{source}")
41
+ end
42
+ rescue Exception => e
43
+ Glue.warn e.message
44
+ Glue.warn e.backtrace
45
+ end
46
+ end
47
+
48
+ def supported?
49
+ supported=runsystem(true, "brakeman", "-v")
50
+ if supported =~ /command not found/
51
+ Glue.notify "Run: gem install brakeman"
52
+ return false
53
+ else
54
+ return true
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,95 @@
1
+ require 'glue/tasks/base_task'
2
+ require 'json'
3
+ require 'glue/util'
4
+ require 'digest'
5
+
6
+ class Glue::BundleAudit < Glue::BaseTask
7
+ Glue::Tasks.add self
8
+ include Glue::Util
9
+
10
+ def initialize(trigger, tracker)
11
+ super(trigger, tracker)
12
+ @name = "BundleAudit"
13
+ @description = "Dependency Checker analysis for Ruby"
14
+ @stage = :code
15
+ @labels << "code" << "ruby"
16
+ @results = {}
17
+ end
18
+
19
+ def run
20
+ directories_with?('Gemfile.lock').each do |dir|
21
+ Glue.notify "#{@name} scanning: #{dir}"
22
+ Dir.chdir(dir) do
23
+ @results[dir] = runsystem(true, "bundle-audit", "check")
24
+ end
25
+ end
26
+ end
27
+
28
+ def analyze
29
+ # puts @result
30
+ begin
31
+ get_warnings
32
+ rescue Exception => e
33
+ Glue.warn e.message
34
+ Glue.notify "Appears not to be a project with Gemfile.lock or there was another problem ... bundle-audit skipped."
35
+ end
36
+ end
37
+
38
+ def supported?
39
+ supported=runsystem(false, "bundle-audit", "update")
40
+ if supported =~ /command not found/
41
+ Glue.notify "Run: gem install bundler-audit"
42
+ return false
43
+ else
44
+ return true
45
+ end
46
+ end
47
+
48
+ private
49
+ def get_warnings
50
+ @results.each do |dir, result|
51
+ detail, jem, source, sev, hash = '','',{},'',''
52
+ result.each_line do | line |
53
+
54
+ if /\S/ !~ line
55
+ # Signal section is over. Reset variables and report.
56
+ if detail != ''
57
+ report "Gem #{jem} has known security issues.", detail, source, sev, fingerprint(hash)
58
+ end
59
+
60
+ detail, jem, source, sev, hash = '','', {},'',''
61
+ end
62
+
63
+ name, value = line.chomp.split(':')
64
+ case name
65
+ when 'Name'
66
+ jem << value
67
+ hash << value
68
+ when 'Version'
69
+ jem << value
70
+ hash << value
71
+ when 'Advisory'
72
+ source = { :scanner => @name, :file => "#{relative_path(dir, @trigger.path)}/Gemfile.lock", :line => nil, :code => nil }
73
+ hash << value
74
+ when 'Criticality'
75
+ sev = severity(value)
76
+ hash << sev
77
+ when 'URL'
78
+ detail += line.chomp.split('URL:').last
79
+ when 'Title'
80
+ detail += ",#{value}"
81
+ when 'Solution'
82
+ detail += ": #{value}"
83
+ when 'Insecure Source URI found'
84
+ report "Insecure GEM Source", "#{line.chomp} - use git or https", {:scanner => @name, :file => 'Gemfile.lock', :line => nil, :code => nil}, severity('high'), fingerprint("bundlerauditgemsource#{line.chomp}")
85
+ else
86
+ if line =~ /\S/ and line !~ /Unpatched versions found/
87
+ Glue.debug "Not sure how to handle line: #{line}"
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+
95
+ end