github.com/google/go-github/v74@v74.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  		EPSS: &AdvisoryEPSS{
   285  			Percentage: 0.05,
   286  			Percentile: 0.5,
   287  		},
   288  		Identifiers: []*AdvisoryIdentifier{
   289  			{
   290  				Type:  Ptr("GHSA"),
   291  				Value: Ptr("GHSA-rf4j-j272-fj86"),
   292  			},
   293  			{
   294  				Type:  Ptr("CVE"),
   295  				Value: Ptr("CVE-2018-6188"),
   296  			},
   297  		},
   298  		References: []*AdvisoryReference{
   299  			{
   300  				URL: Ptr("https://nvd.nist.gov/vuln/detail/CVE-2018-6188"),
   301  			},
   302  			{
   303  				URL: Ptr("https://github.com/advisories/GHSA-rf4j-j272-fj86"),
   304  			},
   305  			{
   306  				URL: Ptr("https://usn.ubuntu.com/3559-1/"),
   307  			},
   308  			{
   309  				URL: Ptr("https://www.djangoproject.com/weblog/2018/feb/01/security-releases/"),
   310  			},
   311  			{
   312  				URL: Ptr("http://www.securitytracker.com/id/1040422"),
   313  			},
   314  		},
   315  		PublishedAt: &Timestamp{publishedAt},
   316  		UpdatedAt:   &Timestamp{updatedAt},
   317  		WithdrawnAt: nil,
   318  	}
   319  
   320  	want := `{
   321  	  "ghsa_id": "GHSA-rf4j-j272-fj86",
   322        "cve_id": "CVE-2018-6188",
   323        "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",
   324        "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.",
   325        "vulnerabilities": [
   326          {
   327            "package": {
   328              "ecosystem": "pip",
   329              "name": "django"
   330            },
   331            "severity": "high",
   332            "vulnerable_version_range": ">= 2.0.0, < 2.0.2",
   333            "first_patched_version": {
   334              "identifier": "2.0.2"
   335            }
   336          },
   337          {
   338            "package": {
   339              "ecosystem": "pip",
   340              "name": "django"
   341            },
   342            "severity": "high",
   343            "vulnerable_version_range": ">= 1.11.8, < 1.11.10",
   344            "first_patched_version": {
   345              "identifier": "1.11.10"
   346            }
   347          }
   348        ],
   349        "severity": "high",
   350        "cvss": {
   351          "vector_string": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
   352          "score": 7.5
   353        },
   354        "cwes": [
   355          {
   356            "cwe_id": "CWE-200",
   357            "name": "Exposure of Sensitive Information to an Unauthorized Actor"
   358          }
   359        ],
   360        "epss": {
   361          "percentage": 0.05,
   362          "percentile": 0.5
   363        },
   364        "identifiers": [
   365          {
   366            "type": "GHSA",
   367            "value": "GHSA-rf4j-j272-fj86"
   368          },
   369          {
   370            "type": "CVE",
   371            "value": "CVE-2018-6188"
   372          }
   373        ],
   374        "references": [
   375          {
   376            "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-6188"
   377          },
   378          {
   379            "url": "https://github.com/advisories/GHSA-rf4j-j272-fj86"
   380          },
   381          {
   382            "url": "https://usn.ubuntu.com/3559-1/"
   383          },
   384          {
   385            "url": "https://www.djangoproject.com/weblog/2018/feb/01/security-releases/"
   386          },
   387          {
   388            "url": "http://www.securitytracker.com/id/1040422"
   389          }
   390        ],
   391        "published_at": "2018-10-03T21:13:54Z",
   392        "updated_at": "2022-04-26T18:35:37Z",
   393        "withdrawn_at": null
   394  	}`
   395  
   396  	testJSONMarshal(t, h, want)
   397  }