Skip to content

Commit

Permalink
Merge branch 'main' into add-cpe-summary
Browse files Browse the repository at this point in the history
  • Loading branch information
ffontaine authored Aug 17, 2023
2 parents 1b1ac52 + 9b65b25 commit 377ca9e
Show file tree
Hide file tree
Showing 37 changed files with 510 additions and 198 deletions.
3 changes: 3 additions & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ cves
cvs
cvss
cyberciti
cybersecurity
cygwin
darkhttpd
davfs
Expand Down Expand Up @@ -145,6 +146,8 @@ emacs
endoflife
enscript
entrypoint
epss
EPSS
Eqt
Everyone
everytime
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3
uses: github/codeql-action/init@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -76,4 +76,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3
uses: github/codeql-action/analyze@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
2 changes: 1 addition & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: 'Dependency Review'
uses: actions/dependency-review-action@7d90b4f05fea31dde1c4a1fb3fa787e197ea93ab # v3.0.7
uses: actions/dependency-review-action@f6fff72a3217f580d5afd49a46826795305b63c7 # v3.0.8
4 changes: 2 additions & 2 deletions cve_bin_tool/cve_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData):
f"{product_info} already processed. Update path {triage_data['paths']}"
)
# self.products_with_cve += 1
self.all_cve_data[product_info]["paths"] |= triage_data["paths"]
self.all_cve_data[product_info]["paths"] |= set(triage_data["paths"])
return

# Check for anything directly marked
Expand Down Expand Up @@ -283,7 +283,7 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData):
f"{len(cves)} CVE(s) in {product_info.vendor}.{product_info.product} version {product_info.version}"
)
self.all_cve_data[product_info]["cves"] = cves
self.all_cve_data[product_info]["paths"] |= triage_data["paths"]
self.all_cve_data[product_info]["paths"] |= set(triage_data["paths"])
else:
# No cves found for (product, vendor, version) tuple in the NVD database.
self.products_without_cve += 1
Expand Down
8 changes: 4 additions & 4 deletions cve_bin_tool/data_sources/curl_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self, error_mode=ErrorMode.TruncTrace):
self.session = None
self.affected_data = None
self.source_name = self.SOURCE
self.vulnerbility_data = []
self.vulnerability_data = []

async def get_cve_data(self):
await self.fetch_cves()
Expand All @@ -59,16 +59,16 @@ async def fetch_cves(self):
async def download_curl_vulnerabilities(self, session: RateLimiter) -> None:
async with await session.get(self.DATA_SOURCE_LINK) as response:
response.raise_for_status()
self.vulnerbility_data = await response.json()
self.vulnerability_data = await response.json()
path = Path(str(Path(self.cachedir) / "vuln.json"))
filepath = path.resolve()
async with FileIO(filepath, "w") as f:
await f.write(json.dumps(self.vulnerbility_data, indent=4))
await f.write(json.dumps(self.vulnerability_data, indent=4))

def get_cve_list(self):
self.affected_data = []

