owasp-pipeline 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +23 -0
  3. data/FEATURES +19 -0
  4. data/README.md +101 -0
  5. data/bin/pipeline +67 -0
  6. data/lib/pipeline.rb +301 -0
  7. data/lib/pipeline/event.rb +14 -0
  8. data/lib/pipeline/filters.rb +41 -0
  9. data/lib/pipeline/filters/base_filter.rb +19 -0
  10. data/lib/pipeline/filters/jira_one_time_filter.rb +57 -0
  11. data/lib/pipeline/filters/remove_all_filter.rb +16 -0
  12. data/lib/pipeline/finding.rb +52 -0
  13. data/lib/pipeline/mounters.rb +55 -0
  14. data/lib/pipeline/mounters/base_mounter.rb +31 -0
  15. data/lib/pipeline/mounters/docker_mounter.rb +44 -0
  16. data/lib/pipeline/mounters/filesystem_mounter.rb +25 -0
  17. data/lib/pipeline/mounters/git_mounter.rb +52 -0
  18. data/lib/pipeline/mounters/iso_mounter.rb +42 -0
  19. data/lib/pipeline/mounters/url_mounter.rb +28 -0
  20. data/lib/pipeline/options.rb +240 -0
  21. data/lib/pipeline/reporters.rb +50 -0
  22. data/lib/pipeline/reporters/base_reporter.rb +21 -0
  23. data/lib/pipeline/reporters/csv_reporter.rb +19 -0
  24. data/lib/pipeline/reporters/jira_reporter.rb +61 -0
  25. data/lib/pipeline/reporters/json_reporter.rb +20 -0
  26. data/lib/pipeline/reporters/text_reporter.rb +19 -0
  27. data/lib/pipeline/scanner.rb +28 -0
  28. data/lib/pipeline/tasks.rb +124 -0
  29. data/lib/pipeline/tasks/av.rb +43 -0
  30. data/lib/pipeline/tasks/base_task.rb +64 -0
  31. data/lib/pipeline/tasks/brakeman.rb +60 -0
  32. data/lib/pipeline/tasks/bundle-audit.rb +93 -0
  33. data/lib/pipeline/tasks/checkmarx.rb +62 -0
  34. data/lib/pipeline/tasks/eslint.rb +71 -0
  35. data/lib/pipeline/tasks/fim.rb +61 -0
  36. data/lib/pipeline/tasks/nsp.rb +59 -0
  37. data/lib/pipeline/tasks/owasp-dep-check.rb +120 -0
  38. data/lib/pipeline/tasks/patterns.json +394 -0
  39. data/lib/pipeline/tasks/retirejs.rb +106 -0
  40. data/lib/pipeline/tasks/scanjs-eslintrc +106 -0
  41. data/lib/pipeline/tasks/scanjs.rb +32 -0
  42. data/lib/pipeline/tasks/sfl.rb +67 -0
  43. data/lib/pipeline/tasks/test.rb +47 -0
  44. data/lib/pipeline/tasks/zap.rb +84 -0
  45. data/lib/pipeline/tracker.rb +47 -0
  46. data/lib/pipeline/util.rb +39 -0
  47. data/lib/pipeline/version.rb +3 -0
  48. data/lib/zapjson.json +0 -0
  49. metadata +205 -0
