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

Support for DependabotAlert APIs #2879

Merged
merged 11 commits into from
Jan 26, 2024
14 changes: 2 additions & 12 deletions github/AdvisoryBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@
from datetime import datetime
from typing import Any

from github.AdvisoryVulnerability import AdvisoryVulnerability
from github.CVSS import CVSS
from github.CWE import CWE
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet


class AdvisoryBase(NonCompletableGithubObject):
"""
This class represents a the shared attributes between GlobalAdvisory and RepositoryAdvisory
This class represents a the shared attributes between GlobalAdvisory, RepositoryAdvisory and DependabotAdvisory
https://docs.github.com/en/rest/security-advisories/global-advisories
https://docs.github.com/en/rest/security-advisories/repository-advisories
https://docs.github.com/en/rest/dependabot/alerts
"""

def _initAttributes(self) -> None:
Expand All @@ -52,7 +52,6 @@ def _initAttributes(self) -> None:
self._summary: Attribute[str] = NotSet
self._updated_at: Attribute[datetime] = NotSet
self._url: Attribute[str] = NotSet
self._vulnerabilities: Attribute[list[AdvisoryVulnerability]] = NotSet
self._withdrawn_at: Attribute[datetime] = NotSet

def __repr__(self) -> str:
Expand Down Expand Up @@ -106,10 +105,6 @@ def updated_at(self) -> datetime:
def url(self) -> str:
return self._url.value

@property
def vulnerabilities(self) -> list[AdvisoryVulnerability]:
return self._vulnerabilities.value

