owasp-glue 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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