Skip to content

Commit b04e287

Browse files
authored
add enterprise cloud code scanning alerts (#14)
* add first draft of enterprise code scanning alerts * update readme * update readme * troubleshoot cs_list type * split cloud and server cs functions * fix miscount in secret scanning enterprise
1 parent 6f197aa commit b04e287

File tree

4 files changed

+133
-12
lines changed

4 files changed

+133
-12
lines changed

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ An example of use is below. Note that the custom inputs, such as if you are wan
3636

3737
```yaml
3838
- name: CSV export
39-
uses: some-natalie/ghas-to-csv@v0.4.0
39+
uses: some-natalie/ghas-to-csv@v0.5.0
4040
env:
4141
GITHUB_PAT: ${{ secrets.PAT }} # you need to set a PAT
4242
- name: Upload CSV
@@ -49,10 +49,10 @@ An example of use is below. Note that the custom inputs, such as if you are wan
4949
5050
## Reporting
5151
52-
| | GitHub Enterprise Cloud | GitHub Enterprise Server (3.4) | GitHub AE (M2) | Notes |
52+
| | GitHub Enterprise Cloud | GitHub Enterprise Server (3.5) | GitHub AE (M2) | Notes |
5353
| --- | --- | --- | --- | --- |
5454
| 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) |
55-
| Code scanning | :white_check_mark: Repo<br>:white_check_mark: Org<br>:x: Enterprise | :white_check_mark: Repo<br>:x: 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) |
55+
| 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) |
5656
| Dependabot | :x: | :x: | :x: | Waiting on [this API](https://github.com/github/roadmap/issues/495) to :ship: |
5757
5858
:information_source: All of this reporting requires either public repositories or a GitHub Advanced Security license.
@@ -76,10 +76,8 @@ jobs:
7676
data_gathering:
7777
runs-on: ubuntu-latest
7878
steps:
79-
- name: Check out repo
80-
uses: actions/checkout@v3
8179
- name: CSV export
82-
uses: some-natalie/ghas-to-csv@v0.4.0
80+
uses: some-natalie/ghas-to-csv@v0.5.0
8381
env:
8482
GITHUB_PAT: ${{ secrets.PAT }} # needed if not running against the current repository
8583
SCOPE_NAME: "OWNER-NAME/REPO-NAME" # repository name, needed only if not running against the current repository

main.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,16 @@
5757
# code scanning
5858
if enterprise.get_enterprise_version(api_endpoint) != "GHEC":
5959
repo_list = enterprise.get_repo_report(url, github_pat)
60-
cs_list = code_scanning.list_enterprise_code_scanning_alerts(
60+
cs_list = code_scanning.list_enterprise_server_code_scanning_alerts(
6161
api_endpoint, github_pat, repo_list
6262
)
63-
code_scanning.write_enterprise_cs_list(cs_list)
63+
code_scanning.write_enterprise_server_cs_list(cs_list)
64+
else:
65+
cs_list = code_scanning.list_enterprise_cloud_code_scanning_alerts(
66+
api_endpoint, github_pat, scope_name
67+
)
68+
code_scanning.write_enterprise_cloud_cs_list(cs_list)
69+
6470
elif report_scope == "organization":
6571
# code scanning
6672
cs_list = code_scanning.list_org_code_scanning_alerts(

src/code_scanning.py

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def write_org_cs_list(cs_list):
223223
)
224224

225225

226-
def list_enterprise_code_scanning_alerts(api_endpoint, github_pat, repo_list):
226+
def list_enterprise_server_code_scanning_alerts(api_endpoint, github_pat, repo_list):
227227
"""
228228
Get a list of all code scanning alerts on a given enterprise.
229229
@@ -254,7 +254,7 @@ def list_enterprise_code_scanning_alerts(api_endpoint, github_pat, repo_list):
254254
return alerts
255255

256256

257-
def write_enterprise_cs_list(cs_list):
257+
def write_enterprise_server_cs_list(cs_list):
258258
"""
259259
Write a list of code scanning alerts to a csv file.
260260
@@ -274,6 +274,8 @@ def write_enterprise_cs_list(cs_list):
274274
writer = csv.writer(f)
275275
writer.writerow(
276276
[
277+
"repository",
278+
"repo_id",
277279
"number",
278280
"created_at",
279281
"html_url",
@@ -303,6 +305,8 @@ def write_enterprise_cs_list(cs_list):
303305
cs["dismissed_reason"] = "none"
304306
writer.writerow(
305307
[
308+
cs["repository"]["full_name"],
309+
cs["repository"]["id"],
306310
cs["number"],
307311
cs["created_at"],
308312
cs["html_url"],
@@ -328,3 +332,118 @@ def write_enterprise_cs_list(cs_list):
328332
with open("excluded_repos.csv", "a") as g:
329333
writer = csv.writer(g)
330334
writer.writerow([alert_list])
335+
336+
337+
def list_enterprise_cloud_code_scanning_alerts(
338+
api_endpoint, github_pat, enterprise_slug
339+
):
340+
"""
341+
Get a list of all code scanning alerts on a given enterprise.
342+
343+
Inputs:
344+
- API endpoint (for GHES/GHAE compatibility)
345+
- PAT of appropriate scope
346+
347+
Outputs:
348+
- List of _all_ code scanning alerts in enterprise that PAT user can access
349+
"""
350+
351+
# Get code scanning alerts
352+
url = "{}/enterprises/{}/code-scanning/alerts?per_page=100&page=1".format(
353+
api_endpoint, enterprise_slug
354+
)
355+
headers = {
356+
"Authorization": "token {}".format(github_pat),
357+
"Accept": "application/vnd.github.v3+json",
358+
}
359+
response = requests.get(url, headers=headers)
360+
response_json = response.json()
361+
while "next" in response.links.keys():
362+
response = requests.get(response.links["next"]["url"], headers=headers)
363+
response_json.extend(response.json())
364+
365+
print(
366+
"Found {} code scanning alerts in {}".format(
367+
len(response_json), enterprise_slug
368+
)
369+
)
370+
371+
# Return code scanning alerts
372+
return response_json
373+
374+
375+
def write_enterprise_cloud_cs_list(cs_list):
376+
"""
377+
Write a list of code scanning alerts to a csv file.
378+
379+
Inputs:
380+
- List from list_enterprise_code_scanning_alerts function, which contains
381+
strings and lists of dictionaries for the alerts.
382+
383+
Outputs:
384+
- CSV file of code scanning alerts
385+
- CSV file of repositories not accessible or without code scanning enabled
386+
"""
387+
388+
with open("cs_list.csv", "a") as f:
389+
writer = csv.writer(f)
390+
writer.writerow(
391+
[
392+
"repository",
393+
"repo_id",
394+
"number",
395+
"created_at",
396+
"html_url",
397+
"state",
398+
"fixed_at",
399+
"dismissed_by",
400+
"dismissed_at",
401+
"dismissed_reason",
402+
"rule_id",
403+
"rule_severity",
404+
"rule_tags",
405+
"rule_description",
406+
"rule_name",
407+
"tool_name",
408+
"tool_version",
409+
"most_recent_instance_ref",
410+
"most_recent_instance_state",
411+
"most_recent_instance_sha",
412+
"instances_url",
413+
]
414+
)
415+
for cs in cs_list: # loop through each alert in the list
416+
if cs["state"] == "open":
417+
cs["fixed_at"] = "none"
418+
cs["dismissed_by"] = "none"
419+
cs["dismissed_at"] = "none"
420+
cs["dismissed_reason"] = "none"
421+
writer.writerow(
422+
[
423+
cs["repository"]["full_name"],
424+
cs["repository"]["id"],
425+
cs["number"],
426+
cs["created_at"],
427+
cs["html_url"],
428+
cs["state"],
429+
cs["fixed_at"],
430+
cs["dismissed_by"],
431+
cs["dismissed_at"],
432+
cs["dismissed_reason"],
433+
cs["rule"]["id"],
434+
cs["rule"]["severity"],
435+
cs["rule"]["tags"],
436+
cs["rule"]["description"],
437+
cs["rule"]["name"],
438+
cs["tool"]["name"],
439+
cs["tool"]["version"],
440+
cs["most_recent_instance"]["ref"],
441+
cs["most_recent_instance"]["state"],
442+
cs["most_recent_instance"]["commit_sha"],
443+
cs["instances_url"],
444+
]
445+
)
446+
else:
447+
with open("excluded_repos.csv", "a") as g:
448+
writer = csv.writer(g)
449+
writer.writerow([cs])

src/secret_scanning.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,6 @@ def write_enterprise_secrets_list(secrets_list):
336336
alert["resolved_by"]["site_admin"],
337337
alert["secret_type"],
338338
alert["secret_type_display_name"],
339-
alert["secret_type"],
340-
alert["secret_type_display_name"],
341339
alert["repository"]["full_name"],
342340
alert["repository"]["owner"]["login"],
343341
alert["repository"]["owner"]["type"],

0 commit comments

Comments
 (0)