diff --git a/docs/analytics.md b/docs/analytics.md index 5a0d643..f333a33 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -6,16 +6,18 @@ 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`. @@ -23,4 +25,6 @@ The following methods call Veracode REST APIs and return JSON. - `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) diff --git a/docs/api.md b/docs/api.md index 3a4b5b7..e8eb4fe 100644 --- a/docs/api.md +++ b/docs/api.md @@ -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' diff --git a/pyproject.toml b/pyproject.toml index b18c78e..d74dbb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = 'veracode_api_py' -version = '0.9.55' +version = '0.9.56' authors = [ {name = "Tim Jarrett", email="tjarrett@veracode.com"} ] 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' @@ -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" diff --git a/samples/reportingapi_deleted_sample.py b/samples/reportingapi_deleted_sample.py new file mode 100644 index 0000000..749fec3 --- /dev/null +++ b/samples/reportingapi_deleted_sample.py @@ -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)) \ No newline at end of file diff --git a/setup.py b/setup.py index 707d815..2d2b57d 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ 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, @@ -15,7 +15,7 @@ author = 'Tim Jarrett', author_email = 'tjarrett@veracode.com', 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' diff --git a/veracode_api_py/analytics.py b/veracode_api_py/analytics.py index 30bc1f1..036a416 100644 --- a/veracode_api_py/analytics.py +++ b/veracode_api_py/analytics.py @@ -6,7 +6,7 @@ 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" ] @@ -14,22 +14,38 @@ class Analytics(): 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)) @@ -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 diff --git a/veracode_api_py/api.py b/veracode_api_py/api.py index 18ec836..c9947a0 100644 --- a/veracode_api_py/api.py +++ b/veracode_api_py/api.py @@ -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 \ No newline at end of file