@@ -0,0 +1,28 @@
1
+ require 'pipeline/mounters/base_mounter'
2
+
3
+ class Pipeline::URLMounter < Pipeline::BaseMounter
4
+ Pipeline::Mounters.add self
5
+
6
+ def initialize trigger, options
7
+ super(trigger)
8
+ @options = options
9
+ @name = "URL"
10
+ @description = "Mount a url - typically for a live attack."
11
+ end
12
+
13
+ def mount target
14
+ return target
15
+ end
16
+
17
+ def supports? target
18
+ start = target.slice(0,4)
19
+ last = target.slice(-4,target.length)
20
+ if last === ".git"
21
+ return false
22
+ elsif start === "http"
23
+ return true
24
+ else
25
+ return false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,240 @@
1
+ require 'optparse'
2
+ require 'set'
3
+
4
+ #Parses command line arguments for Brakeman
5
+ module Pipeline::Options
6
+
7
+ class << self
8
+
9
+ #Parse argument array
10
+ def parse args
11
+ get_options args
12
+ end
13
+
14
+ #Parse arguments and remove them from the array as they are matched
15
+ def parse! args
16
+ get_options args, true
17
+ end
18
+
19
+ #Return hash of options and the parser
20
+ def get_options args, destructive = false
21
+ options = {}
22
+
23
+ parser = OptionParser.new do |opts|
24
+ opts.banner = "Usage: pipeline [options] image/root/path"
25
+
26
+ opts.separator ""
27
+ opts.separator "Pipeline is a swiss army knife of security analysis tools."
28
+ opts.separator "It has built in support for static analysis, AV, fim, and "
29
+ opts.separator "is being extended to be used for analyzing all kinds of "
30
+ opts.separator "projects, images or file systems."
31
+ opts.separator ""
32
+ opts.separator "Pipeline also features filters to perform deduplication "
33
+ opts.separator "and the abilty to handle false positives."
34
+ opts.separator ""
35
+ opts.separator "See also the docker image."
36
+ opts.separator ""
37
+
38
+ opts.separator "Control options:"
39
+
40
+ opts.on "-n", "--no-threads", "Run checks sequentially" do
41
+ options[:parallel_checks] = false
42
+ end
43
+
44
+ opts.on "--[no-]progress", "Show progress reports" do |progress|
45
+ options[:report_progress] = progress
46
+ end
47
+
48
+ opts.on "-T", "--target PATH", "Specify target" do |target|
49
+ options[:target] = path
50
+ end
51
+
52
+ opts.on "-q", "--[no-]quiet", "Suppress informational messages" do |quiet|
53
+ options[:quiet] = quiet
54
+ end
55
+
56
+ opts.on( "-z", "--exit-on-warn", "Exit code is non-zero if warnings found") do
57
+ options[:exit_on_warn] = true
58
+ end
59
+
60
+ opts.separator ""
61
+ opts.separator "Scanning options:"
62
+
63
+ opts.on "-A", "--run-all-checks", "Run all default and optional checks" do
64
+ options[:run_all_checks] = true
65
+ end
66
+
67
+ opts.on "-t", "--test Check1,Check2,etc", Array, "Only run the specified checks" do |checks|
68
+ options[:run_tasks] ||= Set.new
69
+ options[:run_tasks].merge checks
70
+ end
71
+
72
+ opts.on "-x", "--except Check1,Check2,etc", Array, "Skip the specified checks" do |skip|
73
+ skip.each do |s|
74
+
75
+ options[:skip_checks] ||= Set.new
76
+ options[:skip_checks] << s
77
+ end
78
+ end
79
+
80
+ opts.on "-a", "--appname NAME", "Override the inferred application name." do |appname|
81
+ options[:appname] = appname
82
+ end
83
+
84
+ opts.on "-r", "--revision REV", "Specify a revision of software to pass on to checkmarx" do |revision|
85
+ options[:revision] = revision
86
+ end
87
+
88
+ opts.on "-l", "--labels Label1,Label2,etc", Array, "Run the checks with the supplied labels" do |labels|
89
+ options[:labels] ||= Set.new
90
+ options[:labels].merge labels
91
+ end
92
+
93
+ opts.on "--add-checks-path path1,path2,etc", Array, "A directory containing additional out-of-tree checks to run" do |paths|
94
+ options[:additional_checks_path] ||= Set.new
95
+ options[:additional_checks_path].merge paths.map {|p| File.expand_path p}
96
+ end
97
+
98
+ opts.on "--npm-registry URL", "Use a custom npm registry when installing dependencies for javascript scanners" do |url|
99
+ options[:npm_registry] = url
100
+ end
101
+
102
+ opts.separator ""
103
+ opts.separator "Output options:"
104
+
105
+ opts.on "-d", "--debug", "Lots of output" do
106
+ options[:debug] = true
107
+ end
108
+
109
+ opts.on "-f",
110
+ "--format TYPE",
111
+ [:text, :html, :csv, :tabs, :json, :jira, :markdown],
112
+ "Specify output formats. Default is text" do |type|
113
+ options[:output_format] = type
114
+ end
115
+
116
+ opts.on "--css-file CSSFile", "Specify CSS to use for HTML output" do |file|
117
+ options[:html_style] = File.expand_path file
118
+ end
119
+
120
+ opts.on "-i IGNOREFILE", "--ignore-config IGNOREFILE", "Use configuration to ignore warnings" do |file|
121
+ options[:ignore_file] = file
122
+ end
123
+
124
+ opts.on "-I", "--interactive-ignore", "Interactively ignore warnings" do
125
+ options[:interactive_ignore] = true
126
+ end
127
+
128
+ opts.on "-o", "--output FILE", "Specify file for output. Defaults to stdout." do |file|
129
+ options[:output_file] = file
130
+ end
131
+
132
+ opts.on "--summary", "Only output summary of warnings" do
133
+ options[:summary_only] = true
134
+ end
135
+
136
+ opts.separator ""
137
+ opts.separator "JIRA options:"
138
+
139
+ opts.on "--jira-project PROJECT", "Specify the jira project to create issues in. If issue looks like APPS-13, this should be APPS." do |project|
140
+ options[:jira_project] = project
141
+ end
142
+
143
+ opts.on "--jira-api-url URL", "Specify the jira rest api endpoint. Eg. domain.com/jira/jira/rest/api/2/." do |url|
144
+ options[:jira_api_url] = url
145
+ end
146
+
147
+ opts.on "--jira-cookie COOKIE", "Specify the session cookie to get to Jira." do |cookie|
148
+ options[:jira_cookie] = cookie
149
+ end
150
+
151
+ opts.on "--jira-component COMPONENT", "Specify the JIRA component to use." do |component|
152
+ options[:jira_component] = component
153
+ end
154
+
155
+ opts.separator ""
156
+ opts.separator "ZAP options:"
157
+
158
+ opts.on "--zap-api-token token", "Specify the ZAP API token to use when connecting to the API" do |token|
159
+ options[:zap_api_token] = token
160
+ end
161
+
162
+ opts.on "--zap-host HOST", "Specify the host ZAP is running on." do |host|
163
+ options[:zap_host] = host
164
+ end
165
+
166
+ opts.on "--zap-port PORT", "Specify the port ZAP is running on." do |port|
167
+ options[:zap_port] = port
168
+ end
169
+
170
+ opts.separator ""
171
+ opts.separator "Checkmarx options:"
172
+
173
+ opts.on "--checkmarx-user USER", "Specify the Checkmarx user to use when connecting to the API" do |user|
174
+ options[:checkmarx_user] = user
175
+ end
176
+
177
+ opts.on "--checkmarx-password PASSWORD", "Specify password for the Checkmarx API user" do |password|
178
+ options[:checkmarx_password] = password
179
+ end
180
+
181
+ opts.on "--checkmarx-server server", "Specify the API server to use for Checkmarx scans" do |server|
182
+ options[:checkmarx_server] = server
183
+ end
184
+
185
+ opts.on "--checkmarx-log logfile", "Specify the log file to use for Checkmarx scans" do |logfile|
186
+ options[:checkmarx_log] = logfile
187
+ end
188
+
189
+ opts.on "--checkmarx-project project", "Specify the full path of the Checkmarx project for this scan" do |project|
190
+ options[:checkmarx_project] = project
191
+ end
192
+
193
+ opts.separator ""
194
+ opts.separator "Configuration files:"
195
+
196
+ opts.on "-c", "--config-file FILE", "Use specified configuration file" do |file|
197
+ options[:config_file] = File.expand_path(file)
198
+ end
199
+
200
+ opts.on "-C", "--create-config [FILE]", "Output configuration file based on options" do |file|
201
+ if file
202
+ options[:create_config] = file
203
+ else
204
+ options[:create_config] = true
205
+ end
206
+ end
207
+
208
+ opts.separator ""
209
+
210
+ opts.on "-k", "--checks", "List all available vulnerability checks" do
211
+ options[:list_checks] = true
212
+ end
213
+
214
+ opts.on "--optional-checks", "List optional checks" do
215
+ options[:list_optional_checks] = true
216
+ end
217
+
218
+ opts.on "-v", "--version", "Show Pipeline version" do
219
+ options[:show_version] = true
220
+ end
221
+
222
+ opts.on_tail "-h", "--help", "Display this message" do
223
+ options[:show_help] = true
224
+ end
225
+ end
226
+
227
+ if destructive
228
+ parser.parse! args
229
+ else
230
+ parser.parse args
231
+ end
232
+
233
+ if options[:previous_results_json] and options[:output_files]
234
+ options[:comparison_output_file] = options[:output_files].shift
235
+ end
236
+
237
+ return options, parser
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,50 @@
1
+ class Pipeline::Reporters
2
+ @reporters = []
3
+
4
+ #Add a task. This will call +_klass_.new+ when running tests
5
+ def self.add klass
6
+ @reporters << klass unless @reporters.include? klass
7
+ end
8
+
9
+ def self.reporters
10
+ @reporters
11
+ end
12
+
13
+ def self.initialize_reporters reporters_directory = ""
14
+ #Load all files in task_directory
15
+ Dir.glob(File.join(reporters_directory, "*.rb")).sort.each do |f|
16
+ require f
17
+ end
18
+ end
19
+
20
+ #No need to use this directly.
21
+ def initialize options = { }
22
+ end
23
+
24
+ #Run all the tasks on the given Tracker.
25
+ #Returns a new instance of tasks with the results.
26
+ def self.run_report(tracker)
27
+ @reporters.each do |c|
28
+ reporter = c.new()
29
+ if tracker.options[:output_format].first == reporter.format
30
+ begin
31
+ output = reporter.run_report(tracker)
32
+ if tracker.options[:output_file]
33
+ file = File.open(tracker.options[:output_file], 'w'){ |f| f.write(output)}
34
+ else
35
+ Pipeline.notify output
36
+ end
37
+ rescue => e
38
+ Pipeline.error e.message
39
+ tracker.error e
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ #Load all files in reporters/ directory
48
+ Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/reporters/*.rb").sort.each do |f|
49
+ require f.match(/(pipeline\/reporters\/.*)\.rb$/)[0]
50
+ end
@@ -0,0 +1,21 @@
1
+ require 'pipeline/finding'
2
+
3
+ class Pipeline::BaseReporter
4
+ attr_accessor :name, :format
5
+
6
+ def initialize()
7
+ end
8
+
9
+ def run_report(tracker)
10
+ Pipeline.notify "Running base reoprt..."
11
+ output = ""
12
+ tracker.findings.each do |finding|
13
+ output += out(finding)
14
+ end
15
+ output
16
+ end
17
+
18
+ def out(finding)
19
+ end
20
+
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'pipeline/finding'
2
+ require 'pipeline/reporters/base_reporter'
3
+
4
+ class Pipeline::CSVReporter < Pipeline::BaseReporter
5
+
6
+ Pipeline::Reporters.add self
7
+
8
+ attr_accessor :name, :format
9
+
10
+ def initialize()
11
+ @name = "CSVReporter"
12
+ @format = :to_csv
13
+ end
14
+
15
+ def out(finding)
16
+ finding.to_csv
17
+ end
18
+
19
+ end
@@ -0,0 +1,61 @@
1
+ require 'pipeline/finding'
2
+ require 'pipeline/reporters/base_reporter'
3
+ require 'json'
4
+ require 'curb'
5
+
6
+ class Pipeline::JiraReporter < Pipeline::BaseReporter
7
+
8
+ Pipeline::Reporters.add self
9
+
10
+ attr_accessor :name, :format
11
+
12
+ def initialize()
13
+ @name = "JiraReporter"
14
+ @format = :to_jira
15
+ end
16
+
17
+ def run_report(tracker)
18
+ @project = tracker.options[:jira_project.to_s]
19
+ @api = tracker.options[:jira_api_url.to_s]
20
+ @cookie = tracker.options[:jira_cookie.to_s]
21
+ @component = tracker.options[:jira_component.to_s]
22
+
23
+ tracker.findings.each do |finding|
24
+ report finding
25
+ end
26
+ "Results are in JIRA"
27
+ end
28
+
29
+ def report(finding)
30
+ json = get_jira_json(finding)
31
+ http = Curl.post("#{@api}/issue/", json.to_s) do |http|
32
+ http.headers['Content-Type'] = "application/json"
33
+ http.headers['Cookie'] = @cookie
34
+ end
35
+ if http.response_code != 201 # Created ...
36
+ Pipeline.error "Problem with HTTP #{http.response_code} - #{http.body_str}"
37
+ end
38
+ end
39
+
40
+ private
41
+ def get_jira_json(finding)
42
+ json = {
43
+ "fields": {
44
+ "project":
45
+ {
46
+ "key": "#{@project}"
47
+ },
48
+ "summary": "#{finding.appname} - #{finding.description}",
49
+ "description": "#{finding.to_string}\n\nFINGERPRINT: #{finding.fingerprint}",
50
+ "issuetype": {
51
+ "name": "Task"
52
+ },
53
+ "labels":["Pipeline","#{finding.appname}"],
54
+ "components": [ { "name": "#{@component}" } ]
55
+ }
56
+ }.to_json
57
+ json
58
+ end
59
+ end
60
+
61
+
@@ -0,0 +1,20 @@
1
+ require 'pipeline/finding'
2
+ require 'pipeline/reporters/base_reporter'
3
+
4
+ require 'json'
5
+
6
+ class Pipeline::JSONReporter < Pipeline::BaseReporter
7
+
8
+ Pipeline::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