Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add explicit version information to emitted records #763

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
  •  
  •  
  •  
214 changes: 214 additions & 0 deletions schema/vulnerability/os/schema-1.0.3.json
Copy link
Contributor Author

@wagoodman wagoodman Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff schema/vulnerability/os/schema-1.0.2.json schema/vulnerability/os/schema-1.0.3.json
72a73,89
>                 "OS": {
>                   "type": "object",
>                   "properties": {
>                     "ID": {
>                       "type": "string"
>                     },
>                     "Version": {
>                       "type": "string"
>                     }
>                   }
>                 },
>                 "Architectures": {
>                   "type": "array",
>                   "items": {
>                     "type": "string"
>                   }
>                 },

Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"title": "os-vulnerability",
"description": "represents vulnerability records for common linux distributions",
"properties": {
"Vulnerability": {
"type": "object",
"properties": {
"CVSS": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"base_metrics": {
"type": "object",
"properties": {
"base_score": {
"type": "number"
},
"base_severity": {
"type": "string"
},
"exploitability_score": {
"type": "number"
},
"impact_score": {
"type": "number"
}
},
"required": [
"base_score",
"base_severity",
"exploitability_score",
"impact_score"
]
},
"status": {
"type": "string"
},
"vector_string": {
"type": "string"
},
"version": {
"type": "string"
}
},
"required": [
"base_metrics",
"status",
"vector_string",
"version"
]
}
]
},
"Description": {
"type": "string"
},
"FixedIn": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"Name": {
"type": "string"
},
"NamespaceName": {
"type": "string"
},
"OS": {
"type": "object",
"properties": {
"ID": {
"type": "string"
},
"Version": {
"type": "string"
}
}
},
"Architectures": {
"type": "array",
"items": {
"type": "string"
}
},
"VendorAdvisory": {
"type": "object",
"properties": {
"AdvisorySummary": {
"type": "array",
"items": {}
},
"NoAdvisory": {
"type": "boolean"
}
},
"required": [
"NoAdvisory"
]
},
"Version": {
"type": "string"
},
"VersionFormat": {
"type": "string"
},
"VulnerableRange": {
"type": ["string", "null"]
},
"Module": {
"type": ["string", "null"]
},
"Issued": {
"type": "string",
"description": "date the fix was made available"
}
},
"required": [
"Name",
"Version",
"VersionFormat"
]
}
]
},
"Link": {
"type": "string"
},
"Metadata": {
"type": "object",
"properties": {
"Issued": {
"type": "string",
"description": "date the vulnerability was published"
},
"Updated": {
"type": "string",
"description": "date the vulnerability was last updated"
},
"Withdrawn": {
"type": "string",
"description": "date the vulnerability was withdrawn"
},
"RefId": {
"type": "string"
},
"CVE": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"Name": {
"type": "string"
},
"Link": {
"type": "string"
}
},
"required": [
"Name"
]
}
]
},
"NVD": {
"type": "object",
"properties": {
"CVSSv2": {
"type": "object",
"properties": {
"Score": {
"type": "number"
},
"Vectors": {
"type": "string"
}
},
"required": [
"Score"
]
}
}
}
}
},
"Name": {
"type": "string"
},
"NamespaceName": {
"type": "string"
},
"Severity": {
"type": "string"
}
},
"required": [
"Description",
"FixedIn",
"Link",
"Metadata",
"Name",
"Severity"
]
}
},
"required": [
"Vulnerability"
]
}
6 changes: 6 additions & 0 deletions src/vunnel/providers/alpine/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from vunnel import workspace

# namespace should match the value found in the /etc/os-release ID field
namespace = "alpine"
feedtype = "vulnerabilities"
purge_unreported = True
Expand Down Expand Up @@ -242,11 +243,16 @@ def _normalize(self, release, dbtype_data_dict): # noqa: C901, PLR0912
fixed_el["NamespaceName"] = namespace + ":" + str(release)
fixed_el["Name"] = pkg
fixed_el["Version"] = pkg_version
fixed_el["OS"] = self.os_info(release)

vuln_record["Vulnerability"]["FixedIn"].append(fixed_el)

return vuln_dict

def os_info(self, release: any) -> dict[str, str]:
# note: ID is based off of the /etc/os-release ID field
return {"ID": namespace, "Version": str(release)}

