Example #1
0
    def can_access_api(self):
        """
        :return: True when we can access the REST API
        """
        try:
            version_dict = self.get_version()
        except Exception as e:
            msg = 'An exception was raised when connecting to REST API: "%s"'
            raise APIException(msg % e)
        else:
            """
            This is an example response from the REST API
            {
                "branch": "develop",
                "dirty": "Yes",
                "revision": "f1cae98161 - 24 Jun 2015 16:29",
                "version": "1.7.2"
            }
            """
            if 'version' in version_dict:
                # Yup, this looks like a w3af REST API
                return True

            msg = 'Unexpected HTTP response when connecting to REST API'
            raise APIException(msg)
Example #2
0
    def get_findings(self):
        code, data = self.conn.send_request('/kb/', method='GET')

        if code != 200:
            raise APIException('Failed to retrieve findings')

        findings = data.get('items', None)

        if findings is None:
            raise APIException('Failed to retrieve findings')

        return [Finding(self.conn, f['id']) for f in findings]
Example #3
0
    def get_exceptions(self):
        url = '/scans/%s/exceptions/' % self.scan_id
        code, data = self.conn.send_request(url, method='GET')

        if code != 200:
            raise APIException('Failed to retrieve exceptions')

        exceptions = data.get('items', None)

        if exceptions is None:
            raise APIException('Failed to retrieve exceptions')

        return [ScannerException(self.conn, e['href']) for e in exceptions]
Example #4
0
    def get_urls(self):
        url = '/scans/%s/urls/' % self.scan_id
        code, data = self.conn.send_request(url, method='GET')

        if code != 200:
            raise APIException('Failed to retrieve urls')

        urls = data.get('items', None)

        if urls is None:
            raise APIException('Failed to retrieve urls')

        return urls
Example #5
0
    def get_fuzzable_requests(self):
        url = '/scans/%s/fuzzable-requests/' % self.scan_id
        code, data = self.conn.send_request(url, method='GET')

        if code != 200:
            raise APIException('Failed to retrieve fuzzable requests')

        encoded_fuzzable_requests = data.get('items', None)

        if encoded_fuzzable_requests is None:
            raise APIException('Failed to retrieve fuzzable requests')

        return [base64.b64decode(fr) for fr in encoded_fuzzable_requests]
Example #6
0
    def send_request(self, path, json_data=None, method='GET'):
        full_url = urlparse.urljoin(self.api_url, path)

        if method == 'GET':
            response = self.session.get(full_url, timeout=self.timeout)

        elif method == 'DELETE':
            response = self.session.delete(full_url, timeout=self.timeout)

        elif method == 'POST':
            data = json.dumps(json_data)
            response = self.session.post(full_url,
                                         data=data,
                                         timeout=self.timeout)

        else:
            raise ValueError('Invalid HTTP method: "%s"' % method)

        try:
            json_data = response.json()
        except ValueError:
            msg = ('REST API service did not return JSON, if this issue'
                   ' persists please create an issue in the w3af framework'
                   ' repository at %s. The response body starts with: "%s"')
            raise APIException(msg % (ISSUE_URL, response.content[:20]))

        pretty_json = json.dumps(json_data, indent=4)
        msg = 'Received %s HTTP response from the wire:\n%s'
        api_logger.debug(msg % (response.status_code, pretty_json))

        #
        # Error handling
        #
        if response.status_code in API_EXCEPTIONS:
            error = json_data.get('message', None)
            exception_klass = API_EXCEPTIONS.get(response.status_code)

            if error is not None:
                raise exception_klass(error)
            else:
                msg = ('REST API service did not return the expected "message"'
                       ' attribute for the %s response. Please create a new'
                       ' issue in the w3af framework repository at %s with'
                       ' this JSON data:\n\n%s')
                dump = json.dumps(json_data, indent=4)
                args = (response.status_code, ISSUE_URL, dump)
                raise APIException(msg % args)

        return response.status_code, json_data
Example #7
0
    def get_page(self, page_number):
        url = '/scans/%s/log?page=%s' % (self.scan_id, page_number)
        code, page = self.conn.send_request(url, method='GET')

        if code != 200:
            raise APIException('Could not retrieve log entry list')

        entries = page.get('entries', None)

        if entries is None:
            raise APIException('Could not retrieve log entries attribute')

        for entry_dict in entries:
            yield LogEntry(entry_dict['type'], entry_dict['message'],
                           entry_dict['time'], entry_dict['severity'])
