Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions scanners/zap-advanced/examples/nginx/0-foo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# tmp file for local testing

global:
# Sets the ZAP Session name
sessionName: nginx

# ZAP Contexts Configuration
contexts:
# Name to be used to refer to this context in other jobs, mandatory
- name: nginx
# The top level url, mandatory, everything under this will be included. IMPORTANT: must be the hostname without any subpath!
url: http://localhost:8082/
# An optional list of regexes to include
includePaths:
- "http://localhost:8082/.*"
# An optional list of regexes to exclude
excludePaths:
- ".*\\.css"
- ".*\\.png"
- ".*\\.jpeg"
alertFilters:
- ruleId: 10106
newLevel: "False Positive"
matches:
attack: ".*"
attackIsRegex: true

evidence: ".*"
evidenceIsRegex: true

parameter: ".*"
parameterIsRegex: true

url: ".*"
urlIsRegex: true


# ZAP Spiders Configuration
spiders:
- name: nginx-spider
# String: Name of the context to spider, default: first context
context: nginx
# String: Url to start spidering from, default: first context URL
url: http://localhost:8082/

# ZAP ActiveScans Configuration
scanners:
- name: nginx-scan
# String: Name of the context to attack, default: first context
context: nginx
# String: Url to start scanning from, default: first context URL
url: http://localhost:8082/
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# SPDX-FileCopyrightText: 2021 iteratec GmbH
#
# SPDX-License-Identifier: Apache-2.0

---
# Global ZAP Configurations
global:
# True to create another ZAP session (overwrite the former if the same name already exists), False to use an existing on
isNewSession: true
# Sets the ZAP Session name
sessionName: SCB

# List of 1 or more contexts, mandatory
contexts:
# Name to be used to refer to this context in other jobs, mandatory
- name: scb-petstore-context
# The top level url, mandatory, everything under this will be included. IMPORTANT: must be the hostname without any subpath!
url: http://petstore:8080/
# An optional list of regexes to include
includePaths:
- "http://petstore:8080/v2.*"
# An optional list of regexes to exclude
excludePaths:
- ".*\\.css"
- ".*\\.png"
- ".*\\.jpeg"
alertFilters:
# ignore a bunch of rules to reduce number of findings in tests
- ruleId: 10020
newLevel: "False Positive"
- ruleId: 10021
newLevel: "False Positive"
- ruleId: 10024
newLevel: "False Positive"
- ruleId: 10036
newLevel: "False Positive"
- ruleId: 10038
newLevel: "False Positive"
- ruleId: 10049
newLevel: "False Positive"
- ruleId: 10063
newLevel: "False Positive"
- ruleId: 10098
newLevel: "False Positive"
- ruleId: 10109
newLevel: "False Positive"
- ruleId: 40033
newLevel: "False Positive"
- ruleId: 40039
newLevel: "False Positive"
- ruleId: 40040
newLevel: "False Positive"
- ruleId: 90003
newLevel: "False Positive"

apis:
# -- The name of the spider configuration
- name: scb-petstore-api
# -- The Name of the context (zapConfiguration.contexts[x].name) to spider, default: first context available.
context: scb-petstore-context
# -- format of the API ('openapi', 'grapql', 'soap')
format: openapi
# -- Url to start spidering from, default: first context URL
url: http://petstore:8080/v2/swagger.json
# -- Override host setting in swagger.json
hostOverride: http://petstore:8080

spiders:
- name: scb-petstore-spider
# String: Name of the context to spider, default: first context
context: scb-petstore-context
# String: Url to start spidering from, default: first context URL
url: http://petstore:8080/v2/
# Int: Fail if spider finds less than the specified number of URLs, default: 0
failIfFoundUrlsLessThan: 0
# Int: Warn if spider finds less than the specified number of URLs, default: 0
warnIfFoundUrlsLessThan: 0
# Int: The max time in minutes the spider will be allowed to run for, default: 0 unlimited
maxDuration: 1
# Int: The maximum tree depth to explore, default 5
maxDepth: 5
# Int: The maximum number of children to add to each node in the tree
maxChildren: 10
# # Int: The max size of a response that will be parsed, default: 2621440 - 2.5 Mb
# maxParseSizeBytes: 2621440
# Bool: Whether the spider will accept cookies, default: true
acceptCookies: true
# Bool: Whether the spider will handle OData responses, default: false
handleODataParametersVisited: false
# Enum [ignore_completely, ignore_value, use_all]: How query string parameters are used when checking if a URI has already been visited, default: use_all
handleParameters: use_all
# Bool: Whether the spider will parse HTML comments in order to find URLs, default: true
parseComments: true
# Bool: Whether the spider will parse Git metadata in order to find URLs, default: false
parseGit: false
# Bool: Whether the spider will parse 'robots.txt' files in order to find URLs, default: true
parseRobotsTxt: false
# Bool: Whether the spider will parse 'sitemap.xml' files in order to find URLs, default: true
parseSitemapXml: false
# Bool: Whether the spider will parse SVN metadata in order to find URLs, default: false
parseSVNEntries: false
# Bool: Whether the spider will submit POST forms, default: true
postForm: true
# Bool: Whether the spider will process forms, default: true
processForm: true
# Int: The time between the requests sent to a server in milliseconds, default: 200
requestWaitTime: 200
# Bool: Whether the spider will send the referer header, default: true
sendRefererHeader: true
# Int: The number of spider threads, default: 2
threadCount: 5
# String: The user agent to use in requests, default: '' - use the default ZAP one
userAgent: "secureCodeBox / ZAP Spider"