def get(self):
"""
Download, load and normalize alpine sec db and return a dict of releae - list of vulnerability records
Expand Down
9 changes: 9 additions & 0 deletions src/vunnel/providers/amazon/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from vunnel.utils import http, rpm

# namespace should match the value found in the /etc/os-release ID field
namespace = "amzn"

AlasSummary = namedtuple("AlasSummary", ["id", "url", "sev", "cves"])
Expand Down Expand Up @@ -235,6 +236,13 @@ def __init__(self):
self.NamespaceName = None
self.VersionFormat = None
self.Version = None
self.OS = None


class OperatingSystem(JsonifierMixin):
def __init__(self, identifier: str | None = None, version: str | None = None):
self.ID = identifier
self.Version = version


class PackagesHTMLParser(HTMLParser):
Expand Down Expand Up @@ -316,6 +324,7 @@ def map_to_vulnerability(version, alas, fixed_in, description):
f.NamespaceName = v.NamespaceName
f.VersionFormat = "rpm"
f.Version = item.ver
f.OS = OperatingSystem(identifier=namespace, version=version)
v.FixedIn.append(f)

return v
11 changes: 9 additions & 2 deletions src/vunnel/providers/debian/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"sid": "unstable",
}

# namespace should match the value found in the /etc/os-release ID field
namespace = "debian"


class Parser:
_json_url_ = "https://security-tracker.debian.org/tracker/data/json"
Expand Down Expand Up @@ -316,7 +319,7 @@ def _normalize_json(self, ns_cve_dsalist=None): # noqa: PLR0912,PLR0915,C901
# populate the static information about the new vuln record
vuln_record["Vulnerability"]["Description"] = vulnerability_data.get("description", "")
vuln_record["Vulnerability"]["Name"] = str(vid)
vuln_record["Vulnerability"]["NamespaceName"] = "debian:" + str(relno)
vuln_record["Vulnerability"]["NamespaceName"] = namespace + ":" + str(relno)
vuln_record["Vulnerability"]["Link"] = "https://security-tracker.debian.org/tracker/" + str(vid)
vuln_record["Vulnerability"]["Severity"] = "Unknown"
else:
Expand Down Expand Up @@ -357,8 +360,12 @@ def _normalize_json(self, ns_cve_dsalist=None): # noqa: PLR0912,PLR0915,C901
skip_fixedin = False
fixed_el = {
"Name": pkg,
"NamespaceName": "debian:" + str(relno),
"NamespaceName": namespace + ":" + str(relno),
"VersionFormat": "dpkg",
"OS": {
"ID": namespace,
"Version": str(relno),
},
}

if "fixed_version" in distro_record:
Expand Down
22 changes: 21 additions & 1 deletion src/vunnel/providers/mariner/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from vunnel.providers.mariner.model import Definition, RpminfoObject, RpminfoState, RpminfoTest
from vunnel.utils import http
from vunnel.utils.vulnerability import FixedIn, Vulnerability
from vunnel.utils.vulnerability import FixedIn, OperatingSystem, Vulnerability

if TYPE_CHECKING:
import logging
Expand Down Expand Up @@ -166,6 +166,10 @@ def make_fixed_in(self, definition: Definition) -> FixedIn | None:
VulnerableRange=vulnerability_range_str,
Module=None,
VendorAdvisory=None,
OS=OperatingSystem(
ID=VERSION_TO_OS_ID.get(self.mariner_version, DEFAULT_VERSION_ID),
Version=self.mariner_version,
),
)

def vulnerability_id(self, definition: Definition) -> str | None:
Expand Down Expand Up @@ -223,6 +227,22 @@ def vulnerabilities(self) -> Generator[Vulnerability, None, None]:
}


# All values are based off of the ID value within /etc/os-release
# e.g.:
# docker run --rm -it mcr.microsoft.com/azurelinux/base/core:3.0 cat /etc/os-release | grep ID
# ID=azurelinux
# ...
#
# docker run --rm -it mcr.microsoft.com/cbl-mariner/base/core:2.0.20220731-arm64@sha256:51101e635f56032d5afd3fb cat /etc/os-release | grep ID
# ID=mariner
# ...
DEFAULT_VERSION_ID = "azurelinux"
VERSION_TO_OS_ID = {
"1.0": "mariner",
"2.0": "mariner",
}


class Parser:
def __init__(self, workspace: Workspace, download_timeout: int, allow_versions: list[Any], logger: logging.Logger):
self.workspace = workspace
Expand Down
23 changes: 23 additions & 0 deletions src/vunnel/providers/rhel/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
if TYPE_CHECKING:
import requests

# namespace should match the value found in the /etc/os-release ID field
namespace = "rhel"


Expand Down Expand Up @@ -816,6 +817,7 @@ def _parse_cve(self, cve_id, content): # noqa: C901, PLR0912, PLR0915
"VersionFormat": "rpm", # hard code version format for now
"NamespaceName": ns,
"VendorAdvisory": a,
"OS": {"ID": namespace, "Version": parse_release(artifact.version, platform)},
},
)

Expand Down Expand Up @@ -896,3 +898,24 @@ def normalize(self):
"base_severity": self.cvss3_obj.severities()[0],
},
}


def parse_release(fix_version: str, platform: str) -> str:
# attempt to parse 0:1.0.0-8.el8_8.1 for the release info (8.8.1)
# otherwise fallback to the platform version
try:
last_section = fix_version.split("-")[-1]
except IndexError:
return platform

el_match = re.search(r"\.el(\d+)(?:_([0-9.]+))?", last_section)
if not el_match:
return platform

major_version = el_match.group(1)
minor_version = el_match.group(2)

if not minor_version:
return major_version

return f"{major_version}.{minor_version}"
Loading
Loading