Skip to content

Add org-level / enterprise-level dependabot alert list #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Or for an enterprise:
| --- | --- | --- | --- | --- |
| Secret scanning | :white_check_mark: Repo<br>:white_check_mark: Org<br>:white_check_mark: Enterprise | :white_check_mark: Repo<br>:white_check_mark: Org<br>:white_check_mark: Enterprise | :white_check_mark: Repo<br>:x: Org<br>:x: Enterprise | [API docs](https://docs.github.com/en/enterprise-cloud@latest/rest/reference/secret-scanning) |
| Code scanning | :white_check_mark: Repo<br>:white_check_mark: Org<br>:white_check_mark: Enterprise | :white_check_mark: Repo<br>:white_check_mark: Org<br>:curly_loop: Enterprise | :white_check_mark: Repo<br>:x: Org<br>:curly_loop: Enterprise | [API docs](https://docs.github.com/en/enterprise-cloud@latest/rest/reference/code-scanning) |
| Dependabot | :white_check_mark: Repo<br>:x: Org<br>:x: Enterprise | :x: | :x: | [API docs](https://docs.github.com/en/enterprise-cloud@latest/rest/dependabot/alerts) |
| Dependabot | :white_check_mark: Repo<br>:white_check_mark: Org<br>:white_check_mark: Enterprise | :x: | :x: | [API docs](https://docs.github.com/en/enterprise-cloud@latest/rest/dependabot/alerts) |

:information_source: All of this reporting requires either public repositories or a GitHub Advanced Security license.

Expand Down
16 changes: 16 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,29 @@
api_endpoint, github_pat, scope_name
)
code_scanning.write_enterprise_cloud_cs_list(cs_list)
# dependabot alerts
if enterprise.get_enterprise_version(api_endpoint) == "GHEC":
dependabot_list = dependabot.list_enterprise_dependabot_alerts(
api_endpoint, github_pat, scope_name
)
dependabot.write_org_or_enterprise_dependabot_list(dependabot_list)
else:
pass

elif report_scope == "organization":
# code scanning
cs_list = code_scanning.list_org_code_scanning_alerts(
api_endpoint, github_pat, scope_name
)
code_scanning.write_org_cs_list(cs_list)
# dependabot alerts
if enterprise.get_enterprise_version(api_endpoint) == "GHEC":
dependabot_list = dependabot.list_org_dependabot_alerts(
api_endpoint, github_pat, scope_name
)
dependabot.write_org_or_enterprise_dependabot_list(dependabot_list)
else:
pass
# secret scanning
secrets_list = secret_scanning.get_org_secret_scanning_alerts(
api_endpoint, github_pat, scope_name
Expand Down
159 changes: 159 additions & 0 deletions src/dependabot.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,162 @@ def write_repo_dependabot_list(dependabot_list):
alert["security_advisory"]["cvss"]["score"],
]
)


def list_org_dependabot_alerts(api_endpoint, github_pat, org_name):
"""
Get a list of all dependabot alerts on a given organization.

Inputs:
- API endpoint (for GHES/GHAE compatibility)
- PAT of appropriate scope
- Organization name

Outputs:
- List of _all_ dependency alerts on the organization
"""

# Get dependabot alerts
url = "{}/orgs/{}/dependabot/alerts?per_page=100&page=1".format(
api_endpoint, org_name
)
response = requests.get(
url,
headers={
"Authorization": "token {}".format(github_pat),
"Accept": "application/vnd.github+json",
},
)
response_json = response.json()
while "next" in response.links.keys():
response = requests.get(
response.links["next"]["url"],
headers={"Authorization": "token {}".format(github_pat)},
)
response_json.extend(response.json())

print("Found {} dependabot alerts in {}".format(len(response_json), org_name))

# Return dependabot alerts
return response_json


def list_enterprise_dependabot_alerts(api_endpoint, github_pat, enterprise_slug):
"""
Get a list of all dependabot alerts on a given enterprise.

Inputs:
- API endpoint (for GHES/GHAE compatibility)
- PAT of appropriate scope
- Enterprise slug (enterprise name URL, documented below)
- https://docs.github.com/en/rest/reference/enterprise-admin

Outputs:
- List of _all_ dependency alerts on the enterprise
"""

# Get dependabot alerts
url = "{}/enterprises/{}/dependabot/alerts?per_page=100&page=1".format(
api_endpoint, enterprise_slug
)
response = requests.get(
url,
headers={
"Authorization": "token {}".format(github_pat),
"Accept": "application/vnd.github+json",
},
)
response_json = response.json()
while "next" in response.links.keys():
response = requests.get(
response.links["next"]["url"],
headers={"Authorization": "token {}".format(github_pat)},
)
response_json.extend(response.json())

print(
"Found {} dependabot alerts in {}".format(len(response_json), enterprise_slug)
)

# Return dependabot alerts
return response_json


def write_org_or_enterprise_dependabot_list(dependabot_list):
"""
Write the list of dependabot alerts to a CSV file.

Inputs:
- List of dependabot alerts

Outputs:
- CSV file of dependabot alerts
"""
with open("dependabot_list.csv", "w") as f:
writer = csv.writer(f)
writer.writerow(
[
"number",
"state",
"created_at",
"updated_at",
"fixed_at",
"dismissed_at",
"dismissed_by",
"dismissed_reason",
"html_url",
"dependency_manifest",
"dependency_ecosystem",
"dependency_name",
"severity",
"ghsa_id",
"cve_id",
"cvss_score",
"repo_name",
"repo_owner",
"repo_owner_type",
"repo_owner_isadmin",
"repo_url",
"repo_isfork",
"repo_isprivate",
]
)
for alert in dependabot_list:
if alert["state"] == "open":
alert["fixed_at"] = "none"
alert["dismissed_by"] = "none"
alert["dismissed_at"] = "none"
alert["dismissed_reason"] = "none"
if alert["state"] == "dismissed":
alert["fixed_at"] = "none"
if alert["state"] == "fixed":
alert["dismissed_by"] = "none"
alert["dismissed_at"] = "none"
alert["dismissed_reason"] = "none"
writer.writerow(
[
alert["number"],
alert["state"],
alert["created_at"],
alert["updated_at"],
alert["fixed_at"],
alert["dismissed_at"],
alert["dismissed_by"],
alert["dismissed_reason"],
alert["html_url"],
alert["dependency"]["manifest_path"],
alert["dependency"]["package"]["ecosystem"],
alert["dependency"]["package"]["name"],
alert["security_vulnerability"]["severity"],
alert["security_advisory"]["ghsa_id"],
alert["security_advisory"]["cve_id"],
alert["security_advisory"]["cvss"]["score"],
alert["repository"]["full_name"],
alert["repository"]["owner"]["login"],
alert["repository"]["owner"]["type"],
alert["repository"]["owner"]["site_admin"],
alert["repository"]["html_url"],
str(alert["repository"]["fork"]),
str(alert["repository"]["private"]),
]
)