Skip to content

Commit

Permalink
Merge pull request #23 from zkarpinski/refactor/refactor-experimental…
Browse files Browse the repository at this point in the history
…-get-vulnerabilities

refactoring
  • Loading branch information
zkarpinski committed Jan 17, 2024
2 parents 359e968 + 8832966 commit 176e321
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 89 deletions.
2 changes: 1 addition & 1 deletion codeinsight_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(
self.__api_token = api_token
self.__api_headers = {
"Content-Type": "application/json",
"Authorization": "Bearer %s" % self.__api_token,
"Authorization": f"Bearer {self.__api_token}",
"User-Agent": "codeinsight_sdk_python",
}
self.__timeout = timeout
Expand Down
35 changes: 9 additions & 26 deletions codeinsight_sdk/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,21 @@ def get_project_vulnerabilities(
Returns:
dict: The vulnerabilities.
"""
# First we get the inventory summary for the project with vulnerability summary
# First we get the inventory for the project
# Then we iterate over the inventory items and calling the inventory vulnerability endpoint for each item with a vulnerability
inventory = self.client.projects.get_inventory_summary(
project_id, vulnerabilitySummary=True
inventory = self.client.projects.get_inventory(
project_id, skip_vulnerabilities=False, include_files=True
)

# Iterate over the inventory items, find which have vulnerabilities.
item: ProjectInventoryItem
vuln_items: list(ProjectInventoryItem) = []
for item in inventory:
if item.vulnerabilitySummary is None or len(item.vulnerabilitySummary) == 0:
for item in inventory.inventoryItems:
if item.vulnerabilities is None:
continue

# If the item has a vulnerability, get the vulnerability details for this item and append it
item_vul_summary = item.vulnerabilitySummary[0]
k = ""
if "CvssV2" in item_vul_summary.keys():
k = "CvssV2"
elif "CvssV3" in item_vul_summary.keys():
k = "CvssV3"
else:
# If the item has no vulnerabilities, skip it
continue

if sum(item_vul_summary[k].values()) > 0:
vul_detail = self.client.inventories.get_inventory_vulnerabilities(
item.id
)
item.vulnerabilities = vul_detail
vuln_items.append(item)
else:
# If the item has no vulnerabilities, skip it
# If the item no no vulnerabilities, ignore it
if len(item.vulnerabilities) == 0:
continue

# TODO: Summarize the vulnerabilities?
vuln_items.append(item)
return vuln_items
7 changes: 6 additions & 1 deletion codeinsight_sdk/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Vulnerability(DataClassJsonMixin):
vulnerabilityId: int
vulnerabilityName: str
vulnerabilityDescription: str
vulnerabilityCvssV3Score: int
vulnerabilityCvssV3Score: str
vulnerabilityCvssV3Severity: str


Expand All @@ -46,13 +46,18 @@ class ProjectInventoryItem(DataClassJsonMixin):
vulnerabilities: Optional[List[Vulnerability]] = None
vulnerabilitySummary: Optional[List[Dict[str, Dict]]] = None
filePaths: Optional[List[str]] = None
parentInventoryItem: Optional[str] = None


@dataclass_json # Trying this style instead of DataClassJsonMixin
@dataclass
class ProjectInventory:
projectId: int
inventoryItems: List[ProjectInventoryItem]
projectName: Optional[str] = None
projectDescription: Optional[str] = None
ownerName: Optional[str] = None
ownerEmail: Optional[str] = None


@dataclass
Expand Down
4 changes: 2 additions & 2 deletions examples/example-1-list-projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@

client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN)


print(client.projects.all())
for prj in client.projects.all():
print(f"{prj.id}) - {prj.name}")
10 changes: 10 additions & 0 deletions examples/example-2-get-project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import shared

from codeinsight_sdk.client import CodeInsightClient

print("Example 1: List projects")

client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN)

id = client.projects.get_id("Example Project")
project = client.projects.get(id)
27 changes: 24 additions & 3 deletions examples/example-9-dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,30 @@
from codeinsight_sdk.client import CodeInsightClient

print("Example 9: Working with Pandas DataFrames")
PROJECT_ID = 1 # Update this to your project ID

client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN)
client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN, experimental=True)

# Get all the project vulnerabilities.
# This will return a list of Vulnerability objects.
proj_vulns = client.experimental.get_project_vulnerabilities(PROJECT_ID)

inventory = client.project_inventory.get(1)
df = pd.DataFrame(inventory.__dict__["inventoryItems"])
# Convert the list of Vulnerability objects to a Pandas DataFrame.
df = pd.DataFrame([v.to_dict() for v in proj_vulns])

# Now "explode" list of vulnerabilities into separate rows.
# This will create a new row for each vulnerability.
df = df.explode("vulnerabilities", ignore_index=True)

# Next we convert the vulnerabilities column into another DataFrame
# and then merge it back into the original DataFrame so that we can
# access the vulnerability properties as columns.
df_vul = pd.json_normalize(df["vulnerabilities"])
df = df.merge(df_vul, left_index=True, right_index=True)

# Optionally we can drop the original vulnerabilities column
# since we no longer need it. You can also drop other columns
df.drop(columns=["vulnerabilities"], inplace=True)

# Finally we write the output to excel
df.to_excel("example-9-output.xlsx", index=False)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "codeinsight_sdk"
version = "0.0.10"
version = "0.0.11"
description = "A Python client for the Revenera Code Insight"
authors = ["Zachary Karpinski <[email protected]>"]
readme = "README.md"
Expand Down
67 changes: 12 additions & 55 deletions tests/test_experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,15 @@ def test_experimental_enabled(self, client):

def test_get_project_vulnerabilities(self, client):
project_id = 1
total_pages = 4
total_records = total_pages * 2
total_pages = 1
total_records = total_pages * 3
response_header = {"content-type": "application/json"}
response_header["current-page"] = "1"
response_header["number-of-pages"] = str(total_pages)
response_header["total-records"] = str(total_records)
fake_response_json = """ { "data": [
{
"itemNumber": 1,
"id": 12345,
"name": "Inventory Item 1",
"type":"component",
"priority":"low",
"createdBy":"Zach",
"createdOn":"Today",
"updatedOn":"Tomorrow",
"componentName":"snakeyaml",
"componentVersionName":"2.0",
"vulnerabilitySummary": []
},
{
"itemNumber": 2,
"id": 12346,
"name": "Inventory Item 2",
"type":"component",
"priority":"low",
"createdBy":"Zach",
"createdOn":"Today",
"updatedOn":"Tomorrow",
"componentName":"snakeyaml",
"componentVersionName":"2.0",
"vulnerabilitySummary": [{
"CvssV2": {
"High": 2,
"Medium": 2,
"Low": 3,
"Unknown": 4
},
"CvssV3": {
"Critical": 1,
"High": 1,
"Medium": 2,
"Low": 6,
"Unknown": 1
}
}]
}
]}
"""

mock_resp_vuln = """ { "data": [ {
fake_response_json = """
{ "projectId": 1, "inventoryItems": [
{"itemNumber":1, "id":1234, "name":"Example component","type":"component","priority":"low","createdBy":"Zach","createdOn":"Today","updatedOn":"Tomorrow","componentName":"snakeyaml","componentVersionName":"2.0","vulnerabilities": [ {
"vulnerabilityId":123456,
"vulnerabilityName":"CVE-123-45678",
"vulnerabilityDescription":"Insecure library! Watch out.",
Expand All @@ -84,23 +42,22 @@ def test_get_project_vulnerabilities(self, client):
"vulnerabilityName":"CVE-987-65432",
"vulnerabilityDescription":"Insecure library 2! Watch out.",
"vulnerabilityCvssV3Score": 9,
"vulnerabilityCvssV3Severity":"CRITICAL"} ] }
"vulnerabilityCvssV3Severity":"CRITICAL"} ] },
{"itemNumber":2, "id":1235, "name":"Example component 2","type":"component","priority":"low","createdBy":"Zach","createdOn":"Today","updatedOn":"Tomorrow","componentName":"snakeyaml","componentVersionName":"2.0"},
{"itemNumber":3, "id":1235, "name":"Example component 3","type":"component","priority":"low","createdBy":"Zach","createdOn":"Today","updatedOn":"Tomorrow","componentName":"snakeyaml","componentVersionName":"2.0","vulnerabilities":[]}
]}
"""

with requests_mock.Mocker() as m:
m.get(
f"{TEST_URL}/codeinsight/api/projects/{project_id}/inventorySummary",
f"{TEST_URL}/codeinsight/api/project/inventory/{project_id}",
text=fake_response_json,
headers=response_header,
)
m.get(
f"{TEST_URL}/codeinsight/api/inventories/12346/vulnerabilities",
text=mock_resp_vuln,
headers=response_header,
)
vulnerable_items = client.experimental.get_project_vulnerabilities(
project_id
)
assert len(vulnerable_items) > 0
assert len(vulnerable_items) == 1
assert vulnerable_items[0].vulnerabilities is not None
assert (
vulnerable_items[0].vulnerabilities[1].vulnerabilityName == "CVE-987-65432"
Expand Down

0 comments on commit 176e321

Please sign in to comment.