Skip to content

Commit

Permalink
Working ZAP via API. Jenkins example script.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkonda committed Jan 3, 2016
1 parent 75445d9 commit eec2d82
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 194 deletions.
Empty file.
File renamed without changes.
9 changes: 9 additions & 0 deletions integrations/jenkins/pipeline-active.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Use existing managed scripts to add this to your Jenkins build
# Obviously depends on having docker-machine and docker set up.

echo "Starting Pipeline Tool"
echo "Script executed from: ${PWD}"

eval $(docker-machine env patched)
GUID="$RANDOM"
docker run -v ${PWD}:/tmp/$GUID/ jemurai/pipeline:0.8 -z -t zap -d /tmp/$GUID/
9 changes: 9 additions & 0 deletions integrations/jenkins/pipeline-code.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Use existing managed scripts to add this to your Jenkins build
# Obviously depends on having docker-machine and docker set up.

echo "Starting Pipeline Tool"
echo "Script executed from: ${PWD}"

eval $(docker-machine env patched)
GUID="$RANDOM"
docker run -v ${PWD}:/tmp/$GUID/ jemurai/pipeline:0.8 -z -L code -d /tmp/$GUID/
2 changes: 2 additions & 0 deletions lib/pipeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def self.default_options
:exit_on_warn => true,
:output_format => :text,
:working_dir => "~/line/tmp/",
:zap_host => "http://localhost",
:zap_port => "9999",
:labels => Set.new() << "filesystem" << "code" # Defaults to run.
}
end
Expand Down
22 changes: 7 additions & 15 deletions lib/pipeline/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,26 +149,18 @@ def get_options args, destructive = false
end

opts.separator ""
opts.separator "Checkmarx options:"
opts.separator "ZAP options:"

opts.on "--checkmarx-user USER", "Specify the Checkmarx user to use when connecting to the API" do |user|
options[:checkmarx_user] = user
opts.on "--zap-api-token token", "Specify the ZAP API token to use when connecting to the API" do |token|
options[:zap_api_token] = token
end

opts.on "--checkmarx-password PASSWORD", "Specify password for the Checkmarx API user" do |password|
options[:checkmarx_password] = password
opts.on "--zap-host HOST", "Specify the host ZAP is running on." do |host|
options[:zap_host] = host
end

opts.on "--checkmarx-server server", "Specify the API server to use for Checkmarx scans" do |server|
options[:checkmarx_server] = server
end

opts.on "--checkmarx-log logfile", "Specify the log file to use for Checkmarx scans" do |logfile|
options[:checkmarx_log] = logfile
end

opts.on "--checkmarx-project project", "Specify the full path of the Checkmarx project for this scan" do |project|
options[:checkmarx_project] = project
opts.on "--zap-port PORT", "Specify the port ZAP is running on." do |port|
options[:zap_port] = port
end

opts.separator ""
Expand Down
17 changes: 10 additions & 7 deletions lib/pipeline/tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,16 @@ def self.run_tasks(target, stage, tracker)

task = c.new(trigger, tracker)
begin
if (task.supported? && ( task.stage === stage ) && ( task.labels.intersect? tracker.options[:labels] ) ) # Only run tasks with lables.
Pipeline.notify "#{stage} - #{task_name} - #{task.labels}"
task.run
task.analyze
task.findings.each do | finding |
tracker.report finding
end
if task.supported? and task.stage == stage
if task.labels.intersect? tracker.options[:labels] or # Only run tasks with labels
( run_tasks and run_tasks.include? task_name.downcase ) # or that are explicitly requested.
Pipeline.notify "#{stage} - #{task_name} - #{task.labels}"
task.run
task.analyze
task.findings.each do | finding |
tracker.report finding
end
end
end
rescue => e
Pipeline.notify e.message
Expand Down
224 changes: 52 additions & 172 deletions lib/pipeline/tasks/zap.rb
Original file line number Diff line number Diff line change
@@ -1,154 +1,7 @@
require 'pipeline/tasks/base_task'
require 'json'
require 'pipeline/util'

require 'rexml/document'
require 'rexml/streamlistener'
include REXML

# SAX Like Parser for OWASP ZAP XML.
class Pipeline::ZAPListener
include StreamListener

def initialize(task)
@task = task
@count = 0
@pluginid = ""
@alert = ""
@confidence = ""
@riskdesc = ""
@desc = ""
@url = ""
@param = ""
@attack = ""
@otherinfo = ""
@solution = ""
@reference = ""
@wascid = ""
@cwe = ""
@fingerprint = ""
end

def tag_start(name, attrs)
case name
when "alertitem"
@count = @count + 1
# Pipeline.debug "Grabbed #{@count} vulns."
@pluginid = ""
@alert = ""
@confidence = ""
@riskdesc = ""
@desc = ""
@url = ""
@param = ""
@attack = ""
@otherinfo = ""
@solution = ""
@reference = ""
@wascid = ""
@cwe = ""
@fingerprint = ""
end
end

def tag_end(name)
case name
when "pluginid"
@pluginid = @text
when "alert"
@alert = @text
when "confidence"
@confidence = @text
when "riskdesc"
@riskdesc = @text
when "desc"
@desc = @text
when "uri"
@url = @text
when "param"
@param = @text.strip
when "attack"
@attack = @text.strip
when "otherinfo"
@otherinfo = @text.chomp
when "solution"
@solution = @text
when "reference"
@reference = @text
when "wascid"
@wascid = @text
when "cwe"
@cwe = @text
when "alertitem"
detail = get_detail
description = get_description
source = get_source
get_fingerprint
risk = "Risk: #{@riskdesc} / Confidence (of 1-3 Low, Medium, High): #{@confidence}"

