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

Fix for #91 - new reporting api scan type #92

Merged
merged 1 commit into from
Nov 25, 2024
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
14 changes: 9 additions & 5 deletions docs/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ The following methods call Veracode REST APIs and return JSON.

1. The Reporting API is available to Veracode customers by request. More information about the API is available in the [Veracode Docs](https://docs.veracode.com/r/Reporting_REST_API).

- `Analytics().create_report(report_type ('findings'),last_updated_start_date, last_updated_end_date (opt), scan_type (opt), finding_status(opt), passed_policy(opt), policy_sandbox(opt), application_id(opt), rawjson(opt))`: set up a request for a report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
- `report_type`: required, currently supports `findings` and `scans`.
- `last_updated_start_date`: required, beginning of date range for new or changed findings
- `last_updated_end_date`: optional, end of date range for new or changed findings
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'
- `Analytics().create_report(report_type ('findings'),last_updated_start_date(opt), last_updated_end_date (opt), scan_type (opt), finding_status(opt), passed_policy(opt), policy_sandbox(opt), application_id(opt), rawjson(opt), deletion_start_date(opt), deletion_end_date(opt))`: set up a request for a report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
- `report_type`: required, currently supports `findings`, `scans`, and `deletedscans`.
- `last_updated_start_date`: required for `findings` report type, beginning of date range for new or changed findings or scans
- `last_updated_end_date`: optional, end of date range for new or changed findings or scans
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'. `SCA` is only supported for the `findings` report type.
- `finding_status`: optional, 'Open' or 'Closed'. Applies only to the `findings` report.
- `passed_policy`: optional, boolean. Applies only to the `findings` report.
- `policy_sandbox`: optional, 'Policy' or 'Sandbox'
- `application_id`: optional, application ID for which to return results
- `rawjson`: optional, defaults to False. Returns full response if True, the GUID of the request if false
- `deletion_start_date`: required for `deletedscans` report type, beginning of date range for deleted scans.
- `deletion_end_date`: optional, end of date range for deleted scans.

- `Analytics().get(guid, report_type(findings))`: check the status of the report request and return the report contents when ready. Note that this method returns a tuple of `status` (string) and `results` (list); when `status` is `COMPLETED`, the `results` list will populate with results. Also, you need to specify the type of data expected by the GUID with `report_type`; this defaults to `findings`.

- `Analytics().get_findings(guid)`: check the status of a findings report request specified by `guid` and return the report contents when ready. Note that this method returns a tuple of `status` (string) and `results` (list); when `status` is `COMPLETED`, the `results` list will populate with results.

- `Analytics().get_scans(guid)`: check the status of a scans report request specified by `guid` and return the report contents when ready. Note that this method returns a tuple of `status` (string) and `results` (list); when `status` is `COMPLETED`, the `results` list will populate with results.

- `Analytics().get_deletedscans(guid)`: check the status of a deleted scans report request specified by `guid` and return the report contents when ready. Note that this method returns a tuple of `status` (string) and `results` (list); when `status` is `COMPLETED`, the `results` list will populate with results.

[All docs](docs.md)
5 changes: 2 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ As an alternative to importing individual objects into your library, you can acc

### Analytics Reporting

1. *Accessing*: The Reporting API is available to Veracode customers by request.
1. *More information*: See the [Veracode Docs](https://docs.veracode.com/r/Reporting_REST_API).
1. *See Also*: You can also access these methods from the [Analytics class](analytics.md).

- `create_analytics_report(report_type ('findings'),last_updated_start_date, last_updated_end_date (opt), scan_type (opt), finding_status(opt), passed_policy(opt), policy_sandbox(opt), application_id(opt), rawjson(opt))`: set up a request for a report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
- `report_type`: required, currently only supports `findings`
- `report_type`: required, currently only supports `findings`, `scans` and `deletedscans`
- `last_updated_start_date`: required, beginning of date range for new or changed findings
- `last_updated_end_date`: optional, end of date range for new or changed findings
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'. `SCA` is only supported for the `findings` type
- `finding_status`: optional, 'Open' or 'Closed'
- `passed_policy`: optional, boolean
- `policy_sandbox`: optional, 'Policy' or 'Sandbox'
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = 'veracode_api_py'
version = '0.9.55'
version = '0.9.56'
authors = [ {name = "Tim Jarrett", email="[email protected]"} ]
description = 'Python helper library for working with the Veracode APIs. Handles retries, pagination, and other features of the modern Veracode REST APIs.'
readme = 'README.md'
Expand All @@ -22,4 +22,4 @@ dependencies = {file = ["requirements.txt"]}
[project.urls]
"Homepage" = "https://github.com/veracode/veracode-api-py"
"Bug Tracker" = "https://github.com/veracode/veracode-api-py/issues"
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0955.tar.gz"
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0956.tar.gz"
32 changes: 32 additions & 0 deletions samples/reportingapi_deleted_sample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import time
import sys
import json
import datetime
from veracode_api_py import Analytics

wait_seconds = 15

print('Generating deleted scans report...')
theguid = Analytics().create_report(report_type="deletedscans",deletion_start_date='2024-07-01',deletion_end_date='2024-12-31')

print('Checking status for report {}...'.format(theguid))
thestatus,thescans=Analytics().get_deleted_scans(theguid)

while thestatus != 'COMPLETED':
print('Waiting {} seconds before we try again...'.format(wait_seconds))
time.sleep(wait_seconds)
print('Checking status for report {}...'.format(theguid))
thestatus,thescans=Analytics().get_deleted_scans(theguid)

recordcount = len(thescans)

print('Retrieved {} scans'.format(recordcount))

if recordcount > 0:
now = datetime.datetime.now().astimezone()
filename = 'report-{}'.format(now)
with open('{}.json'.format(filename), 'w') as outfile:
json.dump(thescans,outfile)
outfile.close()

print('Wrote {} findings to {}.json'.format(recordcount,filename))
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
setup(
name = 'veracode_api_py',
packages = ['veracode_api_py'],
version = '0.9.54',
version = '0.9.56',
license='MIT',
description = 'Python helper library for working with the Veracode APIs. Handles retries, pagination, and other features of the modern Veracode REST APIs.',
long_description = long_description,
long_description_content_type="text/markdown",
author = 'Tim Jarrett',
author_email = '[email protected]',
url = 'https://github.com/tjarrettveracode',
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0955.tar.gz',
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0956.tar.gz',
keywords = ['veracode', 'veracode-api'],
install_requires=[
'veracode-api-signing'
Expand Down
30 changes: 25 additions & 5 deletions veracode_api_py/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,46 @@
from .apihelper import APIHelper

class Analytics():
report_types = [ "findings", "scans" ]
report_types = [ "findings", "scans", "deletedscans" ]

findings_scan_types = ["Static Analysis", "Dynamic Analysis", "Manual", "SCA", "Software Composition Analysis" ]
scan_scan_types = ["Static Analysis", "Dynamic Analysis", "Manual" ]

base_url = 'appsec/v1/analytics/report'

#public methods
def create_report(self,report_type,last_updated_start_date,last_updated_end_date=None,
def create_report(self,report_type,last_updated_start_date=None,last_updated_end_date=None,
scan_type:list = [], finding_status=None,passed_policy=None,
policy_sandbox=None,application_id=None,rawjson=False):
policy_sandbox=None,application_id=None,rawjson=False, deletion_start_date=None,
deletion_end_date=None):

if report_type not in self.report_types:
raise ValueError("{} is not in the list of valid report types ({})".format(report_type,self.report_types))

report_def = { "report_type": report_type,"last_updated_start_date": last_updated_start_date }
report_def = { 'report_type': report_type }

if last_updated_start_date:
report_def['last_updated_start_date'] = last_updated_start_date
else:
if report_type in ['findings','scans']:
raise ValueError("{} report type requires a last updated start date.").format(report_type)

if last_updated_end_date:
report_def['last_updated_end_date'] = last_updated_end_date

if deletion_end_date:
report_def['deletion_end_date'] = deletion_end_date

if deletion_start_date:
report_def['deletion_start_date'] = deletion_start_date
else:
if report_type == 'deletedscans':
raise ValueError("{} report type requires a deleteion start date.").format(report_type)

if len(scan_type) > 0:
if report_type == 'findings':
valid_scan_types = self.findings_scan_types
elif report_type == 'scans':
elif report_type in [ 'scans', 'deletedscans' ]:
valid_scan_types = self.scan_scan_types
if not(self._case_insensitive_list_compare(scan_type,valid_scan_types)):
raise ValueError("{} is not in the list of valid scan types ({})".format(report_type,valid_scan_types))
Expand Down Expand Up @@ -62,6 +78,10 @@ def get_findings(self, guid: UUID):
def get_scans(self, guid: UUID):
thestatus, thescans = self.get(guid=guid,report_type='scans')
return thestatus, thescans

def get_deleted_scans(self, guid: UUID):
thestatus, thescans = self.get(guid=guid,report_type='deletedscans')
return thestatus, thescans

def get(self,guid: UUID,report_type='findings'):
# handle multiple scan types
Expand Down
20 changes: 17 additions & 3 deletions veracode_api_py/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,15 +641,29 @@ def dyn_start_scan(self, length, unit):
return DynUtils().start_scan(length,unit)

# analytics apis
def create_analytics_report(self,report_type,last_updated_start_date,last_updated_end_date=None,
def create_analytics_report(self,report_type,last_updated_start_date=None,last_updated_end_date=None,
scan_type:list = [], finding_status=None,passed_policy=None,
policy_sandbox=None,application_id=None,rawjson=False):
policy_sandbox=None,application_id=None,rawjson=False,deletion_start_date=None,
deletion_end_date=None):
return Analytics().create_report(report_type=report_type,last_updated_start_date=last_updated_start_date,
last_updated_end_date=last_updated_end_date,scan_type=scan_type,
finding_status=finding_status,passed_policy=passed_policy,
policy_sandbox=policy_sandbox,application_id=application_id,
rawjson=rawjson)
rawjson=rawjson, deletion_start_date=deletion_start_date,
deletion_end_date=deletion_end_date)

def get_analytics_report(self,guid: UUID):
status, findings = Analytics().get(guid=guid)
return status, findings

def get_analytics_findings_report(self,guid:UUID):
status, findings = Analytics().get_findings(guid=guid)
return status, findings

def get_analytics_scans(self,guid:UUID):
status, findings = Analytics().get_scans(guid=guid)
return status, findings

def get_analytics_deleted_scans(self,guid:UUID):
status, findings = Analytics().get_deleted_scans(guid=guid)
return status, findings