Skip to content
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

Add audit.open_api_auth plugin #17363

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
67bee14
Update open_api to store a copy of SpecificationHandler in the kb
artem-smotrakov Oct 18, 2018
d46c5ab
Added first draft of open_api_auth.py
artem-smotrakov Oct 18, 2018
1d010be
Updated open_api_auth plugin to check and remove auth info from requests
artem-smotrakov Oct 19, 2018
47a4fde
Added test_open_api_auth.py and fixed issues in open_api_auth
artem-smotrakov Oct 22, 2018
5dec3cd
open_api_auth should take into account 'security' sections of operations
artem-smotrakov Oct 24, 2018
f391035
Updated mock responses in TestOpenAPIAuth
artem-smotrakov Oct 24, 2018
846fdf7
NewPet and Error should be objects in petstore_with_security.yaml
artem-smotrakov Oct 24, 2018
3430223
open_api_auth should take into account 'security' sections of operati…
artem-smotrakov Oct 24, 2018
d613f4e
Check response codes in open_api_auth plugin
artem-smotrakov Oct 24, 2018
cd5b21f
Split tests in test_open_api_auth.py
artem-smotrakov Oct 25, 2018
1b9f54c
Describe open_api_auth plugin
artem-smotrakov Oct 25, 2018
c3c6454
Describe methods in open_api_auth plugin
artem-smotrakov Oct 26, 2018
e154daf
Describe new methods in OpenAPI class
artem-smotrakov Oct 26, 2018
65dbf5d
Describe new methods in SpecificationHandler
artem-smotrakov Oct 26, 2018
1b83286
Merge remote-tracking branch 'upstream/develop' into openapi-auth-plugin
artem-smotrakov Nov 12, 2018
f335295
Better checks for API spec in audit.open_api_auth plugin
artem-smotrakov Nov 12, 2018
3edc480
Don't use _spec_is_good flag in audit.open_api_auth plugin
artem-smotrakov Nov 13, 2018
ce56f14
audit.open_api_auth should depend on crawl.open_api plugin
artem-smotrakov Nov 13, 2018
b9283fb
audit.open_api_auth plugin should expect multiple API specs
artem-smotrakov Nov 13, 2018
d1fef76
Added TestOpenAPIAuthMultipleSpecs
artem-smotrakov Nov 13, 2018
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
32 changes: 28 additions & 4 deletions w3af/core/data/parsers/doc/open_api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class OpenAPI(BaseParser):
The parser only returns interesting results for get_forms(), where all
FuzzableRequests associated with REST API calls are returned.

The parser stores a couple of things to the kb which then can be used by other plugins:
* An instance of SpecificationHandler which provides the API specification.
* A mapping { method, url -> operation id } which allows to find the operation
which is associated with a fuzzable request.

[0] https://www.openapis.org/
[1] https://github.com/Yelp/bravado-core

Expand All @@ -67,6 +72,8 @@ def __init__(self, http_response, no_validation=False, discover_fuzzable_headers
self.api_calls = []
self.no_validation = no_validation
self.discover_fuzzable_headers = discover_fuzzable_headers
self.specification_handler = None
self.request_to_operation_id = {}

@staticmethod
def content_type_match(http_resp):
Expand Down Expand Up @@ -136,20 +143,36 @@ def can_parse(http_resp):
# sure until we really parse it in OpenAPI.parse()
return True

def get_specification_handler(self):
"""
:return: An instance of SpecificationHandler which was used by the parser.
"""
return self.specification_handler

def get_request_to_operation_id(self):
"""
:return: A mapping { method, url -> operation id }
"""
return self.request_to_operation_id

def parse(self):
"""
Extract all the API endpoints using the bravado Open API parser.

The method also looks for all parameters which are passed to endpoints via headers,
and stores them in to the fuzzable request
"""
specification_handler = SpecificationHandler(self.get_http_response(),
self.no_validation)
self.specification_handler = SpecificationHandler(self.get_http_response(),
self.no_validation)

for data in specification_handler.get_api_information():
self.request_to_operation_id = {}
for data in self.specification_handler.get_api_information():
try:
request_factory = RequestFactory(*data)
fuzzable_request = request_factory.get_fuzzable_request(self.discover_fuzzable_headers)

key = '%s|%s' % (fuzzable_request.get_method(), fuzzable_request.get_url())
self.request_to_operation_id[key] = data[4].operation_id
except Exception, e:
#
# This is a strange situation because parsing of the OpenAPI
Expand Down Expand Up @@ -180,7 +203,8 @@ def parse(self):

self.api_calls.append(fuzzable_request)

def _should_audit(self, fuzzable_request):
@staticmethod
def _should_audit(fuzzable_request):
"""
We want to make sure that w3af doesn't delete all the items from the
REST API, so we ignore DELETE calls.
Expand Down
42 changes: 34 additions & 8 deletions w3af/core/data/parsers/doc/open_api/specification.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
"""
requests.py
specification.py

Copyright 2017 Andres Riancho

Expand Down Expand Up @@ -50,28 +50,54 @@


class SpecificationHandler(object):

def __init__(self, http_response, no_validation=False):
self.http_response = http_response
self.spec = None
self.no_validation = no_validation
self.spec = None

def get_http_response(self):
return self.http_response

def shallow_copy(self):
"""
:return: A copy of the SpecificationHandler
which doesn't contain a Spec instance.
"""
return SpecificationHandler(self.http_response, self.no_validation)

def get_spec(self):
"""
:return: An instance of Spec (Bravado)
which was created by this SpecificationHandler.
"""
return self.spec

def parse(self):
"""
Parse an API specification provided in the HTTP response.

:return: An instance of Spec (Bravado).
"""
spec_dict = self._load_spec_dict()
if spec_dict is None:
return None

self.spec = self._parse_spec_from_dict(spec_dict)

return self.spec

def get_api_information(self):
"""
This is the main method.

:yield: All the information we were able to collect about each API endpoint
This includes the specification, parameters, URL, etc.
"""
spec_dict = self._load_spec_dict()

if spec_dict is None:
return
if not self.spec:
self.parse()

self.spec = self._parse_spec_from_dict(spec_dict)
if self.spec is None:
if not self.spec:
return

for api_resource_name, resource in self.spec.resources.items():
Expand Down
Loading