Skip to content

Commit 176e321

Browse files
authored
Merge pull request #23 from zkarpinski/refactor/refactor-experimental-get-vulnerabilities
refactoring
2 parents 359e968 + 8832966 commit 176e321

File tree

8 files changed

+65
-89
lines changed

8 files changed

+65
-89
lines changed

codeinsight_sdk/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def __init__(
2525
self.__api_token = api_token
2626
self.__api_headers = {
2727
"Content-Type": "application/json",
28-
"Authorization": "Bearer %s" % self.__api_token,
28+
"Authorization": f"Bearer {self.__api_token}",
2929
"User-Agent": "codeinsight_sdk_python",
3030
}
3131
self.__timeout = timeout

codeinsight_sdk/experimental.py

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,38 +22,21 @@ def get_project_vulnerabilities(
2222
Returns:
2323
dict: The vulnerabilities.
2424
"""
25-
# First we get the inventory summary for the project with vulnerability summary
25+
# First we get the inventory for the project
2626
# Then we iterate over the inventory items and calling the inventory vulnerability endpoint for each item with a vulnerability
27-
inventory = self.client.projects.get_inventory_summary(
28-
project_id, vulnerabilitySummary=True
27+
inventory = self.client.projects.get_inventory(
28+
project_id, skip_vulnerabilities=False, include_files=True
2929
)
3030

3131
# Iterate over the inventory items, find which have vulnerabilities.
3232
item: ProjectInventoryItem
3333
vuln_items: list(ProjectInventoryItem) = []
34-
for item in inventory:
35-
if item.vulnerabilitySummary is None or len(item.vulnerabilitySummary) == 0:
34+
for item in inventory.inventoryItems:
35+
if item.vulnerabilities is None:
3636
continue
37-
38-
# If the item has a vulnerability, get the vulnerability details for this item and append it
39-
item_vul_summary = item.vulnerabilitySummary[0]
40-
k = ""
41-
if "CvssV2" in item_vul_summary.keys():
42-
k = "CvssV2"
43-
elif "CvssV3" in item_vul_summary.keys():
44-
k = "CvssV3"
45-
else:
46-
# If the item has no vulnerabilities, skip it
47-
continue
48-
49-
if sum(item_vul_summary[k].values()) > 0:
50-
vul_detail = self.client.inventories.get_inventory_vulnerabilities(
51-
item.id
52-
)
53-
item.vulnerabilities = vul_detail
54-
vuln_items.append(item)
55-
else:
56-
# If the item has no vulnerabilities, skip it
37+
# If the item no no vulnerabilities, ignore it
38+
if len(item.vulnerabilities) == 0:
5739
continue
58-
40+
# TODO: Summarize the vulnerabilities?
41+
vuln_items.append(item)
5942
return vuln_items

codeinsight_sdk/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Vulnerability(DataClassJsonMixin):
2424
vulnerabilityId: int
2525
vulnerabilityName: str
2626
vulnerabilityDescription: str
27-
vulnerabilityCvssV3Score: int
27+
vulnerabilityCvssV3Score: str
2828
vulnerabilityCvssV3Severity: str
2929

3030

@@ -46,13 +46,18 @@ class ProjectInventoryItem(DataClassJsonMixin):
4646
vulnerabilities: Optional[List[Vulnerability]] = None
4747
vulnerabilitySummary: Optional[List[Dict[str, Dict]]] = None
4848
filePaths: Optional[List[str]] = None
49+
parentInventoryItem: Optional[str] = None
4950

5051

5152
@dataclass_json # Trying this style instead of DataClassJsonMixin
5253
@dataclass
5354
class ProjectInventory:
5455
projectId: int
5556
inventoryItems: List[ProjectInventoryItem]
57+
projectName: Optional[str] = None
58+
projectDescription: Optional[str] = None
59+
ownerName: Optional[str] = None
60+
ownerEmail: Optional[str] = None
5661

5762

5863
@dataclass

examples/example-1-list-projects.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

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

9-
10-
print(client.projects.all())
9+
for prj in client.projects.all():
10+
print(f"{prj.id}) - {prj.name}")

examples/example-2-get-project.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import shared
2+
3+
from codeinsight_sdk.client import CodeInsightClient
4+
5+
print("Example 1: List projects")
6+
7+
client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN)
8+
9+
id = client.projects.get_id("Example Project")
10+
project = client.projects.get(id)

examples/example-9-dataframe.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,30 @@
33
from codeinsight_sdk.client import CodeInsightClient
44

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

7-
client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN)
8+
client = CodeInsightClient(shared.BASE_URL, shared.AUTH_TOKEN, experimental=True)
89

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

10-
inventory = client.project_inventory.get(1)
11-
df = pd.DataFrame(inventory.__dict__["inventoryItems"])
14+
# Convert the list of Vulnerability objects to a Pandas DataFrame.
15+
df = pd.DataFrame([v.to_dict() for v in proj_vulns])
16+
17+
# Now "explode" list of vulnerabilities into separate rows.
18+
# This will create a new row for each vulnerability.
19+
df = df.explode("vulnerabilities", ignore_index=True)
20+
21+
# Next we convert the vulnerabilities column into another DataFrame
22+
# and then merge it back into the original DataFrame so that we can
23+
# access the vulnerability properties as columns.
24+
df_vul = pd.json_normalize(df["vulnerabilities"])
25+
df = df.merge(df_vul, left_index=True, right_index=True)
26+
27+
# Optionally we can drop the original vulnerabilities column
28+
# since we no longer need it. You can also drop other columns
29+
df.drop(columns=["vulnerabilities"], inplace=True)
30+
31+
# Finally we write the output to excel
32+
df.to_excel("example-9-output.xlsx", index=False)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "codeinsight_sdk"
3-
version = "0.0.10"
3+
version = "0.0.11"
44
description = "A Python client for the Revenera Code Insight"
55
authors = ["Zachary Karpinski <1206496+zkarpinski@users.noreply.github.com>"]
66
readme = "README.md"

tests/test_experimental.py

Lines changed: 12 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -23,57 +23,15 @@ def test_experimental_enabled(self, client):
2323

2424
def test_get_project_vulnerabilities(self, client):
2525
project_id = 1
26-
total_pages = 4
27-
total_records = total_pages * 2
26+
total_pages = 1
27+
total_records = total_pages * 3
2828
response_header = {"content-type": "application/json"}
2929
response_header["current-page"] = "1"
3030
response_header["number-of-pages"] = str(total_pages)
3131
response_header["total-records"] = str(total_records)
32-
fake_response_json = """ { "data": [
33-
{
34-
"itemNumber": 1,
35-
"id": 12345,
36-
"name": "Inventory Item 1",
37-
"type":"component",
38-
"priority":"low",
39-
"createdBy":"Zach",
40-
"createdOn":"Today",
41-
"updatedOn":"Tomorrow",
42-
"componentName":"snakeyaml",
43-
"componentVersionName":"2.0",
44-
"vulnerabilitySummary": []
45-
},
46-
{
47-
"itemNumber": 2,
48-
"id": 12346,
49-
"name": "Inventory Item 2",
50-
"type":"component",
51-
"priority":"low",
52-
"createdBy":"Zach",
53-
"createdOn":"Today",
54-
"updatedOn":"Tomorrow",
55-
"componentName":"snakeyaml",
56-
"componentVersionName":"2.0",
57-
"vulnerabilitySummary": [{
58-
"CvssV2": {
59-
"High": 2,
60-
"Medium": 2,
61-
"Low": 3,
62-
"Unknown": 4
63-
},
64-
"CvssV3": {
65-
"Critical": 1,
66-
"High": 1,
67-
"Medium": 2,
68-
"Low": 6,
69-
"Unknown": 1
70-
}
71-
}]
72-
}
73-
]}
74-
"""
75-
76-
mock_resp_vuln = """ { "data": [ {
32+
fake_response_json = """
33+
{ "projectId": 1, "inventoryItems": [
34+
{"itemNumber":1, "id":1234, "name":"Example component","type":"component","priority":"low","createdBy":"Zach","createdOn":"Today","updatedOn":"Tomorrow","componentName":"snakeyaml","componentVersionName":"2.0","vulnerabilities": [ {
7735
"vulnerabilityId":123456,
7836
"vulnerabilityName":"CVE-123-45678",
7937
"vulnerabilityDescription":"Insecure library! Watch out.",
@@ -84,23 +42,22 @@ def test_get_project_vulnerabilities(self, client):
8442
"vulnerabilityName":"CVE-987-65432",
8543
"vulnerabilityDescription":"Insecure library 2! Watch out.",
8644
"vulnerabilityCvssV3Score": 9,
87-
"vulnerabilityCvssV3Severity":"CRITICAL"} ] }
45+
"vulnerabilityCvssV3Severity":"CRITICAL"} ] },
46+
{"itemNumber":2, "id":1235, "name":"Example component 2","type":"component","priority":"low","createdBy":"Zach","createdOn":"Today","updatedOn":"Tomorrow","componentName":"snakeyaml","componentVersionName":"2.0"},
47+
{"itemNumber":3, "id":1235, "name":"Example component 3","type":"component","priority":"low","createdBy":"Zach","createdOn":"Today","updatedOn":"Tomorrow","componentName":"snakeyaml","componentVersionName":"2.0","vulnerabilities":[]}
48+
]}
8849
"""
50+
8951
with requests_mock.Mocker() as m:
9052
m.get(
91-
f"{TEST_URL}/codeinsight/api/projects/{project_id}/inventorySummary",
53+
f"{TEST_URL}/codeinsight/api/project/inventory/{project_id}",
9254
text=fake_response_json,
9355
headers=response_header,
9456
)
95-
m.get(
96-
f"{TEST_URL}/codeinsight/api/inventories/12346/vulnerabilities",
97-
text=mock_resp_vuln,
98-
headers=response_header,
99-
)
10057
vulnerable_items = client.experimental.get_project_vulnerabilities(
10158
project_id
10259
)
103-
assert len(vulnerable_items) > 0
60+
assert len(vulnerable_items) == 1
10461
assert vulnerable_items[0].vulnerabilities is not None
10562
assert (
10663
vulnerable_items[0].vulnerabilities[1].vulnerabilityName == "CVE-987-65432"

0 commit comments

Comments
 (0)