@property
def withdrawn_at(self) -> datetime:
return self._withdrawn_at.value
Expand Down Expand Up @@ -145,11 +140,6 @@ def _useAttributes(self, attributes: dict[str, Any]) -> None:
self._updated_at = self._makeDatetimeAttribute(attributes["updated_at"])
if "url" in attributes: # pragma no branch
self._url = self._makeStringAttribute(attributes["url"])
if "vulnerabilities" in attributes: # pragma no branch
self._vulnerabilities = self._makeListOfClassesAttribute(
AdvisoryVulnerability,
attributes["vulnerabilities"],
)
if "withdrawn_at" in attributes: # pragma no branch
assert attributes["withdrawn_at"] is None or isinstance(attributes["withdrawn_at"], str), attributes[
"withdrawn_at"
Expand Down
2 changes: 1 addition & 1 deletion github/AdvisoryVulnerabilityPackage.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

class AdvisoryVulnerabilityPackage(NonCompletableGithubObject):
"""
This class represents an identifier for a package that is vulnerable tao parent SecurityAdvisory.
This class represents an identifier for a package that is vulnerable to a parent SecurityAdvisory.
The reference can be found here https://docs.github.com/en/rest/security-advisories/repository-advisories
"""

Expand Down
135 changes: 135 additions & 0 deletions github/DependabotAlert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, Any

import github.AdvisoryVulnerabilityPackage
import github.DependabotAlertAdvisory
import github.DependabotAlertDependency
import github.DependabotAlertVulnerability
import github.NamedUser
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet

if TYPE_CHECKING:
from github.DependabotAlertAdvisory import DependabotAlertAdvisory
from github.DependabotAlertDependency import DependabotAlertDependency
from github.DependabotAlertVulnerability import DependabotAlertVulnerability
from github.NamedUser import NamedUser


class DependabotAlert(NonCompletableGithubObject):
"""
This class represents a DependabotAlert.
The reference can be found here https://docs.github.com/en/rest/dependabot/alerts
"""

def _initAttributes(self) -> None:
self._number: Attribute[int] = NotSet
self._state: Attribute[str] = NotSet
self._dependency: Attribute[DependabotAlertDependency] = NotSet
self._security_advisory: Attribute[DependabotAlertAdvisory] = NotSet
self._security_vulnerability: Attribute[DependabotAlertVulnerability] = NotSet
self._url: Attribute[str] = NotSet
self._html_url: Attribute[str] = NotSet
self._created_at: Attribute[datetime] = NotSet
self._updated_at: Attribute[datetime] = NotSet
self._dismissed_at: Attribute[datetime | None] = NotSet
self._dismissed_by: Attribute[NamedUser | None] = NotSet
self._dismissed_reason: Attribute[str | None] = NotSet
self._dismissed_comment: Attribute[str | None] = NotSet
self._fixed_at: Attribute[str] = NotSet

def __repr__(self) -> str:
return self.get__repr__({"number": self.number, "ghsa_id": self.security_advisory.ghsa_id})

@property
def number(self) -> int:
return self._number.value

@property
def state(self) -> str:
return self._state.value

@property
def dependency(self) -> DependabotAlertDependency:
return self._dependency.value

@property
def security_advisory(self) -> DependabotAlertAdvisory:
return self._security_advisory.value

@property
def security_vulnerability(self) -> DependabotAlertVulnerability:
return self._security_vulnerability.value

@property
def url(self) -> str:
return self._url.value

@property
def html_url(self) -> str:
return self._html_url.value

@property
def created_at(self) -> datetime:
return self._created_at.value

@property
def updated_at(self) -> datetime:
return self._updated_at.value

@property
def dismissed_at(self) -> datetime | None:
return self._dismissed_at.value

@property
def dismissed_by(self) -> NamedUser | None:
return self._dismissed_by.value

@property
def dismissed_reason(self) -> str | None:
return self._dismissed_reason.value

@property
def dismissed_comment(self) -> str | None:
return self._dismissed_comment.value

@property
def fixed_at(self) -> str | None:
return self._fixed_at.value

def _useAttributes(self, attributes: dict[str, Any]) -> None:
if "number" in attributes:
self._number = self._makeIntAttribute(attributes["number"])
if "state" in attributes:
self._state = self._makeStringAttribute(attributes["state"])
if "dependency" in attributes:
self._dependency = self._makeClassAttribute(
github.DependabotAlertDependency.DependabotAlertDependency, attributes["dependency"]
)
if "security_advisory" in attributes:
self._security_advisory = self._makeClassAttribute(
github.DependabotAlertAdvisory.DependabotAlertAdvisory, attributes["security_advisory"]
)
if "security_vulnerability" in attributes:
self._security_vulnerability = self._makeClassAttribute(
github.DependabotAlertVulnerability.DependabotAlertVulnerability, attributes["security_vulnerability"]
)
if "url" in attributes:
self._url = self._makeStringAttribute(attributes["url"])
if "html_url" in attributes:
self._html_url = self._makeStringAttribute(attributes["html_url"])
if "created_at" in attributes:
self._created_at = self._makeDatetimeAttribute(attributes["created_at"])
if "updated_at" in attributes:
self._updated_at = self._makeDatetimeAttribute(attributes["updated_at"])
if "dismissed_at" in attributes:
self._dismissed_at = self._makeDatetimeAttribute(attributes["dismissed_at"])
if "dismissed_by" in attributes:
self._dismissed_by = self._makeClassAttribute(github.NamedUser.NamedUser, attributes["dismissed_by"])
if "dismissed_reason" in attributes:
self._dismissed_reason = self._makeStringAttribute(attributes["dismissed_reason"])
if "dismissed_comment" in attributes:
self._dismissed_comment = self._makeStringAttribute(attributes["dismissed_comment"])
if "fixed_at" in attributes:
self._fixed_at = self._makeStringAttribute(attributes["fixed_at"])
42 changes: 42 additions & 0 deletions github/DependabotAlertAdvisory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any

import github.AdvisoryBase
import github.DependabotAlertVulnerability
from github.GithubObject import Attribute, NotSet

if TYPE_CHECKING:
from github.DependabotAlertVulnerability import DependabotAlertVulnerability


class DependabotAlertAdvisory(github.AdvisoryBase.AdvisoryBase):
"""
This class represents a package flagged by a Dependabot alert that is vulnerable to a parent SecurityAdvisory.
The reference can be found here https://docs.github.com/en/rest/dependabot/alerts
"""

def _initAttributes(self) -> None:
super()._initAttributes()
self._references: Attribute[list[dict]] = NotSet
self._vulnerabilities: Attribute[list[DependabotAlertVulnerability]] = NotSet

@property
def references(self) -> list[dict]:
return self._references.value

@property
def vulnerabilities(self) -> list[DependabotAlertVulnerability]:
return self._vulnerabilities.value

def _useAttributes(self, attributes: dict[str, Any]) -> None:
if "references" in attributes:
self._references = self._makeListOfDictsAttribute(
attributes["references"],
)
if "vulnerabilities" in attributes:
self._vulnerabilities = self._makeListOfClassesAttribute(
github.DependabotAlertVulnerability.DependabotAlertVulnerability,
attributes["vulnerabilities"],
)
super()._useAttributes(attributes)
41 changes: 41 additions & 0 deletions github/DependabotAlertDependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import annotations

from typing import Any

from github.AdvisoryVulnerabilityPackage import AdvisoryVulnerabilityPackage
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet


class DependabotAlertDependency(NonCompletableGithubObject):
"""
This class represents a DependabotAlertDependency.
The reference can be found here https://docs.github.com/en/rest/dependabot/alerts
"""

def _initAttributes(self) -> None:
self._package: Attribute[AdvisoryVulnerabilityPackage] = NotSet
self._manifest_path: Attribute[str] = NotSet
self._scope: Attribute[str] = NotSet

@property
def package(self) -> AdvisoryVulnerabilityPackage:
return self._package.value

@property
def manifest_path(self) -> str:
return self._manifest_path.value

@property
def scope(self) -> str:
return self._scope.value

def _useAttributes(self, attributes: dict[str, Any]) -> None:
if "package" in attributes:
self._package = self._makeClassAttribute(
AdvisoryVulnerabilityPackage,
attributes["package"],
)
if "manifest_path" in attributes:
self._manifest_path = self._makeStringAttribute(attributes["manifest_path"])
if "scope" in attributes:
self._scope = self._makeStringAttribute(attributes["scope"])
52 changes: 52 additions & 0 deletions github/DependabotAlertVulnerability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any

import github.AdvisoryVulnerabilityPackage
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet

if TYPE_CHECKING:
from github.AdvisoryVulnerabilityPackage import AdvisoryVulnerabilityPackage


class DependabotAlertVulnerability(NonCompletableGithubObject):
"""
A vulnerability represented in a Dependabot alert.
"""

def _initAttributes(self) -> None:
self._package: Attribute[AdvisoryVulnerabilityPackage] = NotSet
self._severity: Attribute[str] = NotSet
self._vulnerable_version_range: Attribute[str | None] = NotSet
self._first_patched_version: Attribute[dict] = NotSet

@property
def package(self) -> AdvisoryVulnerabilityPackage:
return self._package.value

@property
def severity(self) -> str:
return self._severity.value

@property
def vulnerable_version_range(self) -> str | None:
return self._vulnerable_version_range.value

@property
def first_patched_version(self) -> dict:
return self._first_patched_version.value

def _useAttributes(self, attributes: dict[str, Any]) -> None:
if "package" in attributes:
self._package = self._makeClassAttribute(
github.AdvisoryVulnerabilityPackage.AdvisoryVulnerabilityPackage,
attributes["package"],
)
if "severity" in attributes:
self._severity = self._makeStringAttribute(attributes["severity"])
if "vulnerable_version_range" in attributes:
self._vulnerable_version_range = self._makeStringAttribute(attributes["vulnerable_version_range"])
if "first_patched_version" in attributes:
self._first_patched_version = self._makeDictAttribute(
attributes["first_patched_version"],
)
11 changes: 11 additions & 0 deletions github/GlobalAdvisory.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from github.AdvisoryBase import AdvisoryBase
from github.AdvisoryCreditDetailed import AdvisoryCreditDetailed
from github.AdvisoryVulnerability import AdvisoryVulnerability
from github.GithubObject import Attribute, NotSet


Expand All @@ -45,6 +46,7 @@ def _initAttributes(self) -> None:
self._repository_advisory_url: Attribute[str] = NotSet
self._source_code_location: Attribute[str] = NotSet
self._type: Attribute[str] = NotSet
self._vulnerabilities: Attribute[list[AdvisoryVulnerability]] = NotSet

def __repr__(self) -> str:
return self.get__repr__({"ghsa_id": self.ghsa_id, "summary": self.summary})
Expand Down Expand Up @@ -79,6 +81,10 @@ def source_code_location(self) -> str:
def type(self) -> str:
return self._type.value

@property
def vulnerabilities(self) -> list[AdvisoryVulnerability]:
return self._vulnerabilities.value

def _useAttributes(self, attributes: dict[str, Any]) -> None:
if "credits" in attributes: # pragma no branch
self._credits = self._makeListOfClassesAttribute(
Expand All @@ -103,4 +109,9 @@ def _useAttributes(self, attributes: dict[str, Any]) -> None:
self._source_code_location = self._makeStringAttribute(attributes["source_code_location"])
if "type" in attributes: # pragma no branch
self._type = self._makeStringAttribute(attributes["type"])
if "vulnerabilities" in attributes:
self._vulnerabilities = self._makeListOfClassesAttribute(
AdvisoryVulnerability,
attributes["vulnerabilities"],
)
super()._useAttributes(attributes)