Example #8
0
    def get_exceptions(self):
        url = '/scans/%s/exceptions/' % self.scan_id
        code, data = self.conn.send_request(url, method='GET')

        if code != 200:
            message = data.get('message', 'None')
            args = (code, message)
            raise APIException('Failed to retrieve exceptions. Received HTTP'
                               ' response code %s. Message: "%s"' % args)

        exceptions = data.get('items', None)

        if exceptions is None:
            raise APIException('Failed to retrieve exceptions')

        return [ScannerException(self.conn, e['href']) for e in exceptions]
Example #9
0
    def update(self):
        code, data = self.conn.send_request(self.resource_href, method='GET')

        if code != 200:
            msg = 'Could not retrieve resource detail "%s"'
            raise APIException(msg % self.resource_href)

        self._data = data
        return self._data
Example #10
0
 def can_access_api(self):
     """
     :return: True when we can access the REST API
     """
     try:
         version_dict = self.get_version()
     except Exception, e:
         msg = 'An exception was raised when connecting to REST API: "%s"'
         raise APIException(msg % e)
Example #11
0
    def send_request(self, path, json_data=None, method='GET'):
        full_url = urlparse.urljoin(self.api_url, path)

        if method == 'GET':
            response = self.session.get(full_url, timeout=self.timeout)

        elif method == 'DELETE':
            response = self.session.delete(full_url, timeout=self.timeout)

        elif method == 'POST':
            data = json.dumps(json_data)
            response = self.session.post(full_url,
                                         data=data,
                                         timeout=self.timeout)

        else:
            raise ValueError('Invalid HTTP method: "%s"' % method)

        try:
            json_data = response.json()
        except ValueError:
            msg = ('REST API service did not return JSON, if this issue'
                   ' persists please create an issue in the w3af framework'
                   ' repository at %s')
            raise APIException(msg % ISSUE_URL)

        pretty_json = json.dumps(json_data, indent=4)
        msg = 'Received %s HTTP response from the wire:\n%s'
        api_logger.debug(msg % (response.status_code, pretty_json))

        #
        # Error handling
        #
        if response.status_code in (400, 403, 404):
            error = json_data.get('error', None)
            if error is not None:
                raise APIException(error)
            else:
                msg = ('REST API service did not return the expected "error"'
                       ' attribute for the %s response. Please create a new'
                       ' issue in the w3af framework repository at %s')
                raise APIException(msg % (response.status_code, ISSUE_URL))

        return response.status_code, json_data
Example #12
0
    def get_page(self, page_number):
        """
        :yield: Log entries for the given page number
        """
        url = '/scans/%s/log?page=%s' % (self.scan_id, page_number)
        code, page = self.conn.send_request(url, method='GET')

        if code != 200:
            message = page.get('message', 'None')
            args = (code, message)
            raise APIException('Failed to retrieve scan log. Received HTTP'
                               ' response code %s. Message: "%s"' % args)

        entries = page.get('entries', None)

        if entries is None:
            raise APIException('Could not retrieve log entries attribute')

        for entry_dict in entries:
            yield LogEntry.from_entry_dict(entry_dict)
Example #13
0
    def get_status(self):
        assert self.scan_id is not None, 'No scan_id has been set'

        code, data = self.conn.send_request('/scans/%s/status' % self.scan_id,
                                            method='GET')

        if code != 200:
            raise APIException('Failed to retrieve scan status. Received HTTP'
                               ' response code %s' % code)

        return data
Example #14
0
    def start(self, scan_profile, target_urls):
        data = {'scan_profile': scan_profile, 'target_urls': target_urls}
        code, data = self.conn.send_request('/scans/',
                                            json_data=data,
                                            method='POST')

        if code != 201:
            raise APIException('Failed to start the new scan')

        api_logger.debug('Scan successfully started using REST API')
        self.scan_id = data['id']
Example #15
0
    def get_by_start_id(self, start_id):
        """
        :yield: Log entries starting from :start_id: and ending 200 entries
                after. In most cases easier to call than the paginate one
                because there is no need to keep track of the already read
                entries in a specific page.
        """
        url = '/scans/%s/log?id=%s' % (self.scan_id, start_id)
        code, page = self.conn.send_request(url, method='GET')

        if code != 200:
            raise APIException('Could not retrieve log entry list')

        entries = page.get('entries', None)

        if entries is None:
            raise APIException('Could not retrieve log entries attribute')

        for entry_dict in entries:
            yield LogEntry(entry_dict['type'], entry_dict['message'],
                           entry_dict['time'], entry_dict['severity'],
                           entry_dict['id'])
