github.com/google/go-github/v70@v70.0.0/github/dependabot_alerts_test.go (about) 1 // Copyright 2022 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "context" 10 "fmt" 11 "net/http" 12 "testing" 13 "time" 14 15 "github.com/google/go-cmp/cmp" 16 ) 17 18 func TestDependabotService_ListRepoAlerts(t *testing.T) { 19 t.Parallel() 20 client, mux, _ := setup(t) 21 22 mux.HandleFunc("/repos/o/r/dependabot/alerts", func(w http.ResponseWriter, r *http.Request) { 23 testMethod(t, r, "GET") 24 testFormValues(t, r, values{"state": "open"}) 25 fmt.Fprint(w, `[{"number":1,"state":"open"},{"number":42,"state":"fixed"}]`) 26 }) 27 28 opts := &ListAlertsOptions{State: Ptr("open")} 29 ctx := context.Background() 30 alerts, _, err := client.Dependabot.ListRepoAlerts(ctx, "o", "r", opts) 31 if err != nil { 32 t.Errorf("Dependabot.ListRepoAlerts returned error: %v", err) 33 } 34 35 want := []*DependabotAlert{ 36 {Number: Ptr(1), State: Ptr("open")}, 37 {Number: Ptr(42), State: Ptr("fixed")}, 38 } 39 if !cmp.Equal(alerts, want) { 40 t.Errorf("Dependabot.ListRepoAlerts returned %+v, want %+v", alerts, want) 41 } 42 43 const methodName = "ListRepoAlerts" 44 testBadOptions(t, methodName, func() (err error) { 45 _, _, err = client.Dependabot.ListRepoAlerts(ctx, "\n", "\n", opts) 46 return err 47 }) 48 49 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 50 got, resp, err := client.Dependabot.ListRepoAlerts(ctx, "o", "r", opts) 51 if got != nil { 52 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 53 } 54 return resp, err 55 }) 56 } 57 58 func TestDependabotService_GetRepoAlert(t *testing.T) { 59 t.Parallel() 60 client, mux, _ := setup(t) 61 62 mux.HandleFunc("/repos/o/r/dependabot/alerts/42", func(w http.ResponseWriter, r *http.Request) { 63 testMethod(t, r, "GET") 64 fmt.Fprint(w, `{"number":42,"state":"fixed"}`) 65 }) 66 67 ctx := context.Background() 68 alert, _, err := client.Dependabot.GetRepoAlert(ctx, "o", "r", 42) 69 if err != nil { 70 t.Errorf("Dependabot.GetRepoAlert returned error: %v", err) 71 } 72 73 want := &DependabotAlert{ 74 Number: Ptr(42), 75 State: Ptr("fixed"), 76 } 77 if !cmp.Equal(alert, want) { 78 t.Errorf("Dependabot.GetRepoAlert returned %+v, want %+v", alert, want) 79 } 80 81 const methodName = "GetRepoAlert" 82 testBadOptions(t, methodName, func() (err error) { 83 _, _, err = client.Dependabot.GetRepoAlert(ctx, "\n", "\n", 0) 84 return err 85 }) 86 87 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 88 got, resp, err := client.Dependabot.GetRepoAlert(ctx, "o", "r", 42) 89 if got != nil { 90 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 91 } 92 return resp, err 93 }) 94 } 95 96 func TestDependabotService_ListOrgAlerts(t *testing.T) { 97 t.Parallel() 98 client, mux, _ := setup(t) 99 100 mux.HandleFunc("/orgs/o/dependabot/alerts", func(w http.ResponseWriter, r *http.Request) { 101 testMethod(t, r, "GET") 102 testFormValues(t, r, values{"state": "open"}) 103 fmt.Fprint(w, `[{"number":1,"state":"open"},{"number":42,"state":"fixed"}]`) 104 }) 105 106 opts := &ListAlertsOptions{State: Ptr("open")} 107 ctx := context.Background() 108 alerts, _, err := client.Dependabot.ListOrgAlerts(ctx, "o", opts) 109 if err != nil { 110 t.Errorf("Dependabot.ListOrgAlerts returned error: %v", err) 111 } 112 113 want := []*DependabotAlert{ 114 {Number: Ptr(1), State: Ptr("open")}, 115 {Number: Ptr(42), State: Ptr("fixed")}, 116 } 117 if !cmp.Equal(alerts, want) { 118 t.Errorf("Dependabot.ListOrgAlerts returned %+v, want %+v", alerts, want) 119 } 120 121 const methodName = "ListOrgAlerts" 122 testBadOptions(t, methodName, func() (err error) { 123 _, _, err = client.Dependabot.ListOrgAlerts(ctx, "\n", opts) 124 return err 125 }) 126 127 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 128 got, resp, err := client.Dependabot.ListOrgAlerts(ctx, "o", opts) 129 if got != nil { 130 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 131 } 132 return resp, err 133 }) 134 } 135 136 func TestDependabotService_UpdateAlert(t *testing.T) { 137 t.Parallel() 138 client, mux, _ := setup(t) 139 140 state := Ptr("dismissed") 141 dismissedReason := Ptr("no_bandwidth") 142 dismissedComment := Ptr("no time to fix this") 143 144 alertState := &DependabotAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment} 145 146 mux.HandleFunc("/repos/o/r/dependabot/alerts/42", func(w http.ResponseWriter, r *http.Request) { 147 testMethod(t, r, "PATCH") 148 fmt.Fprint(w, `{"number":42,"state":"dismissed","dismissed_reason":"no_bandwidth","dismissed_comment":"no time to fix this"}`) 149 }) 150 151 ctx := context.Background() 152 alert, _, err := client.Dependabot.UpdateAlert(ctx, "o", "r", 42, alertState) 153 if err != nil { 154 t.Errorf("Dependabot.UpdateAlert returned error: %v", err) 155 } 156 157 want := &DependabotAlert{ 158 Number: Ptr(42), 159 State: Ptr("dismissed"), 160 DismissedReason: Ptr("no_bandwidth"), 161 DismissedComment: Ptr("no time to fix this"), 162 } 163 if !cmp.Equal(alert, want) { 164 t.Errorf("Dependabot.UpdateAlert returned %+v, want %+v", alert, want) 165 } 166 167 const methodName = "UpdateAlert" 168 testBadOptions(t, methodName, func() (err error) { 169 _, _, err = client.Dependabot.UpdateAlert(ctx, "\n", "\n", 0, alertState) 170 return err 171 }) 172 173 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 174 got, resp, err := client.Dependabot.UpdateAlert(ctx, "o", "r", 42, alertState) 175 if got != nil { 176 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 177 } 178 return resp, err 179 }) 180 } 181 182 func TestDependency_Marshal(t *testing.T) { 183 t.Parallel() 184 testJSONMarshal(t, &Dependency{}, "{}") 185 186 h := &Dependency{ 187 Package: &VulnerabilityPackage{ 188 Ecosystem: Ptr("pip"), 189 Name: Ptr("django"), 190 }, 191 ManifestPath: Ptr("path/to/requirements.txt"), 192 Scope: Ptr("runtime"), 193 } 194 195 want := `{ 196 "package": { 197 "ecosystem": "pip", 198 "name": "django" 199 }, 200 "manifest_path": "path/to/requirements.txt", 201 "scope": "runtime" 202 }` 203 204 testJSONMarshal(t, h, want) 205 } 206 207 func TestAdvisoryCVSS_Marshal(t *testing.T) { 208 t.Parallel() 209 testJSONMarshal(t, &AdvisoryCVSS{}, "{}") 210 211 h := &AdvisoryCVSS{ 212 Score: Ptr(7.5), 213 VectorString: Ptr("CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"), 214 } 215 216 want := `{ 217 "vector_string": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", 218 "score": 7.5 219 }` 220 221 testJSONMarshal(t, h, want) 222 } 223 224 func TestAdvisoryCWEs_Marshal(t *testing.T) { 225 t.Parallel() 226 testJSONMarshal(t, &AdvisoryCWEs{}, "{}") 227 228 h := &AdvisoryCWEs{ 229 CWEID: Ptr("CWE-200"), 230 Name: Ptr("Exposure of Sensitive Information to an Unauthorized Actor"), 231 } 232 233 want := `{ 234 "cwe_id": "CWE-200", 235 "name": "Exposure of Sensitive Information to an Unauthorized Actor" 236 }` 237 238 testJSONMarshal(t, h, want) 239 } 240 241 func TestDependabotSecurityAdvisory_Marshal(t *testing.T) { 242 t.Parallel() 243 testJSONMarshal(t, &DependabotSecurityAdvisory{}, "{}") 244 245 publishedAt, _ := time.Parse(time.RFC3339, "2018-10-03T21:13:54Z") 246 updatedAt, _ := time.Parse(time.RFC3339, "2022-04-26T18:35:37Z") 247 248 h := &DependabotSecurityAdvisory{ 249 GHSAID: Ptr("GHSA-rf4j-j272-fj86"), 250 CVEID: Ptr("CVE-2018-6188"), 251 Summary: Ptr("Django allows remote attackers to obtain potentially sensitive information by leveraging data exposure from the confirm_login_allowed() method, as demonstrated by discovering whether a user account is inactive"), 252 Description: Ptr("django.contrib.auth.forms.AuthenticationForm in Django 2.0 before 2.0.2, and 1.11.8 and 1.11.9, allows remote attackers to obtain potentially sensitive information by leveraging data exposure from the confirm_login_allowed() method, as demonstrated by discovering whether a user account is inactive."), 253 Vulnerabilities: []*AdvisoryVulnerability{ 254 { 255 Package: &VulnerabilityPackage{ 256 Ecosystem: Ptr("pip"), 257 Name: Ptr("django"), 258 }, 259 Severity: Ptr("high"), 260 VulnerableVersionRange: Ptr(">= 2.0.0, < 2.0.2"), 261 FirstPatchedVersion: &FirstPatchedVersion{Identifier: Ptr("2.0.2")}, 262 }, 263 { 264 Package: &VulnerabilityPackage{ 265 Ecosystem: Ptr("pip"), 266 Name: Ptr("django"), 267 }, 268 Severity: Ptr("high"), 269 VulnerableVersionRange: Ptr(">= 1.11.8, < 1.11.10"), 270 FirstPatchedVersion: &FirstPatchedVersion{Identifier: Ptr("1.11.10")}, 271 }, 272 }, 273 Severity: Ptr("high"), 274 CVSS: &AdvisoryCVSS{ 275 Score: Ptr(7.5), 276 VectorString: Ptr("CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"), 277 }, 278 CWEs: []*AdvisoryCWEs{ 279 { 280 CWEID: Ptr("CWE-200"), 281 Name: Ptr("Exposure of Sensitive Information to an Unauthorized Actor"), 282 }, 283 }, 284 Identifiers: []*AdvisoryIdentifier{ 285 { 286 Type: Ptr("GHSA"), 287 Value: Ptr("GHSA-rf4j-j272-fj86"), 288 }, 289 { 290 Type: Ptr("CVE"), 291 Value: Ptr("CVE-2018-6188"), 292 }, 293 }, 294 References: []*AdvisoryReference{ 295 { 296 URL: Ptr("https://nvd.nist.gov/vuln/detail/CVE-2018-6188"), 297 }, 298 { 299 URL: Ptr("https://github.com/advisories/GHSA-rf4j-j272-fj86"), 300 }, 301 { 302 URL: Ptr("https://usn.ubuntu.com/3559-1/"), 303 }, 304 { 305 URL: Ptr("https://www.djangoproject.com/weblog/2018/feb/01/security-releases/"), 306 }, 307 { 308 URL: Ptr("http://www.securitytracker.com/id/1040422"), 309 }, 310 }, 311 PublishedAt: &Timestamp{publishedAt}, 312 UpdatedAt: &Timestamp{updatedAt}, 313 WithdrawnAt: nil, 314 } 315 316 want := `{ 317 "ghsa_id": "GHSA-rf4j-j272-fj86", 318 "cve_id": "CVE-2018-6188", 319 "summary": "Django allows remote attackers to obtain potentially sensitive information by leveraging data exposure from the confirm_login_allowed() method, as demonstrated by discovering whether a user account is inactive", 320 "description": "django.contrib.auth.forms.AuthenticationForm in Django 2.0 before 2.0.2, and 1.11.8 and 1.11.9, allows remote attackers to obtain potentially sensitive information by leveraging data exposure from the confirm_login_allowed() method, as demonstrated by discovering whether a user account is inactive.", 321 "vulnerabilities": [ 322 { 323 "package": { 324 "ecosystem": "pip", 325 "name": "django" 326 }, 327 "severity": "high", 328 "vulnerable_version_range": ">= 2.0.0, < 2.0.2", 329 "first_patched_version": { 330 "identifier": "2.0.2" 331 } 332 }, 333 { 334 "package": { 335 "ecosystem": "pip", 336 "name": "django" 337 }, 338 "severity": "high", 339 "vulnerable_version_range": ">= 1.11.8, < 1.11.10", 340 "first_patched_version": { 341 "identifier": "1.11.10" 342 } 343 } 344 ], 345 "severity": "high", 346 "cvss": { 347 "vector_string": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", 348 "score": 7.5 349 }, 350 "cwes": [ 351 { 352 "cwe_id": "CWE-200", 353 "name": "Exposure of Sensitive Information to an Unauthorized Actor" 354 } 355 ], 356 "identifiers": [ 357 { 358 "type": "GHSA", 359 "value": "GHSA-rf4j-j272-fj86" 360 }, 361 { 362 "type": "CVE", 363 "value": "CVE-2018-6188" 364 } 365 ], 366 "references": [ 367 { 368 "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-6188" 369 }, 370 { 371 "url": "https://github.com/advisories/GHSA-rf4j-j272-fj86" 372 }, 373 { 374 "url": "https://usn.ubuntu.com/3559-1/" 375 }, 376 { 377 "url": "https://www.djangoproject.com/weblog/2018/feb/01/security-releases/" 378 }, 379 { 380 "url": "http://www.securitytracker.com/id/1040422" 381 } 382 ], 383 "published_at": "2018-10-03T21:13:54Z", 384 "updated_at": "2022-04-26T18:35:37Z", 385 "withdrawn_at": null 386 }` 387 388 testJSONMarshal(t, h, want) 389 }