Skip to content

Commit d069ce9

Browse files
Merge branch 'main' of https://github.com/veracode/veracode-api-py into identity-roles
2 parents ffcc472 + bb6330c commit d069ce9

File tree

10 files changed

+74
-42
lines changed

10 files changed

+74
-42
lines changed

README.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,30 @@ Not an official Veracode product. Heavily based on original work by [CTCampbell]
88

99
Install from pypi:
1010

11-
pypi veracode_api_py
11+
pip install veracode-api-py
1212

13-
(Optional) Save Veracode API credentials in `~/.veracode/credentials`
13+
### Authenticating from a developer machine
14+
15+
Save Veracode API credentials in `~/.veracode/credentials`
1416

1517
[default]
1618
veracode_api_key_id = <YOUR_API_KEY_ID>
1719
veracode_api_key_secret = <YOUR_API_KEY_SECRET>
1820

21+
### Authenticating from a pipeline
22+
23+
Set Veracode API credentials as environment variables.
24+
25+
export VERACODE_API_KEY_ID=<YOUR_API_KEY_ID>
26+
export VERACODE_API_KEY_SECRET=<YOUR_API_KEY_SECRET>
27+
28+
### Authenticating through a proxy
29+
30+
To use this library (or a script based on it) with a proxy server, set environment variables with the address of the proxy:
31+
32+
export HTTP_PROXY='http://10.10.10.10:8000'
33+
export HTTPS_PROXY='http://10.10.10.10:1212'
34+
1935
## Use in your applications
2036