Example #16
0
    def get_status(self):
        assert self.scan_id is not None, 'No scan_id has been set'

        code, data = self.conn.send_request('/scans/%s/status' % self.scan_id,
                                            method='GET')

        if code != 200:
            message = data.get('message', 'None')
            args = (code, message)
            raise APIException('Failed to retrieve scan status. Received HTTP'
                               ' response code %s. Message: "%s"' % args)

        return data
Example #17
0
    def get_scans(self):
        """
        :return: A list with all the Scan instances available in the remote API
        """
        code, data = self.send_request('/scans/', method='GET')

        if code != 200:
            msg = 'Failed to retrieve scans. Unexpected code %s'
            raise APIException(msg % code)

        scans = data.get('items', None)

        if scans is None:
            raise APIException('Failed to retrieve scans, no "items" in JSON.')

        scan_instances = []
        for scan_json in scans:
            scan_id = scan_json['id']
            scan_status = scan_json['status']
            scan = Scan(self, scan_id=scan_id, status=scan_status)
            scan_instances.append(scan)

        return scan_instances
Example #18
0
    def finding_data(self):
        """
        Cached access to the KB so a piece of code that accesses this finding
        doesn't perform one HTTP request to the REST API for each attribute

        :return: The JSON data
        """
        code, data = self.conn.send_request('/kb/%s' % self.finding_id,
                                            method='GET')

        if code != 200:
            raise APIException('Could not retrieve finding detail')

        return data
Example #19
0
    def start(self, scan_profile, target_urls):
        data = {'scan_profile': scan_profile, 'target_urls': target_urls}
        code, data = self.conn.send_request('/scans/',
                                            json_data=data,
                                            method='POST')

        if code != 201:
            message = data.get('message', 'None')
            args = (code, message)
            raise APIException('Failed to start the new scan. Received HTTP'
                               ' response code %s. Message: "%s"' % args)

        api_logger.debug('Scan successfully started using REST API')
        self.scan_id = data['id']
Example #20
0
    def get_by_start_id(self, start_id):
        """
        :yield: Log entries starting from :start_id: and ending 200 entries
                after. In most cases easier to call than the paginate one
                because there is no need to keep track of the already read
                entries in a specific page.
        """
        url = '/scans/%s/log?id=%s' % (self.scan_id, start_id)
        code, page = self.conn.send_request(url, method='GET')

        if code != 200:
            message = page.get('message', 'None')
            args = (code, message)
            raise APIException('Failed to retrieve scan log. Received HTTP'
                               ' response code %s. Message: "%s"' % args)

        entries = page.get('entries', None)

        if entries is None:
            raise APIException('Could not retrieve log entries attribute')

        for entry_dict in entries:
            yield LogEntry.from_entry_dict(entry_dict)
Example #21
0
    def from_entry_dict(cls, entry_dict):
        """
        This is a "constructor" for the LogEntry class.

        :param entry_dict: A dict we get from the REST API
        :return: An instance of LogEntry.
        """
        # Debug helper
        # https://circleci.com/gh/andresriancho/w3af-api-docker/30
        try:
            _type = entry_dict['type']
            _id = entry_dict['id']
            _time = entry_dict['time']
            message = entry_dict['message']
            severity = entry_dict['severity']
        except KeyError:
            msg = ('Missing expected log entry attribute. Log entry'
                   ' object is:\n\n%s')
            raise APIException(msg % json.dumps(entry_dict, indent=4))

        return cls(_type, message, _time, severity, _id)
Example #22
0
        else:
            """
            This is an example response from the REST API
            {
                "branch": "develop",
                "dirty": "Yes",
                "revision": "f1cae98161 - 24 Jun 2015 16:29",
                "version": "1.7.2"
            }
            """
            if 'version' in version_dict:
                # Yup, this looks like a w3af REST API
                return True

            msg = 'Unexpected HTTP response when connecting to REST API'
            raise APIException(msg)

    def get_version(self):
        code, version_dict = self.send_request('/version')
        return version_dict

    def set_verbose(self, verbose):
        # Get level based on verbose boolean
        level = logging.DEBUG if verbose else logging.CRITICAL

        # Configure my own logger
        api_logger.setLevel(level=level)

        ch = logging.StreamHandler()
        ch.setLevel(level)