fix(report): GitHub Security Alerts Integration (#970)

This commit is contained in:
Kota Kanbe
2020-04-28 14:26:37 +09:00
committed by GitHub
parent 3093426458
commit ea3b63998d

View File

@@ -5,14 +5,13 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/errof"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/k0kubun/pp"
"golang.org/x/oauth2"
)
@@ -25,8 +24,9 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
httpClient := oauth2.NewClient(context.Background(), src)
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
// Memo : https://developer.github.com/v4/explorer/
const jsonfmt = `{"query":
"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName, dismissReason, dismissedAt } } } } }"}`
"query { repository(owner:\"%s\", name:\"%s\") { url vulnerabilityAlerts(first: %d, %s) { pageInfo { endCursor hasNextPage startCursor } edges { node { id dismissReason dismissedAt securityVulnerability{ package { name ecosystem } severity vulnerableVersionRange firstPatchedVersion { identifier } } securityAdvisory { description ghsaId permalink publishedAt summary updatedAt withdrawnAt origin severity references { url } identifiers { type value } } } } } } } "}`
after := ""
for {
@@ -43,7 +43,7 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
// To toggle this preview and access data, need to provide a custom media type in the Accept header:
// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
// TODO remove this header if it is no longer preview status in the future.
req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
req.Header.Set("Accept", "application/vnd.github.package-deletes-preview+json")
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
@@ -51,16 +51,23 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
return 0, err
}
defer resp.Body.Close()
alerts := SecurityAlerts{}
if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
util.Log.Debugf("%s", pp.Sprint(alerts))
alerts := SecurityAlerts{}
if json.Unmarshal(body, &alerts); err != nil {
return 0, err
}
// util.Log.Debugf("%s", pp.Sprint(alerts))
// util.Log.Debugf("%s", string(body))
if alerts.Data.Repository.URL == "" {
return 0, errof.New(
errof.ErrFailedToAccessGithubAPI,
fmt.Sprintf("Failed to access to GitHub API. Response: %#v", alerts),
fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)),
)
}
@@ -70,31 +77,45 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
}
pkgName := fmt.Sprintf("%s %s",
alerts.Data.Repository.URL, v.Node.PackageName)
alerts.Data.Repository.URL, v.Node.SecurityVulnerability.Package.Name)
m := models.GitHubSecurityAlert{
PackageName: pkgName,
FixedIn: v.Node.FixedIn,
AffectedRange: v.Node.AffectedRange,
FixedIn: v.Node.SecurityVulnerability.FirstPatchedVersion.Identifier,
AffectedRange: v.Node.SecurityVulnerability.VulnerableVersionRange,
Dismissed: len(v.Node.DismissReason) != 0,
DismissedAt: v.Node.DismissedAt,
DismissReason: v.Node.DismissReason,
}
cveID := v.Node.ExternalIdentifier
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
r.ScannedCves[cveID] = val
nCVEs++
} else {
v := models.VulnInfo{
CveID: cveID,
Confidences: models.Confidences{models.GitHubMatch},
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
cveIDs, other := []string{}, []string{}
for _, identifier := range v.Node.SecurityAdvisory.Identifiers {
if identifier.Type == "CVE" {
cveIDs = append(cveIDs, identifier.Value)
} else {
other = append(other, identifier.Value)
}
}
// If CVE-ID has not been assigned, use the GHSA ID etc as a ID.
if len(cveIDs) == 0 {
cveIDs = other
}
for _, cveID := range cveIDs {
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
r.ScannedCves[cveID] = val
nCVEs++
} else {
v := models.VulnInfo{
CveID: cveID,
Confidences: models.Confidences{models.GitHubMatch},
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
}
r.ScannedCves[cveID] = v
nCVEs++
}
r.ScannedCves[cveID] = v
nCVEs++
}
}
if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
@@ -109,26 +130,50 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
type SecurityAlerts struct {
Data struct {
Repository struct {
URL string `json:"url,omitempty"`
URL string `json:"url"`
VulnerabilityAlerts struct {
PageInfo struct {
EndCursor string `json:"endCursor,omitempty"`
HasNextPage bool `json:"hasNextPage,omitempty"`
StartCursor string `json:"startCursor,omitempty"`
} `json:"pageInfo,omitempty"`
EndCursor string `json:"endCursor"`
HasNextPage bool `json:"hasNextPage"`
StartCursor string `json:"startCursor"`
} `json:"pageInfo"`
Edges []struct {
Node struct {
ID string `json:"id,omitempty"`
ExternalIdentifier string `json:"externalIdentifier,omitempty"`
ExternalReference string `json:"externalReference,omitempty"`
FixedIn string `json:"fixedIn,omitempty"`
AffectedRange string `json:"affectedRange,omitempty"`
PackageName string `json:"packageName,omitempty"`
DismissReason string `json:"dismissReason,omitempty"`
DismissedAt time.Time `json:"dismissedAt,omitempty"`
} `json:"node,omitempty"`
} `json:"edges,omitempty"`
} `json:"vulnerabilityAlerts,omitempty"`
} `json:"repository,omitempty"`
} `json:"data,omitempty"`
ID string `json:"id"`
DismissReason string `json:"dismissReason"`
DismissedAt time.Time `json:"dismissedAt"`
SecurityVulnerability struct {
Package struct {
Name string `json:"name"`
Ecosystem string `json:"ecosystem"`
} `json:"package"`
Severity string `json:"severity"`
VulnerableVersionRange string `json:"vulnerableVersionRange"`
FirstPatchedVersion struct {
Identifier string `json:"identifier"`
} `json:"firstPatchedVersion"`
} `json:"securityVulnerability"`
SecurityAdvisory struct {
Description string `json:"description"`
GhsaID string `json:"ghsaId"`
Permalink string `json:"permalink"`
PublishedAt time.Time `json:"publishedAt"`
Summary string `json:"summary"`
UpdatedAt time.Time `json:"updatedAt"`
WithdrawnAt time.Time `json:"withdrawnAt"`
Origin string `json:"origin"`
Severity string `json:"severity"`
References []struct {
URL string `json:"url"`
} `json:"references"`
Identifiers []struct {
Type string `json:"type"`
Value string `json:"value"`
} `json:"identifiers"`
} `json:"securityAdvisory"`
} `json:"node"`
} `json:"edges"`
} `json:"vulnerabilityAlerts"`
} `json:"repository"`
} `json:"data"`
}