2137
Import VeracodeAPI or one of the individual API classes into your code and call the methods. Most methods return JSON or XML depending on the underlying API.
@@ -28,11 +44,6 @@ For detailed documentation on the available methods, please see the [veracode-ap
2844

2945
## Notes
3046

31-
1. Different API calls require different roles. Consult the [Veracode Docs](https://docs.veracode.com/r/c_role_permissions).
47+
1. Different API calls require different roles or permissions. Consult the [Veracode Docs](https://docs.veracode.com/r/c_role_permissions).
3248
2. This library does not include a complete set of Veracode API methods. In particular, it only provides a handful of XML API methods.
33-
3. To use this library (or a script based on it) with a proxy server, you can set environment variables with the addresses of the proxies:
34-
35-
- `export HTTP_PROXY='http://10.10.10.10:8000'`
36-
- `export HTTPS_PROXY='http://10.10.10.10:1212'`
37-
38-
4. Contributions are welcome. See the [Contributions guidelines](CONTRIBUTING.md).
49+
3. Contributions are welcome. See the [Contributions guidelines](CONTRIBUTING.md).

docs/analytics.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@ The following methods call Veracode REST APIs and return JSON.
77
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).
88

99
- `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:
10-
- `report_type`: required, currently only supports `findings`
10+
- `report_type`: required, currently supports `findings` and `scans`.
1111
- `last_updated_start_date`: required, beginning of date range for new or changed findings
1212
- `last_updated_end_date`: optional, end of date range for new or changed findings
1313
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'
14-
- `finding_status`: optional, 'Open' or 'Closed'
15-
- `passed_policy`: optional, boolean
14+
- `finding_status`: optional, 'Open' or 'Closed'. Applies only to the `findings` report.
15+
- `passed_policy`: optional, boolean. Applies only to the `findings` report.
1616
- `policy_sandbox`: optional, 'Policy' or 'Sandbox'
1717
- `application_id`: optional, application ID for which to return results
1818
- `rawjson`: optional, defaults to False. Returns full response if True, the GUID of the request if false
1919

20-
- `Analytics().get(guid)`: 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.
20+
- `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`.
21+
22+
- `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.
23+
24+
- `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.
2125

2226
[All docs](docs.md)

docs/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ As an alternative to importing individual objects into your library, you can acc
7474

7575
*See also*: You can also access this method from the [SummaryReport class](findings.md#summary-report).
7676

77-
- `get_summary_report(app,sandbox(opt))`: get the summary report for `app` (guid) or its `sandbox` (guid).
77+
- `get_summary_report(app,sandbox(opt), build_id(opt))`: get the summary report for `app` (guid) or its `sandbox` (guid). Optionally specify a `build_id` to get a summary report for an older scan.
7878

7979
## Applications and Policy
8080

docs/findings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The following methods call Veracode REST APIs and return JSON.
2020

2121
## Summary Report
2222

23-
- `SummaryReport().get_summary_report(app,sandbox(opt))`: get the summary report for `app` (guid) or its `sandbox` (guid).
23+
- `SummaryReport().get_summary_report(app,sandbox(opt), build_id(opt))`: get the summary report for `app` (guid) or its `sandbox` (guid). Optionally specify a `build_id` to get a summary report for an older scan.
2424

2525
## Manual Testing
2626

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = 'veracode_api_py'
3-
version = '0.9.49'
3+
version = '0.9.51'
44
authors = [ {name = "Tim Jarrett", email="[email protected]"} ]
55
description = 'Python helper library for working with the Veracode APIs. Handles retries, pagination, and other features of the modern Veracode REST APIs.'
66
readme = 'README.md'
@@ -22,4 +22,4 @@ dependencies = {file = ["requirements.txt"]}
2222
[project.urls]
2323
"Homepage" = "https://github.com/veracode/veracode-api-py"
2424
"Bug Tracker" = "https://github.com/veracode/veracode-api-py/issues"
25-
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0949.tar.gz"
25+
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0951.tar.gz"

requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
requests>=2.31.0
1+
requests>=2.32.0
22
veracode-api-signing>=22.3.0
33
urllib3>= 2.2.2
44
Pygments>= 2.9.0
5-
idna>=3.7
5+
idna>=3.7
6+
certifi>=2024.7.4

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
setup(
88
name = 'veracode_api_py',
99
packages = ['veracode_api_py'],
10-
version = '0.9.49',
10+
version = '0.9.51',
1111
license='MIT',
1212
description = 'Python helper library for working with the Veracode APIs. Handles retries, pagination, and other features of the modern Veracode REST APIs.',
1313
long_description = long_description,
1414
long_description_content_type="text/markdown",
1515
author = 'Tim Jarrett',
1616
author_email = '[email protected]',
1717
url = 'https://github.com/tjarrettveracode',
18-
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0949.tar.gz',
18+
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0951.tar.gz',
1919
keywords = ['veracode', 'veracode-api'],
2020
install_requires=[
2121
'veracode-api-signing'

veracode_api_py/analytics.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,34 @@
66
from .apihelper import APIHelper
77

88
class Analytics():
9-
report_types = [ "findings" ]
9+
report_types = [ "findings", "scans" ]
1010

11-
scan_types = ["Static Analysis", "Dynamic Analysis", "Manual", "SCA", "Software Composition Analysis" ]
11+
findings_scan_types = ["Static Analysis", "Dynamic Analysis", "Manual", "SCA", "Software Composition Analysis" ]
12+
scan_scan_types = ["Static Analysis", "Dynamic Analysis", "Manual" ]
1213

1314
base_url = 'appsec/v1/analytics/report'
1415

1516
#public methods
1617
def create_report(self,report_type,last_updated_start_date,last_updated_end_date=None,
1718
scan_type:list = [], finding_status=None,passed_policy=None,
1819
policy_sandbox=None,application_id=None,rawjson=False):
19-
#TODO validate report_type, add scan_type
20+
21+
if report_type not in self.report_types:
22+
raise ValueError("{} is not in the list of valid report types ({})".format(report_type,self.report_types))
2023

2124
report_def = { "report_type": report_type,"last_updated_start_date": last_updated_start_date }
2225

2326
if last_updated_end_date:
2427
report_def['last_updated_end_date'] = last_updated_end_date
2528

2629
if len(scan_type) > 0:
27-
# validate against self.scan_types
28-
if self._case_insensitive_list_compare(scan_type,self.scan_types):
29-
report_def['scan_type'] = scan_type
30-
else:
31-
print("Invalid scan_type provided. Valid types are {}".format(self.scan_types))
32-
return
30+
if report_type == 'findings':
31+
valid_scan_types = self.findings_scan_types
32+
elif report_type == 'scans':
33+
valid_scan_types = self.scan_scan_types
34+
if not(self._case_insensitive_list_compare(scan_type,valid_scan_types)):
35+
raise ValueError("{} is not in the list of valid scan types ({})".format(report_type,valid_scan_types))
36+
report_def['scan_type'] = scan_type
3337

3438
if finding_status:
3539
report_def['finding_status'] = finding_status
@@ -51,19 +55,27 @@ def create_report(self,report_type,last_updated_start_date,last_updated_end_date
5155
else:
5256
return response['_embedded']['id'] #we will usually just need the guid so we can come back and fetch the report
5357

54-
def get(self,guid: UUID):
58+
def get_findings(self, guid: UUID):
59+
thestatus, thefindings = self.get(guid=guid,report_type='findings')
60+
return thestatus, thefindings
61+
62+
def get_scans(self, guid: UUID):
63+
thestatus, thescans = self.get(guid=guid,report_type='scans')
64+
return thestatus, thescans
65+
66+
def get(self,guid: UUID,report_type='findings'):
67+
# handle multiple scan types
5568
uri = "{}/{}".format(self.base_url,guid)
56-
theresponse = APIHelper()._rest_paged_request(uri,"GET","findings",{},fullresponse=True)
69+
theresponse = APIHelper()._rest_paged_request(uri,"GET",report_type,{},fullresponse=True)
5770
thestatus = theresponse.get('_embedded',{}).get('status','')
58-
thefindings = theresponse.get('_embedded',{}).get('findings',{})
59-
return thestatus, thefindings
71+
thebody = theresponse.get('_embedded',{}).get(report_type,{})
72+
return thestatus, thebody
6073

6174
#helper methods
6275
def _case_insensitive_list_compare(self,input_list:list, target_list:list):
6376
input_set = self._lowercase_set_from_list(input_list)
6477
target_set = self._lowercase_set_from_list(target_list)
6578
return target_set.issuperset(input_set)
6679

67-
6880
def _lowercase_set_from_list(self,thelist:list):
6981
return set([x.lower() for x in thelist])

veracode_api_py/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ def get_static_flaw_info(self, app: UUID, issueid, sandbox: UUID = None):
176176
def get_dynamic_flaw_info(self, app: UUID, issueid):
177177
return Findings().get_dynamic_flaw_info(app, issueid)
178178

179-
def get_summary_report(self, app: UUID, sandbox=None):
180-
return SummaryReport().get_summary_report(app, sandbox)
179+
def get_summary_report(self, app: UUID, sandbox=None, build_id=None):
180+
return SummaryReport().get_summary_report(app=app, sandbox=sandbox, build_id=build_id)
181181

182182
def add_annotation(self, app: UUID, issue_list, comment, action, sandbox: UUID = None):
183183
return Findings().add_annotation(app, issue_list, comment, action, sandbox)

veracode_api_py/findings.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,17 @@ def _create_match_format_policy(self, policy_findings, finding_type):
161161
return findings
162162

163163
class SummaryReport():
164-
def get_summary_report(self,app: UUID,sandbox: UUID=None):
164+
def get_summary_report(self,app: UUID,sandbox: UUID=None, build_id: int=None):
165+
uri = "appsec/v2/applications/{}/summary_report".format(app)
166+
167+
params = {}
165168
if sandbox != None:
166-
uri = "appsec/v2/applications/{}/summary_report?context={}".format(app,sandbox)
167-
else:
168-
uri = "appsec/v2/applications/{}/summary_report".format(app)
169+
params['context'] = sandbox
169170

170-
return APIHelper()._rest_request(uri,"GET")
171+
if build_id != None:
172+
params['build_id'] = build_id
173+
174+
return APIHelper()._rest_request(uri,"GET", params=params)
171175

172176
class ManualScans():
173177
def get_for_app(self,appid: UUID):

0 commit comments

Comments
 (0)