-
-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First push of new independent Python API for ZAP #1
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ __pycache__/ | |
*.py[cod] | ||
*$py.class | ||
|
||
.idea/ | ||
# C extensions | ||
*.so | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
include LICENSE requirements.txt | ||
|
||
recursive-exclude * __pycache__ | ||
recursive-exclude * *.pyc | ||
recursive-exclude * *.pyo | ||
recursive-exclude * *.orig | ||
recursive-exclude * .DS_Store | ||
global-exclude __pycache__/* | ||
global-exclude .deps/* | ||
global-exclude *.so | ||
global-exclude *.pyd | ||
global-exclude *.pyc | ||
global-exclude .git* | ||
global-exclude .DS_Store | ||
global-exclude .mailmap |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from __future__ import print_function | ||
|
||
import time | ||
|
||
from pprint import pprint | ||
from zapv2 import ZAPv2 | ||
|
||
target = 'http://127.0.0.1' | ||
|
||
# By default ZAP API client will connect to port 8080 | ||
zap = ZAPv2() | ||
|
||
# Or, you can configure your own IP/Port | ||
# zap_9090 = ZAPv2(proxies={'http': '127.0.0.1:9090', 'https': '127.0.0.1:9090'}) | ||
|
||
# Use the line below if ZAP is not listening on port 8080, for example, if listening on port 8090 | ||
# zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8090', 'https': 'http://127.0.0.1:8090'}) | ||
|
||
# do stuff | ||
print('Accessing target %s' % target) | ||
|
||
# try have a unique enough session... | ||
zap.urlopen(target) | ||
|
||
# Give the sites tree a chance to get updated | ||
time.sleep(2) | ||
|
||
print('Spidering target %s' % target) | ||
scanid = zap.spider.scan(target) | ||
|
||
# Give the Spider a chance to start | ||
time.sleep(2) | ||
|
||
while int(zap.spider.status(scanid)) < 100: | ||
print('Spider progress %: ' + zap.spider.status(scanid)) | ||
time.sleep(2) | ||
|
||
print('Spider completed') | ||
|
||
# Give the passive scanner a chance to finish | ||
time.sleep(5) | ||
|
||
print('Scanning target %s' % target) | ||
|
||
scanid = zap.ascan.scan(target) | ||
while int(zap.ascan.status(scanid)) < 100: | ||
print('Scan progress %: ' + zap.ascan.status(scanid)) | ||
time.sleep(5) | ||
|
||
print('Scan completed') | ||
|
||
# Report the results | ||
|
||
print('Hosts: ' + ', '.join(zap.core.hosts)) | ||
print('Alerts: ') | ||
pprint((zap.core.alerts())) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
six | ||
ujson | ||
requests | ||
requests-cache |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#!/usr/bin/env python | ||
|
||
""" | ||
Standard build script. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whats the process for building and releasing this library now? Be good to document it, esp for python noobs like me ;) Could be documented in this repo or on https://github.com/zaproxy/zaproxy/wiki/GeneratingTheFullRelease#generate-the-release-add-ons There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hahaha oks, no problem :) As a said above, to release a new version to Pypi, we'll need to do: Register the new package https://pypi.python.org/pypi?%3Aaction=login_form Then It'll necessary to register the new version, running (in python 2 o 3): Uploads
|
||
""" | ||
|
||
from __future__ import print_function | ||
|
||
from os.path import dirname, join | ||
|
||
try: | ||
from setuptools import setup, find_packages | ||
except ImportError: | ||
print("You must have setuptools installed to use setup.py. Exiting...") | ||
raise SystemExit(1) | ||
|
||
__docformat__ = 'restructuredtext' | ||
|
||
# Import requirements | ||
with open(join(dirname(__file__), 'requirements.txt')) as f: | ||
required = f.read().splitlines() | ||
|
||
setup( | ||
name="python-owasp-zap-v2.4", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum, should this be renamed to python-owasp-zap-v2.5 ? As 2.5.0 is coming out soon? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really, IMHO, the better choice si to use a standard name and unify the version. So there's a "version" param in setup(..). Currently in Pypi are available 3 different libraries: This is not a the correct way to send an update to Pypi. I'll change the name to: For Python developers is more intuitive and easy if only one library is available, with different versions release. When we update for a new version, not change the name name of library, only de version param, doing: Of, for the first release (if we change the name): There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you agree, I'll send a PR with the change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Python library is using those names to prevent "incompatibility issues" between releases of ZAP. For example, newer versions include functionalities that will not work with older versions of ZAP, so the v2.4 (or v2.5) indicates the version of ZAP that the client implementation is targeting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To do that, I think a better approach with having only one library, but with a different API. This is: For version 2.4 of ZAP For version 2.5 of ZAP And so on. This way, all ZAP versions are unified in only one library. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The API is continually evolving, and will carry on doing so for the foreseeable future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea could be: New organization of code, refactoring the Python entry point classes (ZAP_23, ZAP_25..) but, the entry points generated automatically with Java. Doing this, maybe we can unify the two approaches. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Works for me :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oka, I'll send a PR proposal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just sent the proposal :) |
||
version="0.0.8", | ||
description="OWASP ZAP 2.4 API client", | ||
install_requires=required, | ||
long_description="OWASP Zed Attack Proxy 2.4 API python client", | ||
author="ZAP development team", | ||
author_email='', | ||
url="https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project", | ||
download_url="https://github.com/zaproxy/zaproxy/releases/tag/2.4.3", | ||
platforms=['any'], | ||
|
||
license="ASL2.0", | ||
|
||
package_dir={ | ||
'': 'src', | ||
}, | ||
packages=find_packages('src'), | ||
|
||
classifiers=[ | ||
'License :: OSI Approved :: Apache Software License', | ||
'Development Status :: 4 - Beta', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do we need to do to get to Release status? Hopefully its had a fair amount of use now.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. Most of these kinds of things I was kept it for not change the original library :) |
||
'Topic :: Security', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
'Intended Audience :: Developers', | ||
'Intended Audience :: Information Technology', | ||
'Programming Language :: Python :: 2', | ||
'Programming Language :: Python :: 3', | ||
'Programming Language :: Python'], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
# Zed Attack Proxy (ZAP) and its related class files. | ||
# | ||
# ZAP is an HTTP/HTTPS proxy for assessing web application security. | ||
# | ||
# Copyright 2012 ZAP development team | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2016? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oka, Same response as above :) |
||
# | ||
# 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. | ||
|
||
""" | ||
Client implementation for using the ZAP pentesting proxy remotely. | ||
""" | ||
|
||
try: | ||
# High performance json library | ||
import ujson as json | ||
except ImportError: | ||
import json | ||
|
||
import os | ||
import six | ||
import requests | ||
import requests_cache | ||
requests_cache.install_cache('zap_cache', backend="memory") | ||
|
||
# Improving Python 2 & 3 compatibility | ||
if six.PY2: | ||
from urllib import urlencode, urlopen | ||
from urlparse import urlparse, urljoin | ||
else: | ||
from urllib.parse import urlparse, urlencode, urljoin | ||
from urllib.request import urlopen | ||
|
||
from .acsrf import acsrf | ||
from .ascan import ascan | ||
from .ajaxSpider import ajaxSpider | ||
from .authentication import authentication | ||
from .autoupdate import autoupdate | ||
from .brk import brk | ||
from .context import context | ||
from .core import core | ||
from .forcedUser import forcedUser | ||
from .httpSessions import httpSessions | ||
from .importLogFiles import importLogFiles | ||
from .params import params | ||
from .pnh import pnh | ||
from .pscan import pscan | ||
from .reveal import reveal | ||
from .script import script | ||
from .search import search | ||
from .selenium import selenium | ||
from .sessionManagement import sessionManagement | ||
from .spider import spider | ||
from .users import users | ||
|
||
__docformat__ = 'restructuredtext' | ||
|
||
|
||
class ZapError(Exception): | ||
""" | ||
Base ZAP exception. | ||
""" | ||
pass | ||
|
||
|
||
class ZAPv2(object): | ||
""" | ||
Client API implementation for integrating with ZAP v2. | ||
""" | ||
|
||
# base JSON api url | ||
base = 'http://zap/JSON/' | ||
# base OTHER api url | ||
base_other = 'http://zap/OTHER/' | ||
|
||
def __init__(self, proxies=None): | ||
""" | ||
Creates an instance of the ZAP api client. | ||
|
||
Example: | ||
>>> z=ZAPv2() | ||
|
||
Example with custom proxies | ||
>>> my_proxies = {'http': 'http://10.0.1.1:9090', 'https': 'http://10.0.1.1:9090'} | ||
>>> z=ZAPv2(proxies=my_proxies) | ||
|
||
:param proxies: Dict with the scheme as key and PROXY url as value | ||
:type proxies: dict(str:str) | ||
|
||
""" | ||
if proxies is None: | ||
# Set default | ||
proxies = {'http': 'http://127.0.0.1:8080', | ||
'https': 'http://127.0.0.1:8080'} | ||
self.__proxies = proxies | ||
|
||
self.acsrf = acsrf(self) | ||
self.ajaxSpider = ajaxSpider(self) | ||
self.ascan = ascan(self) | ||
self.authentication = authentication(self) | ||
self.autoupdate = autoupdate(self) | ||
self.brk = brk(self) | ||
self.context = context(self) | ||
self.core = core(self) | ||
self.forcedUser = forcedUser(self) | ||
self.httpsessions = httpSessions(self) | ||
self.importLogFiles = importLogFiles(self) | ||
self.params = params(self) | ||
self.pnh = pnh(self) | ||
self.pscan = pscan(self) | ||
self.reveal = reveal(self) | ||
self.script = script(self) | ||
self.search = search(self) | ||
self.selenium = selenium(self) | ||
self.sessionManagement = sessionManagement(self) | ||
self.spider = spider(self) | ||
self.users = users(self) | ||
|
||
def _expect_ok(self, json_data): | ||
""" | ||
Checks that we have an OK response, else raises an exception. | ||
|
||
:param json_data: the json data to look at. | ||
:type json_data: json | ||
""" | ||
if isinstance(json_data, list) and json_data[0] == u'OK': | ||
return json_data | ||
|
||
raise ZapError(*json_data.values()) | ||
|
||
def urlopen(self, *args, **kwargs): | ||
""" | ||
Opens a url forcing the proxies to be used. | ||
|
||
:param args: all non-keyword arguments. | ||
:type args: list() | ||
|
||
:param kwarg: all non-keyword arguments. | ||
:type kwarg: dict() | ||
""" | ||
# return urlopen(*args, **kwargs).read() | ||
return requests.get(*args, proxies=self.__proxies).text | ||
|
||
def status_code(self, *args, **kwargs): | ||
""" | ||
Open a url forcing the proxies to be used. | ||
|
||
:param args: all non-keyword arguments. | ||
:type args: list() | ||
|
||
:param kwarg: all non-keyword arguments. | ||
:type kwarg: dict() | ||
""" | ||
# return urlopen(*args, **kwargs).getcode() | ||
return requests.get(*args, proxies=self.__proxies).status_code | ||
|
||
def _request(self, url, get=None): | ||
""" | ||
Shortcut for a GET request. | ||
|
||
:param url: the url to GET at. | ||
:type url: str | ||
|
||
:param get: the dictionary to turn into GET variables. | ||
:type get: dict(str:str) | ||
""" | ||
if get is None: | ||
get = {} | ||
|
||
return json.loads(self.urlopen("%s?%s" % (url, urlencode(get)))) | ||
|
||
def _request_other(self, url, get=None): | ||
""" | ||
Shortcut for an API OTHER GET request. | ||
|
||
:param url: the url to GET at. | ||
:type url: str | ||
|
||
:param get: the dictionary to turn into GET variables. | ||
:type get: dict(str:str) | ||
""" | ||
if get is None: | ||
get = {} | ||
|
||
return self.urlopen("%s?%s" % (url, urlencode(get))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an aside, how much do people look at / use the examples? I've got a more complete example (more configurable, option to use ajax spider) that we could include later..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I love the examples. Although the documentation were really good, look a real example of program it's useful. So, I put in an independent directory.
Unlike Java, with Python, these examples are a complete programs. Without more dependencies, compilers, or so on. So there're os useful for programmers.
If you have more examples it could be great.