Skip to content

Commit 14af705

Browse files
Support for DependabotAlert APIs (#2879)
Co-authored-by: Enrico Minack <github@enrico.minack.dev>
1 parent d0caa3c commit 14af705

23 files changed

+755
-14
lines changed

github/AdvisoryBase.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
from datetime import datetime
2727
from typing import Any
2828

29-
from github.AdvisoryVulnerability import AdvisoryVulnerability
3029
from github.CVSS import CVSS
3130
from github.CWE import CWE
3231
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet
3332

3433

3534
class AdvisoryBase(NonCompletableGithubObject):
3635
"""
37-
This class represents a the shared attributes between GlobalAdvisory and RepositoryAdvisory
36+
This class represents a the shared attributes between GlobalAdvisory, RepositoryAdvisory and DependabotAdvisory
3837
https://docs.github.com/en/rest/security-advisories/global-advisories
3938
https://docs.github.com/en/rest/security-advisories/repository-advisories
39+
https://docs.github.com/en/rest/dependabot/alerts
4040
"""
4141

4242
def _initAttributes(self) -> None:
@@ -52,7 +52,6 @@ def _initAttributes(self) -> None:
5252
self._summary: Attribute[str] = NotSet
5353
self._updated_at: Attribute[datetime] = NotSet
5454
self._url: Attribute[str] = NotSet
55-
self._vulnerabilities: Attribute[list[AdvisoryVulnerability]] = NotSet
5655
self._withdrawn_at: Attribute[datetime] = NotSet
5756

5857
def __repr__(self) -> str:
@@ -106,10 +105,6 @@ def updated_at(self) -> datetime:
106105
def url(self) -> str:
107106
return self._url.value
108107

109-
@property
110-
def vulnerabilities(self) -> list[AdvisoryVulnerability]:
111-
return self._vulnerabilities.value
112-
113108
@property
114109
def withdrawn_at(self) -> datetime:
115110
return self._withdrawn_at.value
@@ -145,11 +140,6 @@ def _useAttributes(self, attributes: dict[str, Any]) -> None:
145140
self._updated_at = self._makeDatetimeAttribute(attributes["updated_at"])
146141
if "url" in attributes: # pragma no branch
147142
self._url = self._makeStringAttribute(attributes["url"])
148-
if "vulnerabilities" in attributes: # pragma no branch
149-
self._vulnerabilities = self._makeListOfClassesAttribute(
150-
AdvisoryVulnerability,
151-
attributes["vulnerabilities"],
152-
)
153143
if "withdrawn_at" in attributes: # pragma no branch
154144
assert attributes["withdrawn_at"] is None or isinstance(attributes["withdrawn_at"], str), attributes[
155145
"withdrawn_at"

github/AdvisoryVulnerabilityPackage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

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

github/DependabotAlert.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
from __future__ import annotations
2+
3+
from datetime import datetime
4+
from typing import TYPE_CHECKING, Any
5+
6+
import github.AdvisoryVulnerabilityPackage
7+
import github.DependabotAlertAdvisory
8+
import github.DependabotAlertDependency
9+
import github.DependabotAlertVulnerability
10+
import github.NamedUser
11+
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet
12+
13+
if TYPE_CHECKING:
14+
from github.DependabotAlertAdvisory import DependabotAlertAdvisory
15+
from github.DependabotAlertDependency import DependabotAlertDependency
16+
from github.DependabotAlertVulnerability import DependabotAlertVulnerability
17+
from github.NamedUser import NamedUser
18+
19+
20+
class DependabotAlert(NonCompletableGithubObject):
21+
"""
22+
This class represents a DependabotAlert.
23+
The reference can be found here https://docs.github.com/en/rest/dependabot/alerts
24+
"""
25+
26+
def _initAttributes(self) -> None:
27+
self._number: Attribute[int] = NotSet
28+
self._state: Attribute[str] = NotSet
29+
self._dependency: Attribute[DependabotAlertDependency] = NotSet
30+
self._security_advisory: Attribute[DependabotAlertAdvisory] = NotSet
31+
self._security_vulnerability: Attribute[DependabotAlertVulnerability] = NotSet
32+
self._url: Attribute[str] = NotSet
33+
self._html_url: Attribute[str] = NotSet
34+
self._created_at: Attribute[datetime] = NotSet
35+
self._updated_at: Attribute[datetime] = NotSet
36+
self._dismissed_at: Attribute[datetime | None] = NotSet
37+
self._dismissed_by: Attribute[NamedUser | None] = NotSet
38+
self._dismissed_reason: Attribute[str | None] = NotSet
39+
self._dismissed_comment: Attribute[str | None] = NotSet
40+
self._fixed_at: Attribute[str] = NotSet
41+
42+
def __repr__(self) -> str:
43+
return self.get__repr__({"number": self.number, "ghsa_id": self.security_advisory.ghsa_id})
44+
45+
@property
46+
def number(self) -> int:
47+
return self._number.value
48+
49+
@property
50+
def state(self) -> str:
51+
return self._state.value
52+
53+
@property
54+
def dependency(self) -> DependabotAlertDependency:
55+
return self._dependency.value
56+
57+
@property
58+
def security_advisory(self) -> DependabotAlertAdvisory:
59+
return self._security_advisory.value
60+
61+
@property
62+
def security_vulnerability(self) -> DependabotAlertVulnerability:
63+
return self._security_vulnerability.value
64+
65+
@property
66+
def url(self) -> str:
67+
return self._url.value
68+
69+
@property
70+
def html_url(self) -> str:
71+
return self._html_url.value
72+
73+
@property
74+
def created_at(self) -> datetime:
75+
return self._created_at.value
76+
77+
@property
78+
def updated_at(self) -> datetime:
79+
return self._updated_at.value
80+
81+
@property
82+
def dismissed_at(self) -> datetime | None:
83+
return self._dismissed_at.value
84+
85+
@property
86+
def dismissed_by(self) -> NamedUser | None:
87+
return self._dismissed_by.value
88+
89+
@property
90+
def dismissed_reason(self) -> str | None:
91+
return self._dismissed_reason.value
92+
93+
@property
94+
def dismissed_comment(self) -> str | None:
95+
return self._dismissed_comment.value
96+
97+
@property
98+
def fixed_at(self) -> str | None:
99+
return self._fixed_at.value
100+
101+
def _useAttributes(self, attributes: dict[str, Any]) -> None:
102+
if "number" in attributes:
103+
self._number = self._makeIntAttribute(attributes["number"])
104+
if "state" in attributes:
105+
self._state = self._makeStringAttribute(attributes["state"])
106+
if "dependency" in attributes:
107+
self._dependency = self._makeClassAttribute(
108+
github.DependabotAlertDependency.DependabotAlertDependency, attributes["dependency"]
109+
)
110+
if "security_advisory" in attributes:
111+
self._security_advisory = self._makeClassAttribute(
112+
github.DependabotAlertAdvisory.DependabotAlertAdvisory, attributes["security_advisory"]
113+
)
114+
if "security_vulnerability" in attributes:
115+
self._security_vulnerability = self._makeClassAttribute(
116+
github.DependabotAlertVulnerability.DependabotAlertVulnerability, attributes["security_vulnerability"]
117+
)
118+
if "url" in attributes:
119+
self._url = self._makeStringAttribute(attributes["url"])
120+
if "html_url" in attributes:
121+
self._html_url = self._makeStringAttribute(attributes["html_url"])
122+
if "created_at" in attributes:
123+
self._created_at = self._makeDatetimeAttribute(attributes["created_at"])
124+
if "updated_at" in attributes:
125+
self._updated_at = self._makeDatetimeAttribute(attributes["updated_at"])
126+
if "dismissed_at" in attributes:
127+
self._dismissed_at = self._makeDatetimeAttribute(attributes["dismissed_at"])
128+
if "dismissed_by" in attributes:
129+
self._dismissed_by = self._makeClassAttribute(github.NamedUser.NamedUser, attributes["dismissed_by"])
130+
if "dismissed_reason" in attributes:
131+
self._dismissed_reason = self._makeStringAttribute(attributes["dismissed_reason"])
132+
if "dismissed_comment" in attributes:
133+
self._dismissed_comment = self._makeStringAttribute(attributes["dismissed_comment"])
134+
if "fixed_at" in attributes:
135+
self._fixed_at = self._makeStringAttribute(attributes["fixed_at"])

github/DependabotAlertAdvisory.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
import github.AdvisoryBase
6+
import github.DependabotAlertVulnerability
7+
from github.GithubObject import Attribute, NotSet
8+
9+
if TYPE_CHECKING:
10+
from github.DependabotAlertVulnerability import DependabotAlertVulnerability
11+
12+
13+
class DependabotAlertAdvisory(github.AdvisoryBase.AdvisoryBase):
14+
"""
15+
This class represents a package flagged by a Dependabot alert that is vulnerable to a parent SecurityAdvisory.
16+
The reference can be found here https://docs.github.com/en/rest/dependabot/alerts
17+
"""
18+
19+
def _initAttributes(self) -> None:
20+
super()._initAttributes()
21+
self._references: Attribute[list[dict]] = NotSet
22+
self._vulnerabilities: Attribute[list[DependabotAlertVulnerability]] = NotSet
23+
24+
@property
25+
def references(self) -> list[dict]:
26+
return self._references.value
27+
28+
@property
29+
def vulnerabilities(self) -> list[DependabotAlertVulnerability]:
30+
return self._vulnerabilities.value
31+
32+
def _useAttributes(self, attributes: dict[str, Any]) -> None:
33+
if "references" in attributes:
34+
self._references = self._makeListOfDictsAttribute(
35+
attributes["references"],
36+
)
37+
if "vulnerabilities" in attributes:
38+
self._vulnerabilities = self._makeListOfClassesAttribute(
39+
github.DependabotAlertVulnerability.DependabotAlertVulnerability,
40+
attributes["vulnerabilities"],
41+
)
42+
super()._useAttributes(attributes)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
from github.AdvisoryVulnerabilityPackage import AdvisoryVulnerabilityPackage
6+
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet
7+
8+
9+
class DependabotAlertDependency(NonCompletableGithubObject):
10+
"""
11+
This class represents a DependabotAlertDependency.
12+
The reference can be found here https://docs.github.com/en/rest/dependabot/alerts
13+
"""
14+
15+
def _initAttributes(self) -> None:
16+
self._package: Attribute[AdvisoryVulnerabilityPackage] = NotSet
17+
self._manifest_path: Attribute[str] = NotSet
18+
self._scope: Attribute[str] = NotSet
19+
20+
@property
21+
def package(self) -> AdvisoryVulnerabilityPackage:
22+
return self._package.value
23+
24+
@property
25+
def manifest_path(self) -> str:
26+
return self._manifest_path.value
27+
28+
@property
29+
def scope(self) -> str:
30+
return self._scope.value
31+
32+
def _useAttributes(self, attributes: dict[str, Any]) -> None:
33+
if "package" in attributes:
34+
self._package = self._makeClassAttribute(
35+
AdvisoryVulnerabilityPackage,
36+
attributes["package"],
37+
)
38+
if "manifest_path" in attributes:
39+
self._manifest_path = self._makeStringAttribute(attributes["manifest_path"])
40+
if "scope" in attributes:
41+
self._scope = self._makeStringAttribute(attributes["scope"])
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
import github.AdvisoryVulnerabilityPackage
6+
from github.GithubObject import Attribute, NonCompletableGithubObject, NotSet
7+
8+
if TYPE_CHECKING:
9+
from github.AdvisoryVulnerabilityPackage import AdvisoryVulnerabilityPackage
10+
11+
12+
class DependabotAlertVulnerability(NonCompletableGithubObject):
13+
"""
14+
A vulnerability represented in a Dependabot alert.
15+
"""
16+
17+
def _initAttributes(self) -> None:
18+
self._package: Attribute[AdvisoryVulnerabilityPackage] = NotSet
19+
self._severity: Attribute[str] = NotSet
20+
self._vulnerable_version_range: Attribute[str | None] = NotSet
21+
self._first_patched_version: Attribute[dict] = NotSet
22+
23+
@property
24+
def package(self) -> AdvisoryVulnerabilityPackage:
25+
return self._package.value
26+
27+
@property
28+
def severity(self) -> str:
29+
return self._severity.value
30+
31+
@property
32+
def vulnerable_version_range(self) -> str | None:
33+
return self._vulnerable_version_range.value
34+
35+
@property
36+
def first_patched_version(self) -> dict:
37+
return self._first_patched_version.value
38+
39+
def _useAttributes(self, attributes: dict[str, Any]) -> None:
40+
if "package" in attributes:
41+
self._package = self._makeClassAttribute(
42+
github.AdvisoryVulnerabilityPackage.AdvisoryVulnerabilityPackage,
43+
attributes["package"],
44+
)
45+
if "severity" in attributes:
46+
self._severity = self._makeStringAttribute(attributes["severity"])
47+
if "vulnerable_version_range" in attributes:
48+
self._vulnerable_version_range = self._makeStringAttribute(attributes["vulnerable_version_range"])
49+
if "first_patched_version" in attributes:
50+
self._first_patched_version = self._makeDictAttribute(
51+
attributes["first_patched_version"],
52+
)

github/GlobalAdvisory.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
from github.AdvisoryBase import AdvisoryBase
3030
from github.AdvisoryCreditDetailed import AdvisoryCreditDetailed
31+
from github.AdvisoryVulnerability import AdvisoryVulnerability
3132
from github.GithubObject import Attribute, NotSet
3233

3334

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

4951
def __repr__(self) -> str:
5052
return self.get__repr__({"ghsa_id": self.ghsa_id, "summary": self.summary})
@@ -79,6 +81,10 @@ def source_code_location(self) -> str:
7981
def type(self) -> str:
8082
return self._type.value
8183

84+
@property
85+
def vulnerabilities(self) -> list[AdvisoryVulnerability]:
86+
return self._vulnerabilities.value
87+
8288
def _useAttributes(self, attributes: dict[str, Any]) -> None:
8389
if "credits" in attributes: # pragma no branch
8490
self._credits = self._makeListOfClassesAttribute(
@@ -103,4 +109,9 @@ def _useAttributes(self, attributes: dict[str, Any]) -> None:
103109
self._source_code_location = self._makeStringAttribute(attributes["source_code_location"])
104110
if "type" in attributes: # pragma no branch
105111
self._type = self._makeStringAttribute(attributes["type"])
112+
if "vulnerabilities" in attributes:
113+
self._vulnerabilities = self._makeListOfClassesAttribute(
114+
AdvisoryVulnerability,
115+
attributes["vulnerabilities"],
116+
)
106117
super()._useAttributes(attributes)

0 commit comments

Comments
 (0)