/ tests / RepositoryAdvisory.py
RepositoryAdvisory.py
  1  ############################ Copyrights and license ############################
  2  #                                                                              #
  3  # Copyright 2023 Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>             #
  4  #                                                                              #
  5  # This file is part of PyGithub.                                               #
  6  # http://pygithub.readthedocs.io/                                              #
  7  #                                                                              #
  8  # PyGithub is free software: you can redistribute it and/or modify it under    #
  9  # the terms of the GNU Lesser General Public License as published by the Free  #
 10  # Software Foundation, either version 3 of the License, or (at your option)    #
 11  # any later version.                                                           #
 12  #                                                                              #
 13  # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY  #
 14  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    #
 15  # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
 16  # details.                                                                     #
 17  #                                                                              #
 18  # You should have received a copy of the GNU Lesser General Public License     #
 19  # along with PyGithub. If not, see <http://www.gnu.org/licenses/>.             #
 20  #                                                                              #
 21  ################################################################################
 22  
 23  from datetime import datetime, timezone
 24  
 25  import github.RepositoryAdvisory
 26  
 27  from . import Framework
 28  
 29  
 30  class RepositoryAdvisory(Framework.TestCase):
 31      advisory: github.RepositoryAdvisory.RepositoryAdvisory
 32  
 33      def setUp(self):
 34          super().setUp()
 35          self.repo = self.g.get_user().get_repo("security-research")
 36          self.advisory = self.repo.get_repository_advisory("GHSA-wmmh-r9w4-hpxx")
 37          self.advisory.clear_credits()
 38          self.advisory.offer_credit("octocat", "analyst")
 39  
 40      def testAttributes(self):
 41          self.assertEqual(self.advisory.author.login, "JLLeitschuh")
 42          self.assertEqual(self.advisory.closed_at, None)
 43          self.assertEqual(
 44              self.advisory.created_at,
 45              datetime(2023, 3, 28, 21, 41, 40, tzinfo=timezone.utc),
 46          )
 47          self.assertListKeyEqual(self.advisory.credits, lambda e: (e.login, e.type), [("octocat", "analyst")])
 48          self.assertListKeyEqual(
 49              self.advisory.credits_detailed,
 50              lambda e: (e.user.login, e.type),
 51              [("octocat", "analyst")],
 52          )
 53          self.assertEqual(self.advisory.cve_id, "CVE-2023-00000")
 54          self.assertListEqual(self.advisory.cwe_ids, ["CWE-400", "CWE-501"])
 55          self.assertListKeyEqual(
 56              self.advisory.cwes,
 57              lambda e: (e.cwe_id, e.name),
 58              [
 59                  ("CWE-400", "Uncontrolled Resource Consumption"),
 60                  ("CWE-501", "Trust Boundary Violation"),
 61              ],
 62          )
 63          self.assertEqual(
 64              self.advisory.description,
 65              "This is a detailed description of this advisories impact and patches.",
 66          )
 67          self.assertEqual(self.advisory.ghsa_id, "GHSA-wmmh-r9w4-hpxx")
 68          self.assertEqual(
 69              self.advisory.html_url,
 70              "https://github.com/JLLeitschuh/security-research/security/advisories/GHSA-wmmh-r9w4-hpxx",
 71          )
 72          self.assertEqual(self.advisory.published_at, None)
 73          self.assertEqual(self.advisory.severity, "high")
 74          self.assertEqual(self.advisory.state, "draft")
 75          self.assertEqual(self.advisory.summary, "A test creating a GHSA via the API")
 76          self.assertEqual(
 77              self.advisory.updated_at,
 78              datetime(2023, 3, 30, 19, 31, 33, tzinfo=timezone.utc),
 79          )
 80          self.assertEqual(
 81              self.advisory.url,
 82              "https://api.github.com/repos/JLLeitschuh/security-research/security-advisories/GHSA-wmmh-r9w4-hpxx",
 83          )
 84          self.assertListKeyEqual(
 85              self.advisory.vulnerabilities,
 86              lambda e: (
 87                  (e.package.ecosystem, e.package.name),
 88                  e.patched_versions,
 89                  e.vulnerable_functions,
 90                  e.vulnerable_version_range,
 91              ),
 92              [(("npm", "a-package"), "1.0.5", ["function-name"], ">= 1.0.2")],
 93          )
 94          self.assertEqual(self.advisory.withdrawn_at, None)
 95  
 96      def testRemoveCredit(self):
 97          self.advisory.revoke_credit("octocat")
 98          self.assertListKeyEqual(
 99              self.advisory.credits,
100              lambda e: e.login,
101              [],
102          )
103          self.assertListKeyEqual(
104              self.advisory.credits_detailed,
105              lambda e: e.user.login,
106              [],
107          )
108  
109      def testOfferCredit(self):
110          self.advisory.offer_credit("JLLeitschuh", "reporter")
111          self.assertListKeyEqual(
112              self.advisory.credits,
113              lambda e: e.login,
114              ["octocat", "JLLeitschuh"],
115          )
116          self.assertListKeyEqual(
117              self.advisory.credits_detailed,
118              lambda e: e.user.login,
119              ["octocat", "JLLeitschuh"],
120          )
121  
122      def testOfferCredits(self):
123          self.advisory.clear_credits()
124          self.advisory.offer_credits(
125              [
126                  {"login": "octocat", "type": "sponsor"},
127                  {"login": "JLLeitschuh", "type": "reporter"},
128              ]
129          )
130          self.assertListKeyEqual(
131              self.advisory.credits_detailed,
132              lambda e: (e.user.login, e.type),
133              [("octocat", "sponsor"), ("JLLeitschuh", "reporter")],
134          )
135  
136      def testRepositoryWithNoAdvisories(self):
137          repo = self.g.get_user().get_repo("PyGithub")
138          self.assertListKeyEqual(
139              repo.get_repository_advisories(),
140              lambda e: e.ghsa_id,
141              [],
142          )
143  
144      def testGetAdvisories(self):
145          self.assertListKeyEqual(
146              self.repo.get_repository_advisories(),
147              lambda e: e.ghsa_id,
148              [
149                  "GHSA-wmmh-r9w4-hpxx",
150                  "GHSA-wvgm-59wj-rh8h",
151                  "GHSA-22cq-8f5q-p5g2",
152                  "GHSA-7hfp-mpq6-2jhf",
153                  "GHSA-hfmw-fx2m-jj4c",
154                  "GHSA-rvp4-r3g6-8hxq",
155                  "GHSA-cm59-pr5q-cw85",
156                  "GHSA-vpcc-9rh2-8jfp",
157                  "GHSA-7fjx-657r-9r5h",
158                  "GHSA-22c6-wcjm-qfjg",
159                  "GHSA-5w9v-8x7x-rfqm",
160                  "GHSA-2r85-x9cf-8fcg",
161                  "GHSA-6m9h-r5m3-9r7f",
162                  "GHSA-f4jh-ww96-9h9j",
163                  "GHSA-j83w-7qr9-wv86",
164                  "GHSA-7gf3-89f6-823j",
165                  "GHSA-jpcm-4485-69p7",
166              ],
167          )
168  
169      def testCreateRepositoryAdvisory(self):
170          repo = self.g.get_repo("JLLeitschuh/code-sandbox")
171          advisory = repo.create_repository_advisory(
172              "A test creating a GHSA via the API",
173              "This is a detailed description of this advisories impact and patches.",
174              "high",
175              "CVE-2000-00000",
176              vulnerabilities=[
177                  {
178                      "package": {"ecosystem": "npm", "name": "b-package"},
179                      "vulnerable_version_range": "<=4.0.4",
180                      "patched_versions": "4.0.5",
181                      "vulnerable_functions": ["function-name"],
182                  }
183              ],
184              cwe_ids=["CWE-401", "CWE-502"],
185              credits=[
186                  {"login": "octocat", "type": "analyst"},
187                  {"login": "JLLeitschuh", "type": "reporter"},
188              ],
189          )
190          self.assertEqual(advisory.ghsa_id, "GHSA-g45c-2crh-4xmp")
191          self.assertEqual(advisory.summary, "A test creating a GHSA via the API")
192          self.assertEqual(
193              advisory.description,
194              "This is a detailed description of this advisories impact and patches.",
195          )
196          self.assertEqual(advisory.severity, "high")
197          self.assertEqual(advisory.cve_id, "CVE-2000-00000")
198          self.assertListKeyEqual(
199              advisory.vulnerabilities,
200              lambda e: (
201                  (e.package.ecosystem, e.package.name),
202                  e.patched_versions,
203                  e.vulnerable_functions,
204                  e.vulnerable_version_range,
205              ),
206              [(("npm", "b-package"), "4.0.5", ["function-name"], "<=4.0.4")],
207          )
208          self.assertListKeyEqual(
209              advisory.cwe_ids,
210              lambda e: e,
211              ["CWE-401", "CWE-502"],
212          )
213          self.assertListKeyEqual(
214              advisory.credits_detailed,
215              lambda e: (e.user.login, e.type),
216              [("octocat", "analyst"), ("JLLeitschuh", "reporter")],
217          )
218  
219      def testUpdateRepositoryAdvisory(self):
220          repo = self.g.get_repo("JLLeitschuh/code-sandbox")
221          advisory = repo.get_repository_advisory("GHSA-g45c-2crh-4xmp")
222          advisory.edit(
223              summary="A test updating a GHSA via the API",
224              description="This is an updated detailed description of this advisories impact and patches.",
225              severity_or_cvss_vector_string="low",
226              cve_id="CVE-2000-00001",
227              vulnerabilities=[
228                  {
229                      "package": {"ecosystem": "npm", "name": "c-package"},
230                      "vulnerable_version_range": "<=4.0.6",
231                      "patched_versions": "4.0.7",
232                      "vulnerable_functions": ["function-name-a"],
233                  }
234              ],
235              cwe_ids=["CWE-402", "CWE-500"],
236              credits=[
237                  {"login": "octocat", "type": "sponsor"},
238                  {"login": "JLLeitschuh", "type": "reporter"},
239              ],
240          )
241          self.assertEqual(advisory.ghsa_id, "GHSA-g45c-2crh-4xmp")
242          self.assertEqual(advisory.summary, "A test updating a GHSA via the API")
243          self.assertEqual(
244              advisory.description,
245              "This is an updated detailed description of this advisories impact and patches.",
246          )
247          self.assertEqual(advisory.severity, "low")
248          self.assertEqual(advisory.cve_id, "CVE-2000-00001")
249          self.assertListKeyEqual(
250              advisory.vulnerabilities,
251              lambda e: (
252                  (e.package.ecosystem, e.package.name),
253                  e.patched_versions,
254                  e.vulnerable_functions,
255                  e.vulnerable_version_range,
256              ),
257              [(("npm", "c-package"), "4.0.7", ["function-name-a"], "<=4.0.6")],
258          )
259          self.assertListKeyEqual(
260              advisory.cwe_ids,
261              lambda e: e,
262              ["CWE-402", "CWE-500"],
263          )
264          self.assertListKeyEqual(
265              advisory.credits_detailed,
266              lambda e: (e.user.login, e.type),
267              [("octocat", "sponsor"), ("JLLeitschuh", "reporter")],
268          )
269  
270      def testUpdateSingleFieldDoesNotRemoveOtherFields(self):
271          repo = self.g.get_repo("JLLeitschuh/code-sandbox")
272          advisory = repo.create_repository_advisory(
273              "A test editing a GHSA via the API with only a single manipulation",
274              "This is a detailed description of this advisories impact and patches.",
275              "high",
276              "CVE-2000-00000",
277              vulnerabilities=[
278                  {
279                      "package": {"ecosystem": "npm", "name": "b-package"},
280                      "vulnerable_version_range": "<=4.0.4",
281                      "patched_versions": "4.0.5",
282                      "vulnerable_functions": ["function-name"],
283                  }
284              ],
285              cwe_ids=["CWE-401", "CWE-502"],
286              credits=[
287                  {"login": "octocat", "type": "analyst"},
288                  {"login": "JLLeitschuh", "type": "reporter"},
289              ],
290          )
291          advisory.edit(description="A modified description")
292          self.assertEqual(advisory.ghsa_id, "GHSA-4wwp-8jp9-9233")
293          self.assertEqual(
294              advisory.summary,
295              "A test editing a GHSA via the API with only a single manipulation",
296          )
297          self.assertEqual(advisory.description, "A modified description")
298          self.assertEqual(advisory.severity, "high")
299          self.assertEqual(advisory.cve_id, "CVE-2000-00000")
300          self.assertListKeyEqual(
301              advisory.vulnerabilities,
302              lambda e: (
303                  (e.package.ecosystem, e.package.name),
304                  e.patched_versions,
305                  e.vulnerable_functions,
306                  e.vulnerable_version_range,
307              ),
308              [(("npm", "b-package"), "4.0.5", ["function-name"], "<=4.0.4")],
309          )
310          self.assertListKeyEqual(
311              advisory.cwe_ids,
312              lambda e: e,
313              ["CWE-401", "CWE-502"],
314          )
315          self.assertListKeyEqual(
316              advisory.credits_detailed,
317              lambda e: (e.user.login, e.type),
318              [("octocat", "analyst"), ("JLLeitschuh", "reporter")],
319          )
320  
321      def testAddVulnerability(self):
322          repo = self.g.get_repo("JLLeitschuh/code-sandbox")
323          advisory = repo.create_repository_advisory(
324              summary="A test creating a GHSA via the API adding and removing vulnerabilities",
325              description="Simple description",
326              severity_or_cvss_vector_string="low",
327          )
328          advisory.add_vulnerability(ecosystem="maven")
329          self.assertListKeyEqual(
330              advisory.vulnerabilities,
331              lambda e: (
332                  (e.package.ecosystem, e.package.name),
333                  e.patched_versions,
334                  e.vulnerable_functions,
335                  e.vulnerable_version_range,
336              ),
337              [(("maven", None), None, [], None)],
338          )
339          advisory.add_vulnerability(
340              ecosystem="npm",
341              package_name="b-package",
342              vulnerable_version_range="<=4.0.9",
343              patched_versions="4.0.10",
344              vulnerable_functions=["function-name-c"],
345          )
346          self.assertListKeyEqual(
347              advisory.vulnerabilities,
348              lambda e: (
349                  (e.package.ecosystem, e.package.name),
350                  e.patched_versions,
351                  e.vulnerable_functions,
352                  e.vulnerable_version_range,
353              ),
354              [
355                  (("maven", None), None, [], None),
356                  (("npm", "b-package"), "4.0.10", ["function-name-c"], "<=4.0.9"),
357              ],
358          )