owasp-pipeline 0.8.3
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 +23 -0
- data/FEATURES +19 -0
- data/README.md +101 -0
- data/bin/pipeline +67 -0
- data/lib/pipeline.rb +301 -0
- data/lib/pipeline/event.rb +14 -0
- data/lib/pipeline/filters.rb +41 -0
- data/lib/pipeline/filters/base_filter.rb +19 -0
- data/lib/pipeline/filters/jira_one_time_filter.rb +57 -0
- data/lib/pipeline/filters/remove_all_filter.rb +16 -0
- data/lib/pipeline/finding.rb +52 -0
- data/lib/pipeline/mounters.rb +55 -0
- data/lib/pipeline/mounters/base_mounter.rb +31 -0
- data/lib/pipeline/mounters/docker_mounter.rb +44 -0
- data/lib/pipeline/mounters/filesystem_mounter.rb +25 -0
- data/lib/pipeline/mounters/git_mounter.rb +52 -0
- data/lib/pipeline/mounters/iso_mounter.rb +42 -0
- data/lib/pipeline/mounters/url_mounter.rb +28 -0
- data/lib/pipeline/options.rb +240 -0
- data/lib/pipeline/reporters.rb +50 -0
- data/lib/pipeline/reporters/base_reporter.rb +21 -0
- data/lib/pipeline/reporters/csv_reporter.rb +19 -0
- data/lib/pipeline/reporters/jira_reporter.rb +61 -0
- data/lib/pipeline/reporters/json_reporter.rb +20 -0
- data/lib/pipeline/reporters/text_reporter.rb +19 -0
- data/lib/pipeline/scanner.rb +28 -0
- data/lib/pipeline/tasks.rb +124 -0
- data/lib/pipeline/tasks/av.rb +43 -0
- data/lib/pipeline/tasks/base_task.rb +64 -0
- data/lib/pipeline/tasks/brakeman.rb +60 -0
- data/lib/pipeline/tasks/bundle-audit.rb +93 -0
- data/lib/pipeline/tasks/checkmarx.rb +62 -0
- data/lib/pipeline/tasks/eslint.rb +71 -0
- data/lib/pipeline/tasks/fim.rb +61 -0
- data/lib/pipeline/tasks/nsp.rb +59 -0
- data/lib/pipeline/tasks/owasp-dep-check.rb +120 -0
- data/lib/pipeline/tasks/patterns.json +394 -0
- data/lib/pipeline/tasks/retirejs.rb +106 -0
- data/lib/pipeline/tasks/scanjs-eslintrc +106 -0
- data/lib/pipeline/tasks/scanjs.rb +32 -0
- data/lib/pipeline/tasks/sfl.rb +67 -0
- data/lib/pipeline/tasks/test.rb +47 -0
- data/lib/pipeline/tasks/zap.rb +84 -0
- data/lib/pipeline/tracker.rb +47 -0
- data/lib/pipeline/util.rb +39 -0
- data/lib/pipeline/version.rb +3 -0
- data/lib/zapjson.json +0 -0
- 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
|