# puts "Vuln: #{@alert} Severity: #{risk}\n\tDescription: #{description}\n\tDetail: #{detail}"
# puts "\tFingerprint: #{@fingerprint}"
@task.report description, detail, source, risk, @fingerprint
end
end

def get_fingerprint
@fingerprint = "ZAP-#{@pluginid}-#{@url}-#{@alert}"
if @param != ""
@fingerprint << "-#{@param}"
end
if @cwe != ""
@fingerprint << "-#{@cwe}"
end
if @wascid != ""
@fingerprint << "-#{@wascid}"
end
@fingerprint = @fingerprint.strip.gsub("\n", "")
end

def get_source
source = "ZAP Plugin: #{@pluginid} URL: #{@url}"
source
end


def get_detail
detail = "URL: #{@url}\n\t"
if @param != ""
detail << "Param: #{@param}\t"
end
if @attack != ""
detail << "Attack: #{@attack}\n\t"
end
if @otherinfo != "" and @otherinfo.strip != ""
detail << "Background: #{@otherinfo}\n\t"
end
if @reference != ""
detail << "Reference: #{@reference}\n\t"
end
if @solution != ""
detail << "Solution: #{@solution}"
end
detail
end

def get_description
# Format description.
description = ""
if @cwe != ""
description = "CWE: #{@cwe}\t"
end
if @wascid != ""
description << "WASC ID: #{@wascid}\t"
end
description << "\n\tDesc: #{@desc}"
description
end

def text(text)
@text = text.chomp
end
end
require 'json'
require 'curb'

class Pipeline::Zap < Pipeline::BaseTask

Expand All @@ -164,46 +17,73 @@ def initialize(trigger,tracker)
end

def run
Pipeline.notify "#{@name}"
rootpath = @trigger.path
host = @tracker.options[:zap_host]
port = @tracker.options[:zap_port]
base = "#{host}:#{port}"
Pipeline.debug "Running ZAP on: #{rootpath} from #{base}"

# TODO: Add API Key
# TODO: Find out if we need to worry about "contexts" stepping on each other.

# Spider
Curl.get("#{base}/JSON/spider/action/scan/?#{rootpath}")
poll_until_100("#{base}/JSON/spider/view/status")

# Active Scan
Curl.get("#{base}/JSON/ascan/action/scan/?recurse=true&inScopeOnly=true&url=#{rootpath}")
poll_until_100("#{base}/JSON/ascan/view/status/")

# Result
@result = Curl.get("#{base}/JSON/core/view/alerts/").body_str
end

Pipeline.debug "Running ZAP on: #{rootpath}"
#@result = runsystem(true, "rm", "/tmp/zap.xml")
#Pipeline.debug "Remove old ZAP file."

# See /docker/zap for details on how this is going to work:
@result=runsystem(true, "docker","run","-u","zap","-i","pipeline/zap:v1","zap-cli","--api-key","123","quick-scan","--spider","-l","Medium","-sc","-r","-o","'-config api.key=123'","#{rootpath}")


def poll_until_100(url)
count = 0
loop do
sleep 5
status = JSON.parse(Curl.get(url).body_str)
count = count + 1
Pipeline.notify "Count ... #{count}"
break if status["status"] == "100" or count > 100
end
end

def analyze
puts @result
begin
#path = @trigger.path + "/tmp/zap.xml"
path = "/tmp/zap.xml"
get_warnings(path)
begin
json = JSON.parse @result
alerts = json["alerts"]
count = 0
alerts.each do |alert|
# def report description, detail, source, severity, fingerprint
description = alert["description"]
detail = "Url: #{alert["url"]} Param: #{alert["param"]} \nReference: #{alert["reference"]}\n"+
"Solution: #{alert["solution"]}\nCWE: #{alert["cweid"]}\tWASCID: #{alert["wascid"]}"
source = @name + alert["url"]
sev = severity alert["risk"]
fingerprint = @name + alert["url"] + alert["alert"] + alert["param"]
report description, detail, source, sev, fingerprint
count = count + 1
end
Pipeline.debug "ZAP Identified #{count} issues."
rescue Exception => e
Pipeline.warn e.message
Pipeline.notify "Problem running ZAP."
end
end

def supported?
supported=runsystem(true, "java","-Xmx512m","-jar", "/area52/ZAP_2.4.1/zap-2.4.1.jar", "-version")
if supported =~ /2.4.1/
host = @tracker.options[:zap_host]
port = @tracker.options[:zap_port]
base = "#{host}:#{port}"
supported=JSON.parse(Curl.get("#{base}/JSON/core/view/version/?zapapiformat=JSON").body_str)
if supported["version"] == "2.4.3"
return true
else
Pipeline.notify "Install ZAP from owasp.org"
Pipeline.notify "Install ZAP from owasp.org and ensure that the configuration to connect is correct."
return false
end
end

def get_warnings(path)
listener = Pipeline::ZAPListener.new(self)
parser = Parsers::StreamParser.new(File.new(path), listener)
parser.parse
end

end

0 comments on commit eec2d82

Please sign in to comment.