From f5db472154d29c13e522d6061420e6132c176a0e Mon Sep 17 00:00:00 2001 From: Aaron Butler Date: Mon, 21 Oct 2024 10:37:37 -0400 Subject: [PATCH 1/9] updated get_findings to work with csv string list --- veracode_api_py/findings.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/veracode_api_py/findings.py b/veracode_api_py/findings.py index f6f7ac0..1d0a43f 100644 --- a/veracode_api_py/findings.py +++ b/veracode_api_py/findings.py @@ -14,8 +14,15 @@ def get_findings(self,app: UUID,scantype='STATIC',annot='TRUE',request_params=No if request_params == None: request_params = {} - if scantype in ['STATIC', 'DYNAMIC', 'MANUAL','SCA']: - request_params['scan_type'] = scantype + scantypes = "" + scantype = scantype.split(',') + for st in scantype: + if st in ['STATIC', 'DYNAMIC', 'MANUAL','SCA']: + if len(scantypes) > 0: + scantypes += "," + scantypes += st + if len(scantypes) > 0: + request_params['scan_type'] = scantypes #note that scantype='ALL' will result in no scan_type parameter as in API request_params['include_annot'] = annot From 412d1fa0076261eed72415e53bb107c7418a8ce8 Mon Sep 17 00:00:00 2001 From: Aaron Butler Date: Mon, 21 Oct 2024 16:20:00 -0400 Subject: [PATCH 2/9] Added get CWE api call to findings.api --- veracode_api_py/findings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/veracode_api_py/findings.py b/veracode_api_py/findings.py index 1d0a43f..9c5ac52 100644 --- a/veracode_api_py/findings.py +++ b/veracode_api_py/findings.py @@ -166,6 +166,11 @@ def _create_match_format_policy(self, policy_findings, finding_type): 'finding': pf} for pf in policy_findings] findings.extend(thesefindings) return findings + + def get_cwe(self,cweid: int): + uri = "appsec/v1/cwes/{}".format(cweid) + + return APIHelper()._rest_request(uri,"GET") class SummaryReport(): def get_summary_report(self,app: UUID,sandbox: UUID=None, build_id: int=None): From b04c9f6018e86892c4efc30339762d7df74e1ed4 Mon Sep 17 00:00:00 2001 From: Aaron Butler Date: Fri, 25 Oct 2024 10:31:47 -0400 Subject: [PATCH 3/9] revert get cwe by ID in favor of CWE class implementation --- veracode_api_py/findings.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/veracode_api_py/findings.py b/veracode_api_py/findings.py index a40e680..c48f0ad 100644 --- a/veracode_api_py/findings.py +++ b/veracode_api_py/findings.py @@ -166,11 +166,6 @@ def _create_match_format_policy(self, policy_findings, finding_type): 'finding': pf} for pf in policy_findings] findings.extend(thesefindings) return findings - - def get_cwe(self,cweid: int): - uri = "appsec/v1/cwes/{}".format(cweid) - - return APIHelper()._rest_request(uri,"GET") class SummaryReport(): def get_summary_report(self,app: UUID,sandbox: UUID=None, build_id: int=None): From 1164fde9f144dfa5bc84fec6e25c0f8b05ef079d Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Fri, 25 Oct 2024 11:23:27 -0400 Subject: [PATCH 4/9] updates for improved getfindings --- docs/findings.md | 2 +- pyproject.toml | 4 ++-- setup.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/findings.md b/docs/findings.md index 6a7637e..d1c79fe 100644 --- a/docs/findings.md +++ b/docs/findings.md @@ -5,7 +5,7 @@ The following methods call Veracode REST APIs and return JSON. ## Findings and Annotations - `Findings().get_findings(app,scantype(opt),annot(opt),request_params(opt),sandbox(opt))`: get the findings for `app` (guid). - - `scantype`: Defaults to STATIC findings, but can be STATIC, DYNAMIC, MANUAL, SCA, or ALL (static, dynamic, manual). + - `scantype`: Defaults to STATIC findings, but can be STATIC, DYNAMIC, MANUAL, SCA, or ALL (static, dynamic, manual). You can also pass a comma-delimited string of valid scantype options. - `annot`: Defaults to TRUE but can be FALSE - `sandbox`: The guid of the sandbox in `app` for which you want findings. (Use the Sandboxes APIs to get the sandbox guid.) - `request_params`: Dictionary of additional query parameters. See the full [Findings API specification](https://help.veracode.com/r/c_findings_v2_intro) for some of the other options available. diff --git a/pyproject.toml b/pyproject.toml index 88ce021..c685ba0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = 'veracode_api_py' -version = '0.9.53' +version = '0.9.54' 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_0953.tar.gz" +"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0954.tar.gz" diff --git a/setup.py b/setup.py index 0a4674d..215552f 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name = 'veracode_api_py', packages = ['veracode_api_py'], - version = '0.9.53', + version = '0.9.54', 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_0953.tar.gz', + download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0954.tar.gz', keywords = ['veracode', 'veracode-api'], install_requires=[ 'veracode-api-signing' From 87844bbee2d895eac80cc7df6d3c5368a56c8125 Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Thu, 7 Nov 2024 15:08:11 -0500 Subject: [PATCH 5/9] Update to latest veracode-api-signing --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0827702..d56790e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ requests>=2.32.0 -veracode-api-signing>=22.3.0 +veracode-api-signing>=24.11.0 urllib3>= 2.2.2 Pygments>= 2.9.0 idna>=3.7 From e87d769af2f3463054cc42f6a5fa62d19d66cce8 Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Thu, 7 Nov 2024 15:18:30 -0500 Subject: [PATCH 6/9] Additional fix for #90 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d56790e..befdf39 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ requests>=2.32.0 veracode-api-signing>=24.11.0 -urllib3>= 2.2.2 Pygments>= 2.9.0 idna>=3.7 certifi>=2024.7.4 \ No newline at end of file From 9b8b98869507bab4699922584f1d485c6675ada9 Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Thu, 7 Nov 2024 15:21:02 -0500 Subject: [PATCH 7/9] bump to new version for release --- pyproject.toml | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c685ba0..b18c78e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = 'veracode_api_py' -version = '0.9.54' +version = '0.9.55' 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_0954.tar.gz" +"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0955.tar.gz" diff --git a/setup.py b/setup.py index 215552f..707d815 100644 --- a/setup.py +++ b/setup.py @@ -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_0954.tar.gz', + download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0955.tar.gz', keywords = ['veracode', 'veracode-api'], install_requires=[ 'veracode-api-signing' From 24b53ba0e97cb3dde753ed1037ea93a5848c0653 Mon Sep 17 00:00:00 2001 From: Tim Jarrett Date: Mon, 25 Nov 2024 16:03:33 -0500 Subject: [PATCH 8/9] Fix for #91 - new reporting api scan type --- docs/analytics.md | 14 +++++++---- docs/api.md | 5 ++-- pyproject.toml | 4 ++-- samples/reportingapi_deleted_sample.py | 32 ++++++++++++++++++++++++++ setup.py | 4 ++-- veracode_api_py/analytics.py | 30 ++++++++++++++++++++---- veracode_api_py/api.py | 20 +++++++++++++--- 7 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 samples/reportingapi_deleted_sample.py 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 From fd7cacd5787763506cbbd1e69e4eb5ea0dafe51f Mon Sep 17 00:00:00 2001 From: Ricardo Pereira - Veracode <20283870+cadonuno@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:35:41 +0000 Subject: [PATCH 9/9] Removing unnecessary 'profile' node from create/update custom role --- veracode_api_py/identity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/veracode_api_py/identity.py b/veracode_api_py/identity.py index 2f9224e..dd1bc9d 100644 --- a/veracode_api_py/identity.py +++ b/veracode_api_py/identity.py @@ -298,7 +298,7 @@ def _create_or_update(self, method, role_name, role_description, role_guid: UUID if len(child_roles) > 0: role_def['child_roles'] = child_roles - payload = json.dumps({"profile": role_def}) + payload = json.dumps(role_def) return APIHelper()._rest_request(uri,httpmethod,body=payload) class Permissions():