scanners:
- name: scb-petstore-scan
# String: Name of the context to attack, default: first context
context: scb-petstore-context
# String: Url to start scaning from, default: first context URL
url: http://petstore:8080/v2/
# String: Name of the scan policy to be used, default: Default Policy
policy: "API-Minimal"
# Int: The max time in minutes any individual rule will be allowed to run for, default: 0 unlimited
maxRuleDurationInMins: 1
# Int: The max time in minutes the active scanner will be allowed to run for, default: 0 unlimited
maxScanDurationInMins: 5
# Int: The max number of threads per host, default: 2
threadPerHost: 5
# Int: The delay in milliseconds between each request, use to reduce the strain on the target, default 0
delayInMs: 0
# Bool: If set will add an extra query parameter to requests that do not have one, default: false
addQueryParam: false
# Bool: If set then automatically handle anti CSRF tokens, default: false
handleAntiCSRFTokens: false
# Bool: If set then the relevant rule Id will be injected into the X-ZAP-Scan-ID header of each request, default: false
injectPluginIdInHeader: false
# Bool: If set then the headers of requests that do not include any parameters will be scanned, default: false
scanHeadersAllRequests: false
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,20 @@ def test_petstore_scan_with_config(get_petstore_url, get_zap_instance: ZAPv2):
logging.info('Found ZAP Alerts: %s', str(len(alerts)))

assert int(len(alerts)) >= 1

@pytest.mark.integrationtest
def test_petstore_scan_with_alert_filters(get_petstore_url, get_zap_instance: ZAPv2):

zap = get_zap_instance
test_config_yaml = "./tests/mocks/scan-full-petstore-alert-filter-docker/"
test_target = "http://petstore:8080/"

zap_automation = ZapAutomation(zap=zap, config_dir=test_config_yaml)
zap_automation.scan_target(target=test_target)

alerts = zap_automation.get_zap_scanner.get_alerts(test_target, [], [])

logging.info('Found ZAP Alerts: %d', len(alerts))

# should normally be 13 alerts but most of them are ignored using alertFilters in the scan config
assert int(len(alerts)) < 10
68 changes: 68 additions & 0 deletions scanners/zap-advanced/scanner/zapclient/context/zap_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ def _configure_context(self, context: collections.OrderedDict):
# TODO: Open a new ZAP GH Issue: Why (or) is this difference (context_id vs. context_name) here really necessary?
self._configure_context_technologies(context["technologies"], context_name)

if self._is_not_empty("alertFilters", context):
self._configure_alert_filters(context["alertFilters"], context_id)

def _configure_context_include(self, context: collections.OrderedDict):
"""Protected method to configure the ZAP 'Context / Include Settings' based on a given ZAP config.

Expand Down Expand Up @@ -277,3 +280,68 @@ def _configure_context_technologies(self, technology: collections.OrderedDict, c
technologies = ", ".join(technology["included"])
logging.debug("Exclude technologies '%s' in context with name %s", technologies, context_name)
self.get_zap.context.exclude_context_technologies(contextname=context_name, technologynames=technologies)

def _get_or_none(self, dict: collections.OrderedDict, key: str):
if dict == None or not isinstance(dict, collections.OrderedDict):
return None

if key in dict:
return dict[key]
else:
return None

def _get_or_none_stringified(self, dict: collections.OrderedDict, key: str):
value = self._get_or_none(dict, key)

if value == None:
return None
else:
return str(value)

def _get_level(self, level: str):
# lowercase input to catch simple typos
level = level.lower()
if level == "false positive":
return -1
elif level == "info" or level == "informational":
return 0
elif level == "low":
return 1
elif level == "medium":
return 2
elif level == "high":
return 3

logging.warn("AlertFilter configured with unknown level: '%s'. This rule will be ignored!", level)
return None

def _configure_alert_filters(self, alert_filters: list[collections.OrderedDict], context_id: int):
"""Protected method to configure the ZAP 'Context / Alert Filters' Settings based on a given ZAP config.

Parameters
----------
alert_filters : collections.OrderedDict
The current alert filter configuration object containing the ZAP alert filter configuration (based on the class ZapConfiguration).
context_id : int
The zap context id to configure the ZAP alert filters for (based on the class ZapConfiguration).
"""

if(alert_filters):
for alert_filter in alert_filters:
logging.debug("Adding AlertFilter for rule '%d' in context with id %s", alert_filter["ruleId"], context_id)

matches = alert_filter["matches"] if "matches" in alert_filter else collections.OrderedDict()
self.get_zap.alertFilter.add_alert_filter(
contextid = context_id,
ruleid = str(alert_filter["ruleId"]),
newlevel = str(self._get_level(alert_filter["newLevel"])),
# optional matchers
url = self._get_or_none(matches, "url"),
urlisregex = self._get_or_none_stringified(matches, "urlIsRegex"),
parameter = self._get_or_none(matches, "parameter"),
parameterisregex = self._get_or_none_stringified(matches, "parameterIsRegex"),
attack = self._get_or_none(matches, "attack"),
attackisregex = self._get_or_none_stringified(matches, "attackIsRegex"),
evidence = self._get_or_none(matches, "evidence"),
evidenceisregex = self._get_or_none_stringified(matches, "evidenceIsRegex"),
)