diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb61aa..7830e19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Unreleased] +## [0.4.1] - 2025-05-30 +- Adds clientSpider API to the `ZAPv2` class. + ## [0.4.0] - 2025-01-20 ### Changed - Update APIs for 2.16. diff --git a/src/examples/client-spider-scan.py b/src/examples/client-spider-scan.py new file mode 100644 index 0000000..4a4f8f9 --- /dev/null +++ b/src/examples/client-spider-scan.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from zapv2 import ZAPv2 +import time + + +target = 'http://127.0.0.1' # Change to match the target URL you want to scan +apikey = 'changeme' # Change to match the API key set in ZAP, or use None if the API key is disabled + + +zap = ZAPv2(apikey=apikey) + +print(f'Spidering target {target}') +scanid = zap.clientSpider.scan(url=target) + +while (int(zap.clientSpider.status(scanid)) < 100): + print(f"Spider progress %: {zap.clientSpider.status(scanid)}") + time.sleep(2) + +print('Spider completed') diff --git a/src/zapv2/__init__.py b/src/zapv2/__init__.py index 7541712..389e157 100644 --- a/src/zapv2/__init__.py +++ b/src/zapv2/__init__.py @@ -36,6 +36,7 @@ from .automation import automation from .autoupdate import autoupdate from .brk import brk +from .client_spider import ClientSpider from .context import context from .core import core from .custompayloads import custompayloads @@ -103,6 +104,7 @@ def __init__(self, proxies=None, apikey=None, validate_status_code=False): self.automation = automation(self) self.autoupdate = autoupdate(self) self.brk = brk(self) + self.clientSpider = ClientSpider(self) self.context = context(self) self.core = core(self) self.custompayloads = custompayloads(self) diff --git a/src/zapv2/client_spider.py b/src/zapv2/client_spider.py new file mode 100644 index 0000000..758ddf5 --- /dev/null +++ b/src/zapv2/client_spider.py @@ -0,0 +1,73 @@ +# Zed Attack Proxy (ZAP) and its related class files. +# +# ZAP is an HTTP/HTTPS proxy for assessing web application security. +# +# Copyright 2022 the ZAP development team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This was hand typed by a human, not generated by a script. +""" + +import warnings +import six + + +class ClientSpider: + + def __init__(self, zap): + self.zap = zap + + def status(self, scanid=None): + """ + + This component is optional and therefore the API will only work if it is installed + """ + params = {} + if scanid is not None: + params['scanId'] = scanid + return six.next(six.itervalues(self.zap._request(self.zap.base + 'clientSpider/view/status/', params))) + + + def scan(self, url=None, browser=None, contextName=None, userName=None, subtreeonly=None, apikey=''): + """ + Runs the spider against the given URL (or context). Optionally, the parameter 'contextName' can be used to constrain the scan to a Context and the parameter 'subtreeOnly' allows to restrict the spider under a site's subtree (using the specified 'url'). + This component is optional and therefore the API will only work if it is installed. + """ + if apikey: + warnings.warn("The 'apikey' parameter is deprecated (unused) and will be removed in a future release.") + + params = {} + if browser is not None: + params['browser'] = browser + if url is not None: + params['url'] = url + if contextName is not None: + params['contextName'] = contextName + if userName is not None: + params['userName'] = userName + if subtreeonly is not None: + params['subtreeOnly'] = subtreeonly + return six.next(six.itervalues(self.zap._request(self.zap.base + 'clientSpider/action/scan/', params))) + + def stop(self, scanid=None, apikey=''): + """ + This component is optional and therefore the API will only work if it is installed + """ + if apikey: + warnings.warn("The 'apikey' parameter is deprecated (unused) and will be removed in a future release.") + + params = {} + if scanid is not None: + params['scanId'] = scanid + return six.next(six.itervalues(self.zap._request(self.zap.base + 'clientSpider/action/stop/', params)))