# Github Vulnerability Scan

Import findings from Github vulnerability scan (GraphQL Query):
[https://help.github.com/en/github/managing-security-vulnerabilities](https://help.github.com/en/github/managing-security-vulnerabilities)

Currently the parser is able to manage only `RepositoryVulnerabilityAlert` object.
The parser has some kind of search feature which detect the data in the report.

Here is the mandatory objects and attributes:

```
vulnerabilityAlerts (RepositoryVulnerabilityAlert object)
    + id
    + createdAt (optional)
    + vulnerableManifestPath
    + state (optional)
    + dependabotUpdate (DependabotUpdate object) (optional)
        + pullRequest (PullRequest object) (optional)
    + securityVulnerability (SecurityVulnerability object)
        + severity (CRITICAL/HIGH/LOW/MODERATE)
        + package (optional)
            + name (optional)
        + firstPatchedVersion (optional, sets fix_available)
            + identifier (optional)
        + advisory (SecurityAdvisory object)
            + description
                + summary
                + description
                + identifiers
                    + value
                + references (optional)
                    + url (optional)
                + cvss (optional - deprecated, use cvssSeverities instead)
                    + score (optional)
                    + vectorString (optional)
                + cvssSeverities (optional)
                    + cvssV3 (CVSS object) (optional)
                        + score (optional)
                        + vectorString (optional)
                + cwes (optional)
                + epss (EPSS object) (optional)
                    + percentage (optional)
                    + percentile (optional)
```

References:

- https://docs.github.com/en/graphql/reference/objects#repositoryvulnerabilityalert
- https://docs.github.com/en/graphql/reference/objects#securityvulnerability

Github v4 graphql query to fetch data, with extended information like the repository name and url, alert number.

```graphql
query getVulnerabilitiesByRepoAndOwner($name: String!, $owner: String!) {
  repository(name: $name, owner: $owner) {
    vulnerabilityAlerts(first: 100, after:AFTER, states: OPEN) {
      nodes {
        id
        createdAt
        vulnerableManifestPath
        securityVulnerability {
          severity
          updatedAt
          package {
            name
            ecosystem
          }
          firstPatchedVersion {
            identifier
          }
          vulnerableVersionRange
          advisory {
            description
            summary
            identifiers {
              value
              type
            }
            references {
              url
            }
            cvss {
              vectorString
            }
          }
        }
        vulnerableManifestPath
        state
        vulnerableManifestFilename
        vulnerableRequirements
        number
        dependencyScope
        dismissComment
        dismissReason
        dismissedAt
        fixedAt
      }
      totalCount
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
        startCursor
      }
    }
    nameWithOwner
    url
  }
}
```

Another example of Python script, to have a function that queries any repository, with support for paginated responses and get all findings.
Has a filter to only get OPEN dependabot alerts but this can be removed in the GraphQL query

```python
def make_query(after_cursor=None):
    return """
query getVulnerabilitiesByRepoAndOwner($name: String!, $owner: String!) {
  repository(name: $name, owner: $owner) {
    vulnerabilityAlerts(first: 100, after:AFTER, states: OPEN) {
      nodes {
        id
        createdAt
        vulnerableManifestPath
        securityVulnerability {
          severity
          updatedAt
          package {
            name
            ecosystem
          }
          firstPatchedVersion {
            identifier
          }
          vulnerableVersionRange
          advisory {
            description
            summary
            identifiers {
              value
              type
            }
            references {
              url
            }
            cvss {
              vectorString
            }
          }
        }
        vulnerableManifestPath
        state
        vulnerableManifestFilename
        vulnerableRequirements
        number
        dependencyScope
        dismissComment
        dismissReason
        dismissedAt
        fixedAt
      }
      totalCount
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
        startCursor
      }
    }
    nameWithOwner
    url
  }
}
""".replace(
        "AFTER", '"{}"'.format(after_cursor) if after_cursor else "null"
    )

# accumulates all pages data into a single object
def get_dependabot_alerts_repository(repo, owner):
    keep_fetching = True
    after_cursor = None
    output_result = {"data": {"repository": {"vulnerabilityAlerts": {"nodes": []}}}}
    while keep_fetching:
        headers = {"Authorization": AUTH_TOKEN}

request = requests.post(
            url="https://api.github.com/graphql",
            json={
                "operationName": "getVulnerabilitiesByRepoAndOwner",
                "query": make_query(after_cursor),
                "variables": {"name": repo, "owner": owner},
            },
            headers=headers,
        )

result = request.json()
        output_result["data"]["repository"]["nameWithOwner"] = result["data"]["repository"][
            "nameWithOwner"
        ]
        output_result["data"]["repository"]["url"] = result["data"]["repository"]["url"]
        if result["data"]["repository"]["vulnerabilityAlerts"]["totalCount"] == 0:
            return None

output_result["data"]["repository"]["vulnerabilityAlerts"]["nodes"] += result[
            "data"
        ]["repository"]["vulnerabilityAlerts"]["nodes"]

keep_fetching = result["data"]["repository"]["vulnerabilityAlerts"]["pageInfo"][
            "hasNextPage"
        ]
        after_cursor = result["data"]["repository"]["vulnerabilityAlerts"]["pageInfo"][
            "endCursor"
        ]
    print(
        "Fetched {} alerts for repo {}/{}".format(
            result["data"]["repository"]["vulnerabilityAlerts"]["totalCount"],
            owner,
            repo,
        )
    )
    return json.dumps(output_result, indent=2)
```

### Sample Scan Data

Sample Github Vulnerability scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/github_vulnerability).

### Default Deduplication Hashcode Fields

By default, DefectDojo identifies duplicate Findings using these [hashcode fields](https://docs.defectdojo.com/en/working_with_findings/finding_deduplication/about_deduplication/):

- title
- severity
- component name
- vulnerability ids
- file path
