Skip to content

Commit

Permalink
Add SCA annotation methods (fix #75)
Browse files Browse the repository at this point in the history
  • Loading branch information
tjarrettveracode committed Nov 9, 2023
1 parent 2cf5b2c commit 5110802
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 6 deletions.
9 changes: 8 additions & 1 deletion docs/sca.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ _Note_: You can also access these methods from the `Workspaces` class.
- `get_vulnerability(vulnerability_id)`: get the vulnerability identified by `vulnerability_id` (an integer value, visible in the output of `get_issues`).
- `get_license(license_id)`: get the license identified by `license_id` (a string, e.g. "GPL30").
- `get_sca_events(date_gte,event_group,event_type)`: get the audit events for the arguments passed. Be careful with the arguments for this and try to limit by date as it will fetch all pages of data, which might be a lot.
- `get_app_projects(app_guid)`: get the list of linked SCA projects for an application. (This API call is also available on the SCAApplications object as `SCAApplications().get_projects()`).

## Component Activity

Expand All @@ -51,4 +50,12 @@ _Note_: You can also access these methods from the `SBOM` class.
- `vulnerability`: if `True`, returns an SBOM containing vulnerability information. Defaults to `True`.
- `dependency` (SPDX only): if `True`, returns an SBOM that includes dependency information. Defaults to `True`.

## Application Info

_Note_: You can also access these methods from the `SCAApplications` class.

- `get_app_projects(app_guid)`: get the list of linked SCA projects for an application. (This API call is also available on the SCAApplications object as `SCAApplications().get_projects()`).
- `get_sca_annotations(app_guid, annotation_type, annotation_reason(opt), annotation_status(opt),cve_name(opt), cwe_id(opt), severities(opt array), license_name(opt), license_risk(opt))`: get the list of annotations (mitigations and comments) for an application. (This API call is also available on the SCAApplications object as `SCAApplications().get_annotations()`.)
- `add_sca_annotation(app_guid, action, comment, annotation_type, component_id, cve_name (required for VULNERABILITY type), license_id (required for LICENSE type))`: add an annotation (mitigation or comment) to an SCA vulnerability or license finding. Note that ability to APPROVE or REJECT requires the mitigation approver role. (This API call is also available on the SCAApplications object as `SCAApplications().add_annotation()`.)

[All docs](docs.md)
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.44'
version = '0.9.45'
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'
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_0944.tar.gz"
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0945.tar.gz"
2 changes: 1 addition & 1 deletion veracode_api_py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from veracode_api_py.findings import Findings, SummaryReport
from veracode_api_py.healthcheck import Healthcheck
from veracode_api_py.identity import Users, Teams, BusinessUnits, APICredentials, Roles
from veracode_api_py.sca import Workspaces, ComponentActivity, SBOM
from veracode_api_py.sca import Workspaces, ComponentActivity, SBOM, SCAApplications
from veracode_api_py.exceptions import VeracodeError, VeracodeAPIError
from veracode_api_py.xmlapi import XMLAPI
from veracode_api_py.analytics import Analytics
13 changes: 13 additions & 0 deletions veracode_api_py/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,19 @@ def get_sbom_project(self, project_guid: UUID, format='cyclonedx', vulnerability

def get_app_projects(self, app_guid: UUID):
return SCAApplications().get_projects(app_guid=app_guid)

def get_sca_annotations(self, app_guid: UUID, annotation_type: str, annotation_reason: str=None,
annotation_status: str=None, cve_name: str=None, cwe_id: str=None, severities=None,
license_name: str=None, license_risk: str=None):
return SCAApplications().get_annotations(app_guid=app_guid, annotation_type=annotation_type,
annotation_reason=annotation_reason,annotation_status=annotation_status,
cve_name=cve_name,cwe_id=cwe_id,severities=severities,
license_name=license_name,license_risk=license_risk)

def add_sca_annotation(self, app_guid: UUID, action: str, comment: str, annotation_type: str,
component_id: UUID, cve_name: str=None, license_id: str=None):
return SCAApplications().add_annotation(app_guid=app_guid, action=action, comment=comment, annotation_type=annotation_type,
component_id=component_id,cve_name=cve_name,license_id=license_id)

# dynamic APIs

Expand Down
12 changes: 11 additions & 1 deletion veracode_api_py/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ class Constants():
"FP":"fp", \
"LIBRARY":"library", \
"ACCEPTRISK": 'acceptrisk'}

SCA_ANNOTATION_TYPE = ["VULNERABILITY", "LICENSE"]

AGENT_TYPE = [ "CLI", "MAVEN", "GRADLE", "JENKINS", "BAMBOO", "CIRCLECI", "CODESHIP", "PIPELINES", "TRAVIS", "WINDOWSCI" ]
SCA_ANNOT_ACTION = ["BYENV", "BYDESIGN", "FP", "ACCEPTRISK", "LEGAL", "COMMERCIAL", "EXPERIMENTAL", "INTERNAL", "APPROVE", "REJECT", "COMMENT"]

SCA_ANNOT_STATUS = ["PROPOSED", "ACCEPTED", "REJECTED"]

SCA_LICENSE_RISK = [ "HIGH", "MEDIUM", "LOW", "NON_OSS", "UNRECOGNIZED"]

AGENT_TYPE = ["CLI", "MAVEN", "GRADLE", "JENKINS", "BAMBOO", "CIRCLECI", "CODESHIP", "PIPELINES", "TRAVIS", "WINDOWSCI"]

SCA_EVENT_GROUP = [ 'WORKSPACE', 'AGENT', 'SCAN', 'PROJECT', 'RULES']

SEVERITIES = [ "VERY_HIGH", "HIGH", "MEDIUM", "LOW", "VERY_LOW", "INFORMATIONAL"]

REGIONS = {
'global': {
'base_xml_url': 'https://analysiscenter.veracode.com/api',
Expand Down
63 changes: 62 additions & 1 deletion veracode_api_py/sca.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,65 @@ class SCAApplications():
entity_base_uri = "srcclr/v3/applications"

def get_projects(self, app_guid: UUID):
return APIHelper()._rest_request(self.entity_base_uri+"/{}/projects".format(app_guid),"GET")
return APIHelper()._rest_request(self.entity_base_uri+"/{}/projects".format(app_guid),"GET")

def get_annotations(self, app_guid: UUID, annotation_type: str, annotation_reason: str=None,
annotation_status: str=None, cve_name: str=None, cwe_id: str=None, severities=None,
license_name: str=None, license_risk: str=None):
if annotation_type not in Constants().SCA_ANNOTATION_TYPE:
raise ValueError("{} is not in the list of valid annotation types ({})".format(annotation_type,Constants().SCA_ANNOTATION_TYPE))
params={"annotation_type":annotation_type}

if annotation_reason:
if annotation_reason not in Constants().SCA_ANNOT_ACTION:
raise ValueError("{} is not in the list of valid annotation reasons ({})".format(annotation_reason,Constants().SCA_ANNOT_ACTION))
params["annotation_reason"] = annotation_reason

if annotation_status:
if annotation_status not in Constants().SCA_ANNOT_STATUS:
raise ValueError("{} is not in the list of valid annotation statuses ({})".format(annotation_status,Constants().SCA_ANNOT_STATUS))
params["annotation_status"] = annotation_status

if cve_name:
params["cve_name"] = cve_name

if cwe_id:
params["cwe_id"] = cwe_id

if severities:
params["severities"] = severities #check against Constants().SEVERITIES

if license_name:
params["license_name"] = license_name

if license_risk:
if license_risk not in Constants().SCA_LICENSE_RISK:
raise ValueError("{} is not in the list of valid license risks ({})".format(license_risk,Constants().SCA_LICENSE_RISK))
params["license_risk"] = license_risk

return APIHelper()._rest_request(self.entity_base_uri+"/{}/sca_annotations".format(app_guid),"GET",params=params)

def add_annotation(self, app_guid: UUID, action: str, comment: str, annotation_type: str,
component_id: UUID, cve_name: str=None, license_id: str=None):

if action not in Constants().SCA_ANNOT_ACTION:
raise ValueError("{} is not in the list of valid actions ({})".format(action,Constants().SCA_ANNOT_ACTION))

if annotation_type == "VULNERABILITY":
if not cve_name:
raise ValueError("You must provide a cve_name for a VULNERABILITY annotation.")
annotation = {"component_id": component_id, "cve_name": cve_name}
elif annotation_type == "LICENSE":
if not license_id:
raise ValueError("You must provide a license_id for a LICENSE annotation.")
annotation = {"component_id": component_id, "license_id": license_id}
else:
raise ValueError("{} is not in the list of valid annotation types ({})".format(annotation_type,Constants().SCA_ANNOTATION_TYPE))

payload = { "action": action, "comment": comment, "annotation_type": annotation_type, "annotations": [ annotation ]}

payload_json = json.dumps(payload)

print(payload_json)

return APIHelper()._rest_request(self.entity_base_uri+"/{}/sca_annotations".format(app_guid),"POST",body=payload_json)

0 comments on commit 5110802

Please sign in to comment.