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 )