Add support for new RepositoryAdvisories API 🎉 (#2483)

This commit is contained in:
Jonathan Leitschuh
2023-06-09 13:42:18 -04:00
committed by GitHub
parent 84912a675c
commit daf62bd461
23 changed files with 2002 additions and 21 deletions
+56
View File
@@ -0,0 +1,56 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
import github.GithubObject
class CWE(github.GithubObject.CompletableGithubObject):
"""
This class represents a CWE.
The reference can be found here https://docs.github.com/en/rest/security-advisories/repository-advisories
"""
@property
def cwe_id(self) -> str:
"""
:type: string
"""
return self._cwe_id.value
@property
def name(self) -> str:
"""
:type: string
"""
return self._name.value
# noinspection PyPep8Naming
def _initAttributes(self):
self._cwe_id = github.GithubObject.NotSet
self._name = github.GithubObject.NotSet
# noinspection PyPep8Naming
def _useAttributes(self, attributes):
if "cwe_id" in attributes: # pragma no branch
self._cwe_id = self._makeStringAttribute(attributes["cwe_id"])
if "name" in attributes: # pragma no branch
self._name = self._makeStringAttribute(attributes["name"])
+13
View File
@@ -10,7 +10,15 @@
# Copyright 2016 Jannis Gebauer <ja.geb@me.com> #
# Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com> #
# Copyright 2016 Sam Corbett <sam.corbett@cloudsoftcorp.com> #
# Copyright 2018 Shubham Singh <41840111+singh811@users.noreply.github.com> #
# Copyright 2018 h.shi <10385628+AnYeMoWang@users.noreply.github.com> #
# Copyright 2018 sfdye <tsfdye@gmail.com> #
# Copyright 2019 Adam Baratz <adam.baratz@gmail.com> #
# Copyright 2019 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2019 Wan Liuyang <tsfdye@gmail.com> #
# Copyright 2020 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2021 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
@@ -31,6 +39,7 @@
################################################################################
import datetime
import typing
from operator import itemgetter
from . import Consts, GithubException
@@ -45,6 +54,10 @@ class _NotSetType:
NotSet = _NotSetType()
T = typing.TypeVar("T")
OptionallySet = typing.Union[T, _NotSetType]
class _ValuedAttribute:
def __init__(self, value):
+10 -3
View File
@@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, List, Optional, Type, Union
from typing import Any, Callable, Dict, List, Optional, Type, Union, TypeVar
from github.Commit import Commit
from github.GistFile import GistFile
@@ -96,6 +96,7 @@ class NonCompletableGithubObject(GithubObject):
def _completeIfNeeded(self) -> None: ...
class CompletableGithubObject(GithubObject):
_requester: Requester
def __eq__(self, other: Any) -> bool: ...
def __init__(
self,
@@ -120,8 +121,14 @@ class _BadAttribute:
class _NotSetType:
def __repr__(self) -> str: ...
@property
def value(self) -> Any: ...
NotSet: _NotSetType
T = TypeVar("T")
OptionallySet = Union[T, _NotSetType]
class _ValuedAttribute:
def __init__(self, value: Any) -> None: ...
NotSet: _NotSetType
+233 -9
View File
@@ -27,7 +27,7 @@
# Copyright 2016 Dustin Spicuzza <dustin@virtualroadside.com> #
# Copyright 2016 Enix Yu <enix223@163.com> #
# Copyright 2016 Jannis Gebauer <ja.geb@me.com> #
# Copyright 2016 Per Øyvind Karlsen <proyvind@moondrake.org> #
# Copyright 2016 Per Øyvind Karlsen <proyvind@moondrake.org> #
# Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com> #
# Copyright 2016 Sylvus <Sylvus@users.noreply.github.com> #
# Copyright 2016 fukatani <nannyakannya@gmail.com> #
@@ -39,34 +39,69 @@
# Copyright 2017 Jannis Gebauer <ja.geb@me.com> #
# Copyright 2017 Jason White <jasonwhite@users.noreply.github.com> #
# Copyright 2017 Jimmy Zelinskie <jimmy.zelinskie+git@gmail.com> #
# Copyright 2017 Nhomar Hernández [Vauxoo] <nhomar@vauxoo.com> #
# Copyright 2017 Nhomar Hernández [Vauxoo] <nhomar@vauxoo.com> #
# Copyright 2017 Simon <spam@esemi.ru> #
# Copyright 2018 Aaron L. Levine <allevin@sandia.gov> #
# Copyright 2018 AetherDeity <aetherdeity+github@gmail.com> #
# Copyright 2018 Alice GIRARD <bouhahah@gmail.com> #
# Copyright 2018 Andrew Smith <espadav8@gmail.com> #
# Copyright 2018 Benoit Latinier <benoit@latinier.fr> #
# Copyright 2018 Brian Torres-Gil <btorres-gil@paloaltonetworks.com> #
# Copyright 2018 Hayden Fuss <wifu1234@gmail.com> #
# Copyright 2018 Ilya Konstantinov <ilya.konstantinov@gmail.com> #
# Copyright 2018 Jacopo Notarstefano <jacopo.notarstefano@gmail.com> #
# Copyright 2018 John Hui <j-hui@users.noreply.github.com> #
# Copyright 2018 Justin Kufro <jkufro@andrew.cmu.edu> #
# Copyright 2018 Mateusz Loskot <mateusz@loskot.net> #
# Copyright 2018 Michael Behrisch <oss@behrisch.de> #
# Copyright 2018 Nicholas Buse <NicholasBuse@users.noreply.github.com> #
# Copyright 2018 Philip May <eniak.info@gmail.com> #
# Copyright 2018 Raihaan <31362124+res0nance@users.noreply.github.com> #
# Copyright 2018 Shinichi TAMURA <shnch.tmr@gmail.com> #
# Copyright 2018 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2018 Vinay Hegde <hegde.vi@husky.neu.edu> #
# Copyright 2018 Wan Liuyang <tsfdye@gmail.com> #
# Copyright 2018 Will Yardley <wyardley@users.noreply.github.com> #
# Copyright 2018 Yossarian King <yggy@blackbirdinteractive.com> #
# Copyright 2018 per1234 <accounts@perglass.com> #
# Copyright 2018 sechastain <sechastain@gmail.com> #
# Copyright 2018 sfdye <tsfdye@gmail.com> #
# Copyright 2018 Vinay Hegde <vinayhegde2010@gmail.com> #
# Copyright 2018 Justin Kufro <jkufro@andrew.cmu.edu> #
# Copyright 2018 Ivan Minno <iminno@andrew.cmu.edu> #
# Copyright 2018 Zilei Gu <zileig@andrew.cmu.edu> #
# Copyright 2018 Yves Zumbach <yzumbach@andrew.cmu.edu> #
# Copyright 2018 Leying Chen <leyingc@andrew.cmu.edu> #
# Copyright 2019 Adam Baratz <adam.baratz@gmail.com> #
# Copyright 2019 Alex <alexmusa@users.noreply.github.com> #
# Copyright 2019 Kevin LaFlamme <k@lamfl.am> #
# Copyright 2019 Olof-Joachim Frahm (欧雅福) <olof@macrolet.net> #
# Copyright 2019 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2019 Tim Gates <tim.gates@iress.com> #
# Copyright 2019 Wan Liuyang <tsfdye@gmail.com> #
# Copyright 2019 Will Li <cuichen.li94@gmail.com> #
# Copyright 2020 Alice GIRARD <bouhahah@gmail.com> #
# Copyright 2020 Anuj Bansal <bansalanuj1996@gmail.com> #
# Copyright 2020 Chris de Graaf <chrisadegraaf@gmail.com> #
# Copyright 2020 Dhruv Manilawala <dhruvmanila@gmail.com> #
# Copyright 2020 Dominic Davis-Foster <dominic@davis-foster.co.uk> #
# Copyright 2020 Florent Clarret <florent.clarret@gmail.com> #
# Copyright 2020 Glenn McDonald <testworksau@users.noreply.github.com> #
# Copyright 2020 Huw Jones <huwcbjones@outlook.com> #
# Copyright 2020 Mark Bromell <markbromell.business@gmail.com> #
# Copyright 2020 Max Wittig <max.wittig@siemens.com> #
# Copyright 2020 Pascal Hofmann <mail@pascalhofmann.de> #
# Copyright 2022 Aleksei Fedotov <aleksei@fedotov.email> #
# Copyright 2020 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2020 Tim Gates <tim.gates@iress.com> #
# Copyright 2020 Victor Zeng <zacker150@users.noreply.github.com> #
# Copyright 2020 ton-katsu <sakamoto.yoshihisa@gmail.com> #
# Copyright 2021 Chris Keating <christopherkeating@gmail.com> #
# Copyright 2021 Floyd Hightower <floyd.hightower27@gmail.com> #
# Copyright 2021 Mark Walker <mark.walker@realbuzz.com> #
# Copyright 2021 Steve Kowalik <steven@wedontsleep.org> #
# Copyright 2021 Tanner <51724788+lightningboltemoji@users.noreply.github.com> #
# Copyright 2021 xmo-odoo <xmo@odoo.com> #
# Copyright 2022 Aleksei Fedotov <lexa@cfotr.com> #
# Copyright 2022 Eric Nieuwland <eric.nieuwland@gmail.com> #
# Copyright 2022 Ibrahim Hussaini <ibrahimhussainialias@outlook.com> #
# Copyright 2022 KimSia Sim <245021+simkimsia@users.noreply.github.com> #
# Copyright 2022 Marco Köpcke <hello@parakoopa.de> #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# Copyright 2023 Sol Redfern <59831933+Tsuesun@users.noreply.github.com> #
# Copyright 2023 Mikhail f. Shiryaev <mr.felixoid@gmail.com> #
# #
# This file is part of PyGithub. #
@@ -89,6 +124,7 @@
import collections
import datetime
import typing
import urllib.parse
from base64 import b64encode
@@ -132,6 +168,9 @@ import github.PublicKey
import github.PullRequest
import github.Referrer
import github.Repository
import github.RepositoryAdvisory
import github.RepositoryAdvisoryCredit
import github.RepositoryAdvisoryVulnerability
import github.RepositoryKey
import github.RepositoryPreferences
import github.SelfHostedActionsRunner
@@ -1472,6 +1511,162 @@ class Repository(github.GithubObject.CompletableGithubObject):
self._requester, headers, data, completed=True
)
def create_repository_advisory(
self,
summary: str,
description: str,
severity_or_cvss_vector_string: str,
cve_id: typing.Optional[str] = None,
vulnerabilities: typing.Optional[
typing.Iterable[
github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability
]
] = None,
cwe_ids: typing.Optional[typing.Iterable[str]] = None,
credits: typing.Optional[
typing.Iterable[github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit]
] = None,
) -> github.RepositoryAdvisory.RepositoryAdvisory:
"""
:calls: `POST /repos/{owner}/{repo}/security-advisories <https://docs.github.com/en/rest/security-advisories/repository-advisories>`_
:param summary: string
:param description: string
:param severity_or_cvss_vector_string: string
:param cve_id: string
:param vulnerabilities: iterable of :class:`github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability`
:param cwe_ids: iterable of string
:param credits: iterable of :class:`github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit`
:rtype: :class:`github.RepositoryAdvisory.RepositoryAdvisory`
"""
return self.__create_repository_advisory(
summary=summary,
description=description,
severity_or_cvss_vector_string=severity_or_cvss_vector_string,
cve_id=cve_id,
vulnerabilities=vulnerabilities,
cwe_ids=cwe_ids,
credits=credits,
private_vulnerability_reporting=False,
)
def report_security_vulnerability(
self,
summary: str,
description: str,
severity_or_cvss_vector_string: str,
cve_id: typing.Optional[str] = None,
vulnerabilities: typing.Optional[
typing.Iterable[
github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability
]
] = None,
cwe_ids: typing.Optional[typing.Iterable[str]] = None,
credits: typing.Optional[
typing.Iterable[github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit]
] = None,
) -> github.RepositoryAdvisory.RepositoryAdvisory:
"""
:calls: `POST /repos/{owner}/{repo}/security-advisories/reports <https://docs.github.com/en/rest/security-advisories/repository-advisories#privately-report-a-security-vulnerability>`_
:param summary: string
:param description: string
:param severity_or_cvss_vector_string: string
:param cve_id: string
:param vulnerabilities: iterable of :class:`github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability`
:param cwe_ids: iterable of string
:param credits: iterable of :class:`github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit`
:rtype: :class:`github.RepositoryAdvisory.RepositoryAdvisory`
"""
return self.__create_repository_advisory(
summary=summary,
description=description,
severity_or_cvss_vector_string=severity_or_cvss_vector_string,
cve_id=cve_id,
vulnerabilities=vulnerabilities,
cwe_ids=cwe_ids,
credits=credits,
private_vulnerability_reporting=True,
)
def __create_repository_advisory(
self,
summary: str,
description: str,
severity_or_cvss_vector_string: str,
cve_id: typing.Optional[str],
vulnerabilities: typing.Optional[
typing.Iterable[
github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability
]
],
cwe_ids: typing.Optional[typing.Iterable[str]],
credits: typing.Optional[
typing.Iterable[github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit]
],
private_vulnerability_reporting: bool,
) -> github.RepositoryAdvisory.RepositoryAdvisory:
if vulnerabilities is None:
vulnerabilities = []
if cwe_ids is None:
cwe_ids = []
assert isinstance(summary, str), summary
assert isinstance(description, str), description
assert isinstance(
severity_or_cvss_vector_string, str
), severity_or_cvss_vector_string
assert isinstance(cve_id, (str, type(None))), cve_id
assert isinstance(vulnerabilities, typing.Iterable), vulnerabilities
for vulnerability in vulnerabilities:
# noinspection PyProtectedMember
github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability._validate_vulnerability(
vulnerability
)
assert isinstance(cwe_ids, typing.Iterable), cwe_ids
assert all(isinstance(element, str) for element in cwe_ids), cwe_ids
assert isinstance(credits, (typing.Iterable, type(None))), credits
if credits is not None:
for credit in credits:
# noinspection PyProtectedMember
github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit._validate_credit(
credit
)
# noinspection PyProtectedMember
post_parameters = {
"summary": summary,
"description": description,
"vulnerabilities": [
github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability._to_github_dict(
vulnerability
)
for vulnerability in vulnerabilities
],
"cwe_ids": list(cwe_ids),
}
if cve_id is not None:
post_parameters["cve_id"] = cve_id
if credits is not None:
# noinspection PyProtectedMember
post_parameters["credits"] = [
github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit._to_github_dict(
credit
)
for credit in credits
]
if severity_or_cvss_vector_string.startswith("CVSS:"):
post_parameters["cvss_vector_string"] = severity_or_cvss_vector_string
else:
post_parameters["severity"] = severity_or_cvss_vector_string
if private_vulnerability_reporting:
headers, data = self._requester.requestJsonAndCheck(
"POST", f"{self.url}/security-advisories/reports", input=post_parameters
)
else:
headers, data = self._requester.requestJsonAndCheck(
"POST", f"{self.url}/security-advisories", input=post_parameters
)
return github.RepositoryAdvisory.RepositoryAdvisory(
self._requester, headers, data, completed=True
)
def create_repository_dispatch(
self, event_type, client_payload=github.GithubObject.NotSet
):
@@ -2228,6 +2423,35 @@ class Repository(github.GithubObject.CompletableGithubObject):
),
}
def get_repository_advisories(
self,
) -> "github.PaginatedList.PaginatedList[github.RepositoryAdvisory.RepositoryAdvisory]":
"""
:calls: `GET /repos/{owner}/{repo}/security-advisories <https://docs.github.com/en/rest/security-advisories/repository-advisories>`_
:rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.RepositoryAdvisory.RepositoryAdvisory`
"""
return github.PaginatedList.PaginatedList(
github.RepositoryAdvisory.RepositoryAdvisory,
self._requester,
f"{self.url}/security-advisories",
None,
)
def get_repository_advisory(
self, ghsa: str
) -> github.RepositoryAdvisory.RepositoryAdvisory:
"""
:calls: `GET /repos/{owner}/{repo}/security-advisories/{ghsa} <https://docs.github.com/en/rest/security-advisories/repository-advisories>`_
:param ghsa: string
:rtype: :class:`github.RepositoryAdvisory.RepositoryAdvisory`
"""
headers, data = self._requester.requestJsonAndCheck(
"GET", f"{self.url}/security-advisories/{ghsa}"
)
return github.RepositoryAdvisory.RepositoryAdvisory(
self._requester, headers, data, completed=True
)
def update_file(
self,
path,
+29 -1
View File
@@ -1,5 +1,5 @@
from datetime import date, datetime
from typing import Any, Dict, List, Optional, Union, overload
from typing import Any, Dict, List, Optional, Union, overload, Iterable
from github.Artifact import Artifact
from github.AuthenticatedUser import AuthenticatedUser
@@ -43,6 +43,12 @@ from github.PublicKey import PublicKey
from github.PullRequest import PullRequest
from github.PullRequestComment import PullRequestComment
from github.Referrer import Referrer
from github.RepositoryAdvisory import RepositoryAdvisory
from github.RepositoryAdvisoryCredit import RepositoryAdvisoryCredit
from github.RepositoryAdvisoryVulnerability import (
RepositoryAdvisoryVulnerability,
AdvisoryVulnerability,
)
from github.RepositoryKey import RepositoryKey
from github.RepositoryPreferences import RepositoryPreferences
from github.SelfHostedActionsRunner import SelfHostedActionsRunner
@@ -134,6 +140,8 @@ class Repository(CompletableGithubObject):
transient_environment: Union[bool, _NotSetType] = ...,
production_environment: Union[bool, _NotSetType] = ...,
) -> Deployment: ...
def get_repository_advisories(self) -> PaginatedList[RepositoryAdvisory]: ...
def get_repository_advisory(self, ghsa: str) -> RepositoryAdvisory: ...
def create_file(
self,
path: str,
@@ -242,6 +250,26 @@ class Repository(CompletableGithubObject):
maintainer_can_modify: _NotSetType,
issue: Issue,
) -> PullRequest: ...
def create_repository_advisory(
self,
summary: str,
description: str,
severity_or_cvss_vector_string: str,
cve_id: Optional[str] = ...,
vulnerabilities: Optional[Iterable[AdvisoryVulnerability]] = ...,
cwe_ids: Optional[Iterable[str]] = ...,
credits: Optional[Iterable[RepositoryAdvisoryCredit]] = ...,
) -> RepositoryAdvisory: ...
def report_security_vulnerability(
self,
summary: str,
description: str,
severity_or_cvss_vector_string: str,
cve_id: Optional[str] = ...,
vulnerabilities: Optional[Iterable[AdvisoryVulnerability]] = ...,
cwe_ids: Optional[Iterable[str]] = ...,
credits: Optional[Iterable[RepositoryAdvisoryCredit]] = ...,
) -> RepositoryAdvisory: ...
def create_repository_dispatch(
self, event_type: str, client_payload: Union[Dict[str, Any], _NotSetType] = ...
) -> bool: ...
+558
View File
@@ -0,0 +1,558 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
import datetime
import typing
import github.GithubObject
import github.NamedUser
from github.CWE import CWE
from github.RepositoryAdvisoryCredit import Credit, RepositoryAdvisoryCredit
from github.RepositoryAdvisoryCreditDetailed import RepositoryAdvisoryCreditDetailed
from github.RepositoryAdvisoryVulnerability import (
AdvisoryVulnerability,
RepositoryAdvisoryVulnerability,
)
from github.Requester import Requester
class RepositoryAdvisory(github.GithubObject.NonCompletableGithubObject):
"""
This class represents a RepositoryAdvisory.
The reference can be found here https://docs.github.com/en/rest/security-advisories/repository-advisories
"""
_requester: Requester
def __repr__(self):
return self.get__repr__({"ghsa_id": self.ghsa_id, "summary": self.summary})
@property
def author(self) -> "github.NamedUser.NamedUser":
"""
:type: :class:`github.NamedUser.NamedUser`
"""
return self._author.value
@property
def closed_at(self) -> datetime.datetime:
"""
:type: datetime.datetime
"""
return self._closed_at.value
@property
def created_at(self) -> datetime.datetime:
"""
:type: datetime.datetime
"""
return self._created_at.value
@property
def credits(
self,
) -> typing.List[RepositoryAdvisoryCredit]:
"""
:type: list of :class:`github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit`
"""
return self._credits.value
@property
def credits_detailed(
self,
) -> typing.List[RepositoryAdvisoryCreditDetailed]:
"""
:type: list of :class:`github.RepositoryAdvisoryCreditDetailed.RepositoryAdvisoryCreditDetailed`
"""
return self._credits_detailed.value
@property
def cve_id(self) -> str:
"""
:type: string
"""
return self._cve_id.value
@property
def cwe_ids(self) -> typing.List[str]:
"""
:type: list of string
"""
return self._cwe_ids.value
@property
def cwes(self) -> typing.List[CWE]:
"""
:type: list of :class:`github.CWE.CWE`
"""
return self._cwes.value
@property
def description(self) -> str:
"""
:type: string
"""
return self._description.value
@property
def ghsa_id(self) -> str:
"""
:type: string
"""
return self._ghsa_id.value
@property
def html_url(self) -> str:
"""
:type: string
"""
return self._html_url.value
@property
def published_at(self) -> datetime.datetime:
"""
:type: datetime.datetime
"""
return self._published_at.value
@property
def severity(self) -> str:
"""
:type: string
"""
return self._severity.value
@property
def state(self) -> str:
"""
:type: string
"""
return self._state.value
@property
def summary(self) -> str:
"""
:type: string
"""
return self._summary.value
@property
def updated_at(self) -> datetime.datetime:
"""
:type: datetime.datetime
"""
return self._updated_at.value
@property
def url(self) -> str:
"""
:type: string
"""
return self._url.value
@property
def vulnerabilities(
self,
) -> typing.List[RepositoryAdvisoryVulnerability]:
"""
:type: list of :class:`github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability`
"""
return self._vulnerabilities.value
@property
def withdrawn_at(self) -> datetime.datetime:
"""
:type: datetime.datetime
"""
return self._withdrawn_at.value
def add_vulnerability(
self,
ecosystem: str,
package_name: typing.Optional[str] = None,
vulnerable_version_range: typing.Optional[str] = None,
patched_versions: typing.Optional[str] = None,
vulnerable_functions: typing.Optional[typing.List[str]] = None,
):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`\
:param ecosystem: string
:param package_name: string
:param vulnerable_version_range: string
:param patched_versions: string
:param vulnerable_functions: list of string
"""
return self.add_vulnerabilities(
[
{
"package": {
"ecosystem": ecosystem,
"name": package_name,
},
"vulnerable_version_range": vulnerable_version_range,
"patched_versions": patched_versions,
"vulnerable_functions": vulnerable_functions,
}
]
)
def add_vulnerabilities(
self, vulnerabilities: typing.Iterable[AdvisoryVulnerability]
):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
:param vulnerabilities: iterable of :class:`github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability`
"""
assert isinstance(vulnerabilities, typing.Iterable), vulnerabilities
for vulnerability in vulnerabilities:
# noinspection PyProtectedMember
github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability._validate_vulnerability(
vulnerability
)
# noinspection PyProtectedMember
post_parameters = {
"vulnerabilities": [
github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability._to_github_dict(
vulnerability
)
for vulnerability in (self.vulnerabilities + list(vulnerabilities))
]
}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=post_parameters,
)
self._useAttributes(data)
def offer_credit(
self,
login_or_user: typing.Union[str, "github.NamedUser.NamedUser"],
credit_type: str,
):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
Offers credit to a user for a vulnerability in a repository.
Unless you are giving credit to yourself, the user having credit offered will need to explicitly accept the credit.
:param login_or_user: string username or :class:`github.NamedUser.NamedUser`
:param credit_type: string
"""
self.offer_credits([{"login": login_or_user, "type": credit_type}])
def offer_credits(
self,
credited: typing.Iterable["Credit"],
):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
Offers credit to a list of users for a vulnerability in a repository.
Unless you are giving credit to yourself, the user having credit offered will need to explicitly accept the credit.
:param credited: iterable of dict with keys "login" and "type"
"""
assert isinstance(credited, typing.Iterable), credited
for credit in credited:
# noinspection PyProtectedMember
RepositoryAdvisoryCredit._validate_credit(credit)
# noinspection PyProtectedMember
patch_parameters = {
"credits": [
RepositoryAdvisoryCredit._to_github_dict(credit)
for credit in (self.credits + list(credited))
]
}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
def revoke_credit(
self, login_or_user: typing.Union[str, "github.NamedUser.NamedUser"]
):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`_
:param login_or_user: string username or :class:`github.NamedUser.NamedUser`
"""
assert isinstance(
login_or_user, (str, github.NamedUser.NamedUser)
), login_or_user
if isinstance(login_or_user, github.NamedUser.NamedUser):
login_or_user = login_or_user.login
patch_parameters = {
"credits": [
dict(login=credit.login, type=credit.type)
for credit in self.credits
if credit.login != login_or_user
]
}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
def clear_credits(self):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`_
"""
patch_parameters = {"credits": []}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
def edit(
self,
summary: github.GithubObject.OptionallySet[str] = github.GithubObject.NotSet,
description: github.GithubObject.OptionallySet[
str
] = github.GithubObject.NotSet,
severity_or_cvss_vector_string: github.GithubObject.OptionallySet[
str
] = github.GithubObject.NotSet,
cve_id: github.GithubObject.OptionallySet[str] = github.GithubObject.NotSet,
vulnerabilities: github.GithubObject.OptionallySet[
typing.Iterable[AdvisoryVulnerability]
] = github.GithubObject.NotSet,
cwe_ids: github.GithubObject.OptionallySet[
typing.Iterable[str]
] = github.GithubObject.NotSet,
credits: github.GithubObject.OptionallySet[
typing.Iterable[Credit]
] = github.GithubObject.NotSet,
state: github.GithubObject.OptionallySet[str] = github.GithubObject.NotSet,
) -> "RepositoryAdvisory":
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`_
:param summary: string
:param description: string
:param severity_or_cvss_vector_string: string
:param cve_id: string
:param vulnerabilities: iterable of :class:`github.RepositoryAdvisoryVulnerability.AdvisoryVulnerability`
:param cwe_ids: iterable of string
:param credits: iterable of :class:`github.RepositoryAdvisoryCredit.Credit`
:param state: string
:rtype: :class:`github.RepositoryAdvisory.RepositoryAdvisory`
"""
assert summary is github.GithubObject.NotSet or isinstance(
summary, str
), summary
assert description is github.GithubObject.NotSet or isinstance(
description, str
), description
assert (
severity_or_cvss_vector_string is github.GithubObject.NotSet
or isinstance(severity_or_cvss_vector_string, str)
), (severity_or_cvss_vector_string)
assert cve_id is github.GithubObject.NotSet or isinstance(cve_id, str), cve_id
assert vulnerabilities is github.GithubObject.NotSet or isinstance(
vulnerabilities, typing.Iterable
), vulnerabilities
if isinstance(vulnerabilities, typing.Iterable):
for vulnerability in vulnerabilities:
# noinspection PyProtectedMember
github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability._validate_vulnerability(
vulnerability
)
assert cwe_ids is github.GithubObject.NotSet or (
isinstance(cwe_ids, typing.Iterable)
and all(isinstance(element, str) for element in cwe_ids)
), cwe_ids
if isinstance(credits, typing.Iterable):
for credit in credits:
# noinspection PyProtectedMember
github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit._validate_credit(
credit
)
assert state is github.GithubObject.NotSet or isinstance(state, str), state
patch_parameters: typing.Dict[str, typing.Any] = dict()
if summary is not github.GithubObject.NotSet:
patch_parameters["summary"] = summary
if description is not github.GithubObject.NotSet:
patch_parameters["description"] = description
if isinstance(severity_or_cvss_vector_string, str):
if severity_or_cvss_vector_string.startswith("CVSS:"):
patch_parameters["cvss_vector_string"] = severity_or_cvss_vector_string
else:
patch_parameters["severity"] = severity_or_cvss_vector_string
if cve_id is not github.GithubObject.NotSet:
patch_parameters["cve_id"] = cve_id
if isinstance(vulnerabilities, typing.Iterable):
# noinspection PyProtectedMember
patch_parameters["vulnerabilities"] = [
github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability._to_github_dict(
vulnerability
)
for vulnerability in vulnerabilities
]
if isinstance(cwe_ids, typing.Iterable):
patch_parameters["cwe_ids"] = list(cwe_ids)
if isinstance(credits, typing.Iterable):
# noinspection PyProtectedMember
patch_parameters["credits"] = [
github.RepositoryAdvisoryCredit.RepositoryAdvisoryCredit._to_github_dict(
credit
)
for credit in credits
]
if state is not github.GithubObject.NotSet:
patch_parameters["state"] = state
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
return self
def accept_report(self):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
Accepts the advisory reported from an external reporter via private vulnerability reporting.
"""
patch_parameters = {"state": "draft"}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
def publish(self):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
Publishes the advisory.
"""
patch_parameters = {"state": "published"}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
def close(self):
"""
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
Closes the advisory.
"""
patch_parameters = {"state": "closed"}
headers, data = self._requester.requestJsonAndCheck(
"PATCH",
self.url,
input=patch_parameters,
)
self._useAttributes(data)
# noinspection DuplicatedCode
# noinspection PyPep8Naming
def _initAttributes(self):
self._author = github.GithubObject.NotSet
self._closed_at = github.GithubObject.NotSet
self._created_at = github.GithubObject.NotSet
self._credits = github.GithubObject.NotSet
self._credits_detailed = github.GithubObject.NotSet
self._cve_id = github.GithubObject.NotSet
self._cwe_ids = github.GithubObject.NotSet
self._cwes = github.GithubObject.NotSet
self._description = github.GithubObject.NotSet
self._ghsa_id = github.GithubObject.NotSet
self._html_url = github.GithubObject.NotSet
self._published_at = github.GithubObject.NotSet
self._severity = github.GithubObject.NotSet
self._state = github.GithubObject.NotSet
self._summary = github.GithubObject.NotSet
self._updated_at = github.GithubObject.NotSet
self._url = github.GithubObject.NotSet
self._vulnerabilities = github.GithubObject.NotSet
self._withdrawn_at = github.GithubObject.NotSet
# noinspection PyPep8Naming
def _useAttributes(self, attributes):
if "author" in attributes: # pragma no branch
self._author = self._makeClassAttribute(
github.NamedUser.NamedUser, attributes["author"]
)
if "closed_at" in attributes: # pragma no branch
assert attributes["closed_at"] is None or isinstance(
attributes["closed_at"], str
), attributes["closed_at"]
self._closed_at = self._makeDatetimeAttribute(attributes["closed_at"])
if "created_at" in attributes: # pragma no branch
assert attributes["created_at"] is None or isinstance(
attributes["created_at"], str
), attributes["created_at"]
self._created_at = self._makeDatetimeAttribute(attributes["created_at"])
if "credits" in attributes: # pragma no branch
self._credits = self._makeListOfClassesAttribute(
RepositoryAdvisoryCredit,
attributes["credits"],
)
if "credits_detailed" in attributes: # pragma no branch
self._credits_detailed = self._makeListOfClassesAttribute(
RepositoryAdvisoryCreditDetailed,
attributes["credits_detailed"],
)
if "cve_id" in attributes: # pragma no branch
self._cve_id = self._makeStringAttribute(attributes["cve_id"])
if "cwe_ids" in attributes: # pragma no branch
self._cwe_ids = self._makeListOfStringsAttribute(attributes["cwe_ids"])
if "cwes" in attributes: # pragma no branch
self._cwes = self._makeListOfClassesAttribute(CWE, attributes["cwes"])
if "description" in attributes: # pragma no branch
self._description = self._makeStringAttribute(attributes["description"])
if "ghsa_id" in attributes: # pragma no branch
self._ghsa_id = self._makeStringAttribute(attributes["ghsa_id"])
if "html_url" in attributes: # pragma no branch
self._html_url = self._makeStringAttribute(attributes["html_url"])
if "published_at" in attributes: # pragma no branch
assert attributes["published_at"] is None or isinstance(
attributes["published_at"], str
), attributes["published_at"]
self._published_at = self._makeDatetimeAttribute(attributes["published_at"])
if "severity" in attributes: # pragma no branch
self._severity = self._makeStringAttribute(attributes["severity"])
if "state" in attributes: # pragma no branch
self._state = self._makeStringAttribute(attributes["state"])
if "summary" in attributes: # pragma no branch
self._summary = self._makeStringAttribute(attributes["summary"])
if "updated_at" in attributes: # pragma no branch
assert attributes["updated_at"] is None or isinstance(
attributes["updated_at"], str
), attributes["updated_at"]
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(
RepositoryAdvisoryVulnerability,
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"]
self._withdrawn_at = self._makeDatetimeAttribute(attributes["withdrawn_at"])
+111
View File
@@ -0,0 +1,111 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
import sys
import typing
import github.GithubObject
import github.NamedUser
if sys.version_info >= (3, 8):
# TypedDict is only available in Python 3.8 and later
class SimpleCredit(typing.TypedDict):
"""
A simple credit for a security advisory.
"""
login: typing.Union[str, "github.NamedUser.NamedUser"]
type: str
else:
SimpleCredit = typing.Dict[str, typing.Any]
Credit = typing.Union[SimpleCredit, "RepositoryAdvisoryCredit"]
class RepositoryAdvisoryCredit(github.GithubObject.NonCompletableGithubObject):
"""
This class represents a credit that is assigned to a SecurityAdvisory.
The reference can be found here https://docs.github.com/en/rest/security-advisories/repository-advisories
"""
@property
def login(self) -> str:
"""
:type: string
"""
return self._login.value
@property
def type(self) -> str:
"""
:type: string
"""
return self._type.value
# noinspection PyPep8Naming
def _initAttributes(self):
self._login = github.GithubObject.NotSet
self._type = github.GithubObject.NotSet
# noinspection PyPep8Naming
def _useAttributes(self, attributes):
if "login" in attributes: # pragma no branch
self._login = self._makeStringAttribute(attributes["login"])
if "type" in attributes: # pragma no branch
self._type = self._makeStringAttribute(attributes["type"])
@staticmethod
def _validate_credit(credit: Credit) -> None:
assert isinstance(credit, (dict, RepositoryAdvisoryCredit)), credit
if isinstance(credit, dict):
assert "login" in credit, credit
assert "type" in credit, credit
assert isinstance(
credit["login"], (str, github.NamedUser.NamedUser)
), credit["login"]
assert isinstance(credit["type"], str), credit["type"]
else:
assert isinstance(credit.login, str), credit.login
assert isinstance(credit.type, str), credit.type
@staticmethod
def _to_github_dict(credit: Credit) -> SimpleCredit:
assert isinstance(credit, (dict, RepositoryAdvisoryCredit)), credit
if isinstance(credit, dict):
assert "login" in credit, credit
assert "type" in credit, credit
assert isinstance(
credit["login"], (str, github.NamedUser.NamedUser)
), credit["login"]
login = credit["login"]
if isinstance(login, github.NamedUser.NamedUser):
login = login.login
return {
"login": login,
"type": credit["type"],
}
else:
return {
"login": credit.login,
"type": credit.type,
}
@@ -0,0 +1,70 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
import github.GithubObject
import github.NamedUser
class RepositoryAdvisoryCreditDetailed(github.GithubObject.NonCompletableGithubObject):
"""
This class represents a credit that is assigned to a SecurityAdvisory.
The reference can be found here https://docs.github.com/en/rest/security-advisories/repository-advisories
"""
@property
def state(self) -> str:
"""
:type: string
"""
return self._state.value
@property
def type(self) -> str:
"""
:type: string
"""
return self._type.value
# noinspection PyPep8Naming
@property
def user(self) -> "github.NamedUser.NamedUser":
"""
:type: :class:`github.NamedUser.NamedUser`
"""
return self._user.value
# noinspection PyPep8Naming
def _initAttributes(self):
self._state = github.GithubObject.NotSet
self._type = github.GithubObject.NotSet
self._user = github.GithubObject.NotSet
# noinspection PyPep8Naming
def _useAttributes(self, attributes):
if "state" in attributes: # pragma no branch
self._state = self._makeStringAttribute(attributes["state"])
if "type" in attributes: # pragma no branch
self._type = self._makeStringAttribute(attributes["type"])
if "user" in attributes: # pragma no branch
self._user = self._makeClassAttribute(
github.NamedUser.NamedUser, attributes["user"]
)
+187
View File
@@ -0,0 +1,187 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
import sys
import typing
from typing import List
import github.GithubObject
import github.RepositoryAdvisoryVulnerabilityPackage
if sys.version_info >= (3, 8):
# TypedDict is only available in Python 3.8 and later
class SimpleAdvisoryVulnerabilityPackage(typing.TypedDict):
"""
A simple package in an advisory.
"""
ecosystem: str
name: typing.Optional[str] # TODO: Python 3.11 make 'NotRequired'
class SimpleAdvisoryVulnerability(typing.TypedDict):
"""
A simple vulnerability in a security advisory.
"""
package: SimpleAdvisoryVulnerabilityPackage
patched_versions: typing.Optional[str] # TODO: Python 3.11 make 'NotRequired'
vulnerable_functions: typing.Optional[
List[str]
] # TODO: Python 3.11 make 'NotRequired'
vulnerable_version_range: typing.Optional[
str
] # TODO: Python 3.11 make 'NotRequired'
else:
SimpleAdvisoryVulnerabilityPackage = typing.Dict[str, typing.Any]
SimpleAdvisoryVulnerability = typing.Dict[str, typing.Any]
AdvisoryVulnerability = typing.Union[
SimpleAdvisoryVulnerability, "RepositoryAdvisoryVulnerability"
]
class RepositoryAdvisoryVulnerability(github.GithubObject.NonCompletableGithubObject):
"""
This class represents 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
"""
@property
def package(
self,
) -> github.RepositoryAdvisoryVulnerabilityPackage.RepositoryAdvisoryVulnerabilityPackage:
"""
:type: :class:`github.RepositoryAdvisoryVulnerability.RepositoryAdvisoryVulnerability`
"""
return self._package.value
@property
def patched_versions(self) -> str:
"""
:type: string
"""
return self._patched_versions.value
@property
def vulnerable_functions(self) -> typing.Optional[List[str]]:
"""
:type: list of string
"""
return self._vulnerable_functions.value
@property
def vulnerable_version_range(self) -> typing.Optional[str]:
"""
:type: string
"""
return self._vulnerable_version_range.value
# noinspection PyPep8Naming
def _initAttributes(self):
self._package = github.GithubObject.NotSet
self._patched_versions = github.GithubObject.NotSet
self._vulnerable_functions = github.GithubObject.NotSet
self._vulnerable_version_range = github.GithubObject.NotSet
# noinspection PyPep8Naming
def _useAttributes(self, attributes):
if "package" in attributes: # pragma no branch
self._package = self._makeClassAttribute(
github.RepositoryAdvisoryVulnerabilityPackage.RepositoryAdvisoryVulnerabilityPackage,
attributes["package"],
)
if "patched_versions" in attributes: # pragma no branch
self._patched_versions = self._makeStringAttribute(
attributes["patched_versions"]
)
if "vulnerable_functions" in attributes: # pragma no branch
self._vulnerable_functions = self._makeListOfStringsAttribute(
attributes["vulnerable_functions"]
)
if "vulnerable_version_range" in attributes: # pragma no branch
self._vulnerable_version_range = self._makeStringAttribute(
attributes["vulnerable_version_range"]
)
@classmethod
def _validate_vulnerability(cls, vulnerability: AdvisoryVulnerability) -> None:
assert isinstance(vulnerability, (dict, cls)), vulnerability
if isinstance(vulnerability, dict):
assert "package" in vulnerability, vulnerability
package: SimpleAdvisoryVulnerabilityPackage = vulnerability["package"]
assert isinstance(package, dict), package
assert "ecosystem" in package, package
assert isinstance(package["ecosystem"], str), package
assert "name" in package, package
assert isinstance(package["name"], (str, type(None))), package
assert "patched_versions" in vulnerability, vulnerability
assert isinstance(
vulnerability["patched_versions"], (str, type(None))
), vulnerability
assert "vulnerable_functions" in vulnerability, vulnerability
assert isinstance(
vulnerability["vulnerable_functions"], (list, type(None))
), vulnerability
assert "vulnerable_functions" in vulnerability, vulnerability
assert (
all(isinstance(vf, str) for vf in vulnerability["vulnerable_functions"])
if vulnerability["vulnerable_functions"] is not None
else True
), vulnerability
assert "vulnerable_version_range" in vulnerability, vulnerability
assert isinstance(
vulnerability["vulnerable_version_range"], (str, type(None))
), vulnerability
else:
assert (
vulnerability.package
is github.RepositoryAdvisoryVulnerabilityPackage.RepositoryAdvisoryVulnerabilityPackage
), vulnerability
@staticmethod
def _to_github_dict(
vulnerability: AdvisoryVulnerability,
) -> SimpleAdvisoryVulnerability:
if isinstance(vulnerability, dict):
vulnerability_package: SimpleAdvisoryVulnerabilityPackage = vulnerability[
"package"
]
return {
"package": {
"ecosystem": vulnerability_package["ecosystem"],
"name": vulnerability_package["name"],
},
"patched_versions": vulnerability["patched_versions"],
"vulnerable_functions": vulnerability["vulnerable_functions"],
"vulnerable_version_range": vulnerability["vulnerable_version_range"],
}
return {
"package": {
"ecosystem": vulnerability.package.ecosystem,
"name": vulnerability.package.name,
},
"patched_versions": vulnerability.patched_versions,
"vulnerable_functions": vulnerability.vulnerable_functions,
"vulnerable_version_range": vulnerability.vulnerable_version_range,
}
@@ -0,0 +1,60 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
from typing import Optional
import github.GithubObject
class RepositoryAdvisoryVulnerabilityPackage(
github.GithubObject.NonCompletableGithubObject
):
"""
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
"""
@property
def ecosystem(self) -> str:
"""
:type: string
"""
return self._ecosystem.value
@property
def name(self) -> Optional[str]:
"""
:type: string or None
"""
return self._name.value
# noinspection PyPep8Naming
def _initAttributes(self):
self._ecosystem = github.GithubObject.NotSet
self._name = github.GithubObject.NotSet
# noinspection PyPep8Naming
def _useAttributes(self, attributes):
if "ecosystem" in attributes: # pragma no branch
self._ecosystem = self._makeStringAttribute(attributes["ecosystem"])
if "name" in attributes: # pragma no branch
self._name = self._makeStringAttribute(attributes["name"])