Skip to content

Commit 3d03d85

Browse files
authored
Merge pull request #12 from theztefan/main
Adding Secret Scanning out-of-the-box validity field in output
2 parents 09101a1 + f56c499 commit 3d03d85

File tree

7 files changed

+77
-41
lines changed

7 files changed

+77
-41
lines changed

.DS_Store

-6 KB
Binary file not shown.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/gh-secret-scanning
22
/gh-secret-scanning.exe
33
release-command.md
4-
Secret Scanning Report*.csv
4+
secret*.csv
5+
Secret*.csv
6+
.DS_Store

README.md

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,102 @@
1-
# Overview
1+
# gh-secret-scanning - The GitHub Secret Scanning CLI Extension
2+
23
This project is a GitHub CLI (`gh`) extension that provides commands for interacting with secret scanning alerts.
34

45
This extension helps GitHub Advanced Security (GHAS) customers prioritize remediation of their secret scanning alerts by identifying and focusing on those that are confirmed active first.
56

6-
While this extension works for Enterprise Cloud (GHEC) customers, it is primarily intended for GitHub Enterprise Server (GHES) customers who do not have access to the [GitHub.com secret scanning validity check feature](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/managing-alerts-from-secret-scanning#validating-partner-patterns).
7+
While this extension works for Enterprise Cloud (GHEC) customers, it is primarily intended for GitHub Enterprise Server (GHES) customers who do not have access to the [GitHub.com secret scanning validity check feature](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/managing-alerts-from-secret-scanning/evaluating-alerts#checking-a-secrets-validity). Validity check on GHES is available as of `>=3.12` but currently limited to GitHub Personal Access Tokens (PAT).
78

89
Primary features include:
10+
911
- Listing secret scanning alerts for an enterprise, organization, or repository
1012
- Verifying if secret scanning alerts are still active
13+
- Expand the out-of-the-box secret scanning validity checks capabilities with custom validators
1114
- Opening issues in repos that contain valid secrets
1215

13-
# Supported Token Types
16+
## Supported Token Types
17+
1418
- GitHub Personal Access Tokens (GHES + GHEC)
1519
- Slack API Tokens
1620

17-
# Pre-requisites
21+
## Pre-requisites
22+
1823
- [GitHub CLI](https://github.com/cli/cli#installation)
1924
- [GHES 3.7+](https://docs.github.com/en/enterprise-server@3.7/admin/all-releases#releases-of-github-enterprise-server) or [GHEC](https://docs.github.com/en/enterprise-cloud@latest/admin/overview/about-github-enterprise-cloud)
2025
- [GHAS](https://docs.github.com/en/enterprise-cloud@latest/get-started/learning-about-github/about-github-advanced-security)
2126

22-
# Installation
23-
```
27+
## Installation
28+
29+
```bash
2430
gh extension install CallMeGreg/gh-secret-scanning
2531
```
2632

27-
# Usage
33+
## Usage
34+
2835
Authenticate with your GitHub Enterprise Server or GitHub Enterprise Cloud account:
29-
```
36+
37+
```bash
3038
gh auth login
3139
```
3240

33-
## Alerts subcommand
41+
### Alerts subcommand
42+
3443
Target either an enterprise, organization, or repository by specifying the `-e`, `-o`, or `-r` flags respectively. _Exactly one selection from these three flags is required._
3544

36-
```
45+
```bash
3746
gh secret-scanning alerts -e <enterprise>
3847
```
3948

40-
```
49+
```bash
4150
gh secret-scanning alerts -o <organization>
4251
```
4352

44-
```
53+
```bash
4554
gh secret-scanning alerts -r <repository>
4655
```
4756

4857
Optionally add flags to specify a GHES server, limit the number of secrets processed, filter for a specific secret provider, display the secret values, generate a csv report, include extra fields, and more:
49-
```
58+
59+
```bash
5060
gh secret-scanning alerts -e github --url my-github-server.com --limit 10 --provider slack --show-secret --csv --verbose
5161
```
5262

53-
## Verify subcommand
63+
### Verify subcommand
64+
5465
Target either an enterprise, organization, or repository by specifying the `-e`, `-o`, or `-r` flags respectively. _Exactly one selection from these three flags is required._
5566

56-
```
67+
```bash
5768
gh secret-scanning verify -e <enterprise>
5869
```
5970

60-
```
71+
```bash
6172
gh secret-scanning verify -o <organization>
6273
```
6374

64-
```
75+
```bash
6576
gh secret-scanning verify -r <repository>
6677
```
6778

6879
Optionally add flags to specify a GHES server, limit the number of secrets processed, filter for a specific secret provider, display the secret values, generate a csv report, include extra fields, and more:
69-
```
80+
81+
```bash
7082
gh secret-scanning verify -e github --url my-github-server.com --limit 10 --provider slack --show-secret --csv --verbose
7183
```
7284

7385
Also, optionally create an issue in any repository that contains a valid secret by using the `--create-issues` (`-i`) flag:
74-
```
86+
87+
```bash
7588
gh secret-scanning verify -e github --url my-github-server.com --create-issues
7689
```
7790

91+
### Help
7892

79-
## Help
8093
See available commands and flags by running:
81-
```
94+
95+
```bash
8296
gh secret-scanning -h
8397
```
8498

85-
```
99+
```bash
86100
Interact with secret scanning alerts for a GHEC or GHES 3.7+ enterprise, organization, or repository
87101

88102
Usage:
@@ -109,8 +123,8 @@ Flags:
109123
Use "secret-scanning [command] --help" for more information about a command.
110124
```
111125

112-
# Demo
126+
## Demo
127+
113128
This example first lists the alerts for an organization with the `alerts` subcommand, and then verifies the secrets with the `verify` subcommand. The `--csv` flag is used to generate a csv report of the results, and the `--create-issues` flag is used to create issues in any repository that contains a valid secret.
114129

115130
https://github.com/CallMeGreg/gh-secret-scanning/assets/110078080/fa8d7b08-1a2c-4522-ae96-5c3aab60107d
116-

cmd/common.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type Alert struct {
4747
Push_protection_bypassed bool `json:"push_protection_bypassed"`
4848
Push_protection_bypassed_at string `json:"push_protection_bypassed_at"`
4949
Push_protection_bypassed_by User `json:"push_protection_bypassed_by"`
50+
Validity_github string `json:"validity"`
5051
Validity_boolean bool `json:"validity_boolean"`
5152
Validity_response_code string `json:"validity_response_code"`
5253
Validity_endpoint string `json:"validity_endpoint"`
@@ -228,22 +229,14 @@ func addRepoFullNameToAlerts(alerts []Alert) []Alert {
228229

229230
func getSecretTypeParameter() (secret_type_param string) {
230231
// if provider was specified, only return the secret types for that provider:
232+
secret_type_param = "all"
231233
if provider != "" {
232234
// get keys for SupportedProviders[provider] map:
233235
var secret_types []string
234236
for key := range SupportedProviders[provider] {
235237
secret_types = append(secret_types, key)
236238
}
237239
secret_type_param = strings.Join(secret_types, ",")
238-
} else {
239-
// get keys for every SupportedProvider key:
240-
var secret_types []string
241-
for _, supported_provider := range SupportedProviders {
242-
for key := range supported_provider {
243-
secret_types = append(secret_types, key)
244-
}
245-
}
246-
secret_type_param = strings.Join(secret_types, ",")
247240
}
248241
return secret_type_param
249242
}
@@ -272,6 +265,7 @@ func prettyPrintAlerts(alerts []Alert, validity_check bool) (err error) {
272265
t.AddField("ID", tableprinter.WithColor(Green), tableprinter.WithTruncate(nil))
273266
t.AddField("State", tableprinter.WithColor(Green), tableprinter.WithTruncate(nil))
274267
t.AddField("Secret Type", tableprinter.WithColor(Green), tableprinter.WithTruncate(nil))
268+
t.AddField("Validity GitHub", tableprinter.WithColor(Green), tableprinter.WithTruncate(nil))
275269
if secret {
276270
t.AddField("Secret", tableprinter.WithColor(Green), tableprinter.WithTruncate(nil))
277271
}
@@ -304,6 +298,7 @@ func prettyPrintAlerts(alerts []Alert, validity_check bool) (err error) {
304298
t.AddField(strconv.Itoa(alert.Number), tableprinter.WithColor(color), tableprinter.WithTruncate(nil))
305299
t.AddField(alert.State, tableprinter.WithColor(color), tableprinter.WithTruncate(nil))
306300
t.AddField(alert.Secret_type, tableprinter.WithColor(color), tableprinter.WithTruncate(nil))
301+
t.AddField(alert.Validity_github, tableprinter.WithColor(color), tableprinter.WithTruncate(nil))
307302
if secret {
308303
t.AddField(alert.Secret, tableprinter.WithColor(color), tableprinter.WithTruncate(nil))
309304
}
@@ -344,8 +339,9 @@ func generateCSVReport(alerts []Alert, scope string, validity_check bool) (err e
344339
counter := 0
345340
// get current date & time:
346341
now := time.Now()
347-
timestamp := now.Format("2006-01-02 15-04-05")
348-
filename := "Secret Scanning Report - " + scope + " - " + timestamp + ".csv"
342+
// Format the time as YYYYMMDD-HHMMSS
343+
timestamp := now.Format("20060102-150405")
344+
filename := "secretscanningreport-" + scope + "-" + timestamp + ".csv"
349345
// Create a CSV file
350346
file, err := os.Create(filename)
351347
if err != nil {
@@ -357,7 +353,7 @@ func generateCSVReport(alerts []Alert, scope string, validity_check bool) (err e
357353
writer := csv.NewWriter(file)
358354
defer writer.Flush()
359355
// Write headers to CSV file
360-
headers := []string{"Repository", "ID", "State", "Secret Type"}
356+
headers := []string{"Repository", "ID", "State", "Secret Type", "GitHub Validity"}
361357
if secret {
362358
headers = append(headers, "Secret")
363359
}
@@ -371,7 +367,7 @@ func generateCSVReport(alerts []Alert, scope string, validity_check bool) (err e
371367
// Write data to CSV file
372368
for counter < len(alerts) && counter < limit {
373369
alert := alerts[counter]
374-
row := []string{alert.Repository.Full_name, strconv.Itoa(alert.Number), alert.State, alert.Secret_type}
370+
row := []string{alert.Repository.Full_name, strconv.Itoa(alert.Number), alert.State, alert.Secret_type, alert.Validity_github}
375371
if secret {
376372
row = append(row, alert.Secret)
377373
}
@@ -393,9 +389,24 @@ func generateCSVReport(alerts []Alert, scope string, validity_check bool) (err e
393389
}
394390

395391
func verifyAlerts(alerts []Alert) (alertsOutput []Alert, err error) {
392+
// Print Supported providers for reference when verbose flag is enabled
393+
if verbose {
394+
fmt.Println(Blue("Supported Providers:"))
395+
for provider, secretTypes := range SupportedProviders {
396+
for secretType := range secretTypes {
397+
fmt.Printf("- %s - %s\n", provider, secretType)
398+
}
399+
}
400+
}
396401
for i, alert := range alerts {
397402
// verify that the alert is valid by making a request to its validation endpoint:
398403
provider := strings.Split(alert.Secret_type, "_")[0]
404+
405+
// Skip alert if provider is not supported
406+
if _, ok := SupportedProviders[provider]; !ok {
407+
continue
408+
}
409+
399410
secret_type := alert.Secret_type
400411
secret_validation_method := SupportedProviders[provider][secret_type]["HttpMethod"]
401412
secret_validation_content_type := SupportedProviders[provider][secret_type]["ContentType"]

cmd/verify.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ func runVerify(cmd *cobra.Command, args []string) (err error) {
5959
values.Set("per_page", per_page)
6060
// if provider was specified, filter results for just that provider. Otherwise, target all supported providers:
6161
secret_type := getSecretTypeParameter()
62-
values.Set("secret_type", secret_type)
63-
parsedURL.RawQuery = values.Encode()
62+
if secret_type != "all" {
63+
values.Set("secret_type", secret_type)
64+
parsedURL.RawQuery = values.Encode()
65+
}
6466

6567
// update the request path
6668
requestPath = parsedURL.String()

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/CallMeGreg/gh-secret-scanning
22

3-
go 1.19
3+
go 1.23
44

55
require (
66
github.com/cli/go-gh v1.2.1

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ github.com/cli/shurcooL-graphql v0.0.4/go.mod h1:3waN4u02FiZivIV+p1y4d0Jo1jc6BVi
1111
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
1212
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
1313
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
14+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1415
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
16+
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
1517
github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs=
1618
github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo=
1719
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
1820
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
1921
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
22+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
2023
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
2124
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
2225
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -32,6 +35,7 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt
3235
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
3336
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
3437
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
38+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3539
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
3640
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
3741
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@@ -42,6 +46,7 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh
4246
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
4347
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
4448
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
49+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
4550
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8=
4651
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI=
4752
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -55,6 +60,8 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
5560
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
5661
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5762
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
63+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5864
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
65+
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
5966
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6067
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)