for cve in self.vulnerbility_data:
for cve in self.vulnerability_data:
affected = {
"cve_id": cve["aliases"][0],
"vendor": "haxx",
Expand Down
6 changes: 4 additions & 2 deletions cve_bin_tool/data_sources/nvd_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ def format_data(self, all_cve_entries):
def parse_node(self, node: dict[str, list[dict[str, str]]]) -> list[dict[str, str]]:
affects_list = []
if "cpe_match" in node:
for cpe_match in node["cpe_match"]:
vulnerable_matches = (m for m in node["cpe_match"] if m["vulnerable"])
for cpe_match in vulnerable_matches:
cpe_split = cpe_match["cpe23Uri"].split(":")
affects = {
"vendor": cpe_split[3],
Expand Down Expand Up @@ -277,7 +278,8 @@ def parse_node_api2(
) -> list[dict[str, str]]:
affects_list = []
if "cpeMatch" in node:
for cpe_match in node["cpeMatch"]:
vulnerable_matches = (m for m in node["cpeMatch"] if m["vulnerable"])
for cpe_match in vulnerable_matches:
cpe_split = cpe_match["criteria"].split(":")
affects = {
"vendor": cpe_split[3],
Expand Down
26 changes: 16 additions & 10 deletions cve_bin_tool/data_sources/osv_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ def format_data(self, all_cve_entries):
vendor = (
"unknown" # OSV Schema does not provide vendor names for packages
)
if "/" in product and "github":
if (
"github.com/" in product
): # if package name is of format github.com/xxxx/yyyy xxxx can be vendor name and yyyy is package name
vendor = product.split("/")[-2] # trying to guess vendor name
product = product.split("/")[-1]

Expand All @@ -314,20 +316,22 @@ def format_data(self, all_cve_entries):

events = None
for ranges in package.get("ranges", []):
if ranges["type"] != "GIT":
if ranges["type"] == "SEMVER":
events = ranges["events"]

if events is None:
if events is None and "versions" in package:
versions = package["versions"]

if versions == []:
continue

affected["versionStartIncluding"] = versions[0]
affected["versionEndIncluding"] = versions[-1]
version_affected = affected.copy()

affected_data.append(affected)
else:
version_affected["versionStartIncluding"] = versions[0]
version_affected["versionEndIncluding"] = versions[-1]

affected_data.append(version_affected)
elif events is not None:
introduced = None
fixed = None

Expand All @@ -338,12 +342,14 @@ def format_data(self, all_cve_entries):
fixed = event.get("fixed")

if fixed is not None:
affected["versionStartIncluding"] = introduced
affected["versionEndExcluding"] = fixed
range_affected = affected.copy()

range_affected["versionStartIncluding"] = introduced
range_affected["versionEndExcluding"] = fixed

fixed = None

affected_data.append(affected)
affected_data.append(range_affected)

return severity_data, affected_data

Expand Down
2 changes: 1 addition & 1 deletion cve_bin_tool/input_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def strip_remark(detail) -> str:
self.parsed_data[product_info][id.strip() or "default"][
"severity"
] = severity.strip()
self.parsed_data[product_info]["paths"] = {""}
self.parsed_data[product_info]["paths"] = {}

def parse_data(self, fields: Set[str], data: Iterable) -> None:
required_fields = {"vendor", "product", "version"}
Expand Down
74 changes: 73 additions & 1 deletion cve_bin_tool/output_engine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,74 @@ def output_pdf(
"Applicationlist", widths=[3 * cm, 3 * cm, 2 * cm, 4 * cm, 3 * cm]
)

pdfdoc.heading(1, "List of Vulnerabilities with different metric")
pdfdoc.paragraph(
"The table given below gives CVE found with there score on different metrics."
)
cve_by_metrics: defaultdict[Remarks, list[dict[str, str]]] = defaultdict(
list
)
col_headings = [
"CVE Number",
"CVSS_version",
"CVSS_score",
"EPSS_propability",
"EPSS_percentile",
]
# group cve_data by its remarks and separately by paths
for product_info, cve_data in all_cve_data.items():
for cve in cve_data["cves"]:
propability = "-"
percentile = "-"
for metric, field in cve.metric.items():
if metric == "EPSS":
propability = round(field[0] * 100, 4)
percentile = field[1]

cve_by_metrics[cve.remarks].append(
{
"cve_number": cve.cve_number,
"cvss_version": str(cve.cvss_version),
"cvss_score": str(cve.score),
"epss_propability": str(propability),
"epss_percentile": str(percentile),
"severity": cve.severity,
}
)

for remarks in sorted(cve_by_metrics):
pdfdoc.createtable(
"cvemetric",
col_headings,
pdfdoc.tblStyle,
)
row = 1
for cve in cve_by_metrics[remarks]:
entry = [
cve["cve_number"],
cve["cvss_version"],
str(cve["cvss_score"]),
str(cve["epss_propability"]),
str(cve["epss_percentile"]),
]
pdfdoc.addrow(
"cvemetric",
entry,
[
(
"TEXTCOLOR",
(0, row),
(4, row),
severity_colour[cve["severity"].split("-")[0].upper()],
),
("FONT", (0, row), (4, row), "Helvetica-Bold"),
],
)
row += 1
pdfdoc.showtable(
"cvemetric", widths=[4 * cm, 4 * cm, 3 * cm, 4 * cm, 4 * cm]
)

# List of scanned products with no identified vulnerabilities
if all_product_data is not None:
pdfdoc.heading(1, "No Identified Vulnerabilities")
Expand All @@ -481,8 +549,12 @@ def output_pdf(
[10, 10, 10],
)
row = 1
products_with_cves = list(map(lambda x: x[1], all_cve_data))
for product_data in all_product_data:
if all_product_data[product_data] == 0:
if (
all_product_data[product_data] == 0
and product_data.product not in products_with_cves
):
product_entry = [
product_data.vendor,
product_data.product,
Expand Down
50 changes: 49 additions & 1 deletion cve_bin_tool/output_engine/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,12 @@ def validate_cell_length(cell_name, cell_type):
table.add_column("Product")
table.add_column("Version")

products_with_cves = list(map(lambda x: x[1], all_cve_data))
for product_data in all_product_data:
if all_product_data[product_data] == 0:
if (
all_product_data[product_data] == 0
and product_data.product not in products_with_cves
):
cells = [
Text.styled(product_data.vendor, color),
Text.styled(product_data.product, color),
Expand All @@ -325,3 +329,47 @@ def validate_cell_length(cell_name, cell_type):
table.add_row(*cells)
# Print the table to the console
console.print(table)

table = Table()
# Add Head Columns to the Table
table.add_column("CVE")
table.add_column("CVSS_version")
table.add_column("CVSS_score")
table.add_column("EPSS_propability")
table.add_column("EPSS_percentile")
color = "green"

cve_by_metrics: defaultdict[Remarks, list[dict[str, str]]] = defaultdict(list)
# group cve_data by its remarks and separately by paths
for product_info, cve_data in all_cve_data.items():
for cve in cve_data["cves"]:
propability = "-"
percentile = "-"
for metric, field in cve.metric.items():
if metric == "EPSS":
propability = round(field[0] * 100, 4)
percentile = field[1]
cve_by_metrics[cve.remarks].append(
{
"cve_number": cve.cve_number,
"cvss_version": str(cve.cvss_version),
"cvss_score": str(cve.score),
"epss_propability": str(propability),
"epss_percentile": str(percentile),
"severity": cve.severity,
}
)

for remarks in sorted(cve_by_remarks):
color = remarks_colors[remarks]
for cve in cve_by_metrics[remarks]:
color = cve["severity"].split("-")[0].lower()
cells = [
Text.styled(cve["cve_number"], color),
Text.styled(cve["cvss_version"], color),
Text.styled(str(cve["cvss_score"]), color),
Text.styled(cve["epss_propability"], color),
Text.styled(cve["epss_percentile"], color),
]
table.add_row(*cells)
console.print(table)
47 changes: 47 additions & 0 deletions cve_bin_tool/output_engine/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,52 @@ def output_html(

cve_severity = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "UNKNOWN": 0}

cve_by_metrics: defaultdict[Remarks, list[dict[str, str]]] = defaultdict(list)
for product_info, cve_data in all_cve_data.items():
for cve in cve_data["cves"]:
propability = "-"
percentile = "-"

for metric, field in cve.metric.items():
if metric == "EPSS":
propability = round(field[0] * 100, 4)
percentile = field[1]

cve_by_metrics[cve.remarks].append(
{
"cve_number": cve.cve_number,
"cvss_version": str(cve.cvss_version),
"cvss_score": str(cve.score),
"epss_propability": str(propability),
"epss_percentile": str(percentile),
"severity": cve.severity,
}
)

cve_metric_html_rows = []
for remarks in sorted(cve_by_metrics):
for cve in cve_by_metrics[remarks]:
row_color = "table-success"
if cve["severity"] == "CRITICAL":
row_color = "table-danger"
elif cve["severity"] == "HIGH":
row_color = "table-primary"
elif cve["severity"] == "MEDIUM":
row_color = "table-warning"

html_row = f"""
<tr class="{row_color}">
<th scope="row">{cve["cve_number"]}</th>
<td>{cve["cvss_version"]}</td>
<td>{cve["cvss_score"]}</td>
<td>{cve["epss_propability"]}</td>
<td>{cve["epss_percentile"]}</td>
</tr>
"""
cve_metric_html_rows.append(html_row)
# Join the HTML rows to create the full table content
table_content = "\n".join(cve_metric_html_rows)

# List of Products
for product_info, cve_data in all_cve_data.items():
# Check if product contains CVEs
Expand Down Expand Up @@ -357,6 +403,7 @@ def output_html(
products_without_cve=products_without_cve,
cve_remarks=cve_remarks,
cve_severity=cve_severity,
table_content=table_content,
)

# try to load the bigger files just before the generation of report
Expand Down
Loading

0 comments on commit 377ca9e

Please sign in to comment.