Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/veracode/veracode-api-py in…
Browse files Browse the repository at this point in the history
…to identity-roles
  • Loading branch information
tjarrettveracode committed Oct 8, 2024
2 parents ffcc472 + bb6330c commit d069ce9
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 42 deletions.
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,30 @@ Not an official Veracode product. Heavily based on original work by [CTCampbell]

Install from pypi:

pypi veracode_api_py
pip install veracode-api-py

(Optional) Save Veracode API credentials in `~/.veracode/credentials`
### Authenticating from a developer machine

Save Veracode API credentials in `~/.veracode/credentials`

[default]
veracode_api_key_id = <YOUR_API_KEY_ID>
veracode_api_key_secret = <YOUR_API_KEY_SECRET>

### Authenticating from a pipeline

Set Veracode API credentials as environment variables.

export VERACODE_API_KEY_ID=<YOUR_API_KEY_ID>
export VERACODE_API_KEY_SECRET=<YOUR_API_KEY_SECRET>

### Authenticating through a proxy

To use this library (or a script based on it) with a proxy server, set environment variables with the address of the proxy:

export HTTP_PROXY='http://10.10.10.10:8000'
export HTTPS_PROXY='http://10.10.10.10:1212'

## Use in your applications

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.
Expand All @@ -28,11 +44,6 @@ For detailed documentation on the available methods, please see the [veracode-ap

## Notes

1. Different API calls require different roles. Consult the [Veracode Docs](https://docs.veracode.com/r/c_role_permissions).
1. Different API calls require different roles or permissions. Consult the [Veracode Docs](https://docs.veracode.com/r/c_role_permissions).
2. This library does not include a complete set of Veracode API methods. In particular, it only provides a handful of XML API methods.
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:

- `export HTTP_PROXY='http://10.10.10.10:8000'`
- `export HTTPS_PROXY='http://10.10.10.10:1212'`

4. Contributions are welcome. See the [Contributions guidelines](CONTRIBUTING.md).
3. Contributions are welcome. See the [Contributions guidelines](CONTRIBUTING.md).
12 changes: 8 additions & 4 deletions docs/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ 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 only supports `findings`
- `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'
- `finding_status`: optional, 'Open' or 'Closed'
- `passed_policy`: optional, boolean
- `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

- `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.
- `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.

[All docs](docs.md)
2 changes: 1 addition & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ As an alternative to importing individual objects into your library, you can acc

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

- `get_summary_report(app,sandbox(opt))`: get the summary report for `app` (guid) or its `sandbox` (guid).
- `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.

## Applications and Policy

Expand Down
2 changes: 1 addition & 1 deletion docs/findings.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The following methods call Veracode REST APIs and return JSON.

## Summary Report

- `SummaryReport().get_summary_report(app,sandbox(opt))`: get the summary report for `app` (guid) or its `sandbox` (guid).
- `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.

## Manual Testing

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.49'
version = '0.9.51'
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_0949.tar.gz"
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0951.tar.gz"
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
requests>=2.31.0
requests>=2.32.0
veracode-api-signing>=22.3.0
urllib3>= 2.2.2
Pygments>= 2.9.0
idna>=3.7
idna>=3.7
certifi>=2024.7.4
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.49',
version = '0.9.51',
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_0949.tar.gz',
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0951.tar.gz',
keywords = ['veracode', 'veracode-api'],
install_requires=[
'veracode-api-signing'
Expand Down
40 changes: 26 additions & 14 deletions veracode_api_py/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,34 @@
from .apihelper import APIHelper

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

scan_types = ["Static Analysis", "Dynamic Analysis", "Manual", "SCA", "Software Composition Analysis" ]
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,
scan_type:list = [], finding_status=None,passed_policy=None,
policy_sandbox=None,application_id=None,rawjson=False):
#TODO validate report_type, add scan_type

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 }

if last_updated_end_date:
report_def['last_updated_end_date'] = last_updated_end_date

if len(scan_type) > 0:
# validate against self.scan_types
if self._case_insensitive_list_compare(scan_type,self.scan_types):
report_def['scan_type'] = scan_type
else:
print("Invalid scan_type provided. Valid types are {}".format(self.scan_types))
return
if report_type == 'findings':
valid_scan_types = self.findings_scan_types
elif report_type == 'scans':
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))
report_def['scan_type'] = scan_type

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

def get(self,guid: UUID):
def get_findings(self, guid: UUID):
thestatus, thefindings = self.get(guid=guid,report_type='findings')
return thestatus, thefindings

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

def get(self,guid: UUID,report_type='findings'):
# handle multiple scan types
uri = "{}/{}".format(self.base_url,guid)
theresponse = APIHelper()._rest_paged_request(uri,"GET","findings",{},fullresponse=True)
theresponse = APIHelper()._rest_paged_request(uri,"GET",report_type,{},fullresponse=True)
thestatus = theresponse.get('_embedded',{}).get('status','')
thefindings = theresponse.get('_embedded',{}).get('findings',{})
return thestatus, thefindings
thebody = theresponse.get('_embedded',{}).get(report_type,{})
return thestatus, thebody

#helper methods
def _case_insensitive_list_compare(self,input_list:list, target_list:list):
input_set = self._lowercase_set_from_list(input_list)
target_set = self._lowercase_set_from_list(target_list)
return target_set.issuperset(input_set)


def _lowercase_set_from_list(self,thelist:list):
return set([x.lower() for x in thelist])
4 changes: 2 additions & 2 deletions veracode_api_py/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ def get_static_flaw_info(self, app: UUID, issueid, sandbox: UUID = None):
def get_dynamic_flaw_info(self, app: UUID, issueid):
return Findings().get_dynamic_flaw_info(app, issueid)

def get_summary_report(self, app: UUID, sandbox=None):
return SummaryReport().get_summary_report(app, sandbox)
def get_summary_report(self, app: UUID, sandbox=None, build_id=None):
return SummaryReport().get_summary_report(app=app, sandbox=sandbox, build_id=build_id)

def add_annotation(self, app: UUID, issue_list, comment, action, sandbox: UUID = None):
return Findings().add_annotation(app, issue_list, comment, action, sandbox)
Expand Down
14 changes: 9 additions & 5 deletions veracode_api_py/findings.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,17 @@ def _create_match_format_policy(self, policy_findings, finding_type):
return findings

class SummaryReport():
def get_summary_report(self,app: UUID,sandbox: UUID=None):
def get_summary_report(self,app: UUID,sandbox: UUID=None, build_id: int=None):
uri = "appsec/v2/applications/{}/summary_report".format(app)

params = {}
if sandbox != None:
uri = "appsec/v2/applications/{}/summary_report?context={}".format(app,sandbox)
else:
uri = "appsec/v2/applications/{}/summary_report".format(app)
params['context'] = sandbox

return APIHelper()._rest_request(uri,"GET")
if build_id != None:
params['build_id'] = build_id

return APIHelper()._rest_request(uri,"GET", params=params)

class ManualScans():
def get_for_app(self,appid: UUID):
Expand Down

0 comments on commit d069ce9

Please sign in to comment.