feat: Support archived repo exclusion from GH org scans (#4875)
* feat: Support archived repo exclusion from GH org scans Signed-off-by: Shubham Hibare <[email protected]> * feat: Support archived repo exclusion from GH org scans Signed-off-by: Shubham Hibare <[email protected]> * fix checks Signed-off-by: Shubham Hibare <[email protected]> --------- Signed-off-by: Shubham Hibare <[email protected]> Signed-off-by: Shubham Hibare <[email protected]> Co-authored-by: Shubham Hibare <[email protected]> Co-authored-by: Shahzad Haider <[email protected]>
This commit is contained in:
co-authored by
Shubham Hibare
Shahzad Haider
parent
f845f184e4
commit
d7dcc6d3fe
@@ -212,7 +212,13 @@ Timestamp: 2022-06-16 10:17:40 -0700 PDT
|
||||
trufflehog github --org=trufflesecurity --results=verified
|
||||
```
|
||||
|
||||
## 3: Scan a GitHub Repo for only verified secrets and get JSON output
|
||||
## 3: Scan a GitHub Org excluding archived repositories
|
||||
|
||||
```bash
|
||||
trufflehog github --org=trufflesecurity --exclude-archived
|
||||
```
|
||||
|
||||
## 4: Scan a GitHub Repo for only verified secrets and get JSON output
|
||||
|
||||
Command:
|
||||
|
||||
@@ -227,37 +233,37 @@ Expected output:
|
||||
...
|
||||
```
|
||||
|
||||
## 4: Scan a GitHub Repo + its Issues and Pull Requests
|
||||
## 5: Scan a GitHub Repo + its Issues and Pull Requests
|
||||
|
||||
```bash
|
||||
trufflehog github --repo=https://github.com/trufflesecurity/test_keys --issue-comments --pr-comments
|
||||
```
|
||||
|
||||
## 5: Scan an S3 bucket for high-confidence results (verified + unknown)
|
||||
## 6: Scan an S3 bucket for high-confidence results (verified + unknown)
|
||||
|
||||
```bash
|
||||
trufflehog s3 --bucket=<bucket name> --results=verified,unknown
|
||||
```
|
||||
|
||||
## 6: Scan S3 buckets using IAM Roles
|
||||
## 7: Scan S3 buckets using IAM Roles
|
||||
|
||||
```bash
|
||||
trufflehog s3 --role-arn=<iam role arn>
|
||||
```
|
||||
|
||||
## 7: Scan a Github Repo using SSH authentication in Docker
|
||||
## 8: Scan a Github Repo using SSH authentication in Docker
|
||||
|
||||
```bash
|
||||
docker run --rm -v "$HOME/.ssh:/root/.ssh:ro" trufflesecurity/trufflehog:latest git ssh://github.com/trufflesecurity/test_keys
|
||||
```
|
||||
|
||||
## 8: Scan individual files or directories
|
||||
## 9: Scan individual files or directories
|
||||
|
||||
```bash
|
||||
trufflehog filesystem path/to/file1.txt path/to/file2.txt path/to/dir
|
||||
```
|
||||
|
||||
## 9: Scan a local git repo
|
||||
## 10: Scan a local git repo
|
||||
|
||||
Clone the git repo. For example [test keys]([email protected]:trufflesecurity/test_keys.git) repo.
|
||||
```bash
|
||||
@@ -271,13 +277,13 @@ trufflehog git file://test_keys --results=verified,unknown
|
||||
|
||||
To guard against malicious git configs in local scanning (see CVE-2025-41390), TruffleHog clones local git repositories to a temporary directory prior to scanning. This follows [Git's security best practices](https://git-scm.com/docs/git#_security). If you want to specify a custom path to clone the repository to (instead of tmp), you can use the `--clone-path` flag. If you'd like to skip the local cloning process and scan the repository directly (only do this for trusted repos), you can use the `--trust-local-git-config` flag.
|
||||
|
||||
## 10: Scan GCS buckets for only verified secrets
|
||||
## 11: Scan GCS buckets for only verified secrets
|
||||
|
||||
```bash
|
||||
trufflehog gcs --project-id=<project-ID> --cloud-environment --results=verified
|
||||
```
|
||||
|
||||
## 11: Scan a Docker image for only verified secrets
|
||||
## 12: Scan a Docker image for only verified secrets
|
||||
|
||||
Use the `--image` flag multiple times to scan multiple images.
|
||||
|
||||
@@ -292,7 +298,7 @@ trufflehog docker --image docker://new_image:tag --results=verified
|
||||
trufflehog docker --image file://path_to_image.tar --results=verified
|
||||
```
|
||||
|
||||
## 12: Scan in CI
|
||||
## 13: Scan in CI
|
||||
|
||||
Set the `--since-commit` flag to your default branch that people merge into (ex: "main"). Set the `--branch` flag to your PR's branch name (ex: "feature-1"). Depending on the CI/CD platform you use, this value can be pulled in dynamically (ex: [CIRCLE_BRANCH in Circle CI](https://circleci.com/docs/variables/) and [TRAVIS_PULL_REQUEST_BRANCH in Travis CI](https://docs.travis-ci.com/user/environment-variables/)). If the repo is cloned and the target branch is already checked out during the CI/CD workflow, then `--branch HEAD` should be sufficient. The `--fail` flag will return an 183 error code if valid credentials are found.
|
||||
|
||||
@@ -300,7 +306,7 @@ Set the `--since-commit` flag to your default branch that people merge into (ex:
|
||||
trufflehog git file://. --since-commit main --branch feature-1 --results=verified,unknown --fail
|
||||
```
|
||||
|
||||
## 13: Scan a Postman workspace
|
||||
## 14: Scan a Postman workspace
|
||||
|
||||
Use the `--workspace-id`, `--collection-id`, `--environment` flags multiple times to scan multiple targets.
|
||||
|
||||
@@ -308,13 +314,13 @@ Use the `--workspace-id`, `--collection-id`, `--environment` flags multiple time
|
||||
trufflehog postman --token=<postman api token> --workspace-id=<workspace id>
|
||||
```
|
||||
|
||||
## 14: Scan a Jenkins server
|
||||
## 15: Scan a Jenkins server
|
||||
|
||||
```bash
|
||||
trufflehog jenkins --url https://jenkins.example.com --username admin --password admin
|
||||
```
|
||||
|
||||
## 15: Scan an Elasticsearch server
|
||||
## 16: Scan an Elasticsearch server
|
||||
|
||||
### Scan a Local Cluster
|
||||
|
||||
@@ -342,7 +348,7 @@ trufflehog elasticsearch \
|
||||
--api-key 'MlVtVjBZ...ZSYlduYnF1djh3NG5FQQ=='
|
||||
```
|
||||
|
||||
## 16. Scan a GitHub Repository for Cross Fork Object References and Deleted Commits
|
||||
## 17. Scan a GitHub Repository for Cross Fork Object References and Deleted Commits
|
||||
|
||||
The following command will enumerate deleted and hidden commits on a GitHub repository and then scan them for secrets. This is an alpha release feature.
|
||||
|
||||
@@ -356,7 +362,7 @@ In addition to the normal TruffleHog output, the `--object-discovery` flag creat
|
||||
|
||||
For more information on Cross Fork Object References, please [read our blog post](https://trufflesecurity.com/blog/anyone-can-access-deleted-and-private-repo-data-github).
|
||||
|
||||
## 17. Scan Hugging Face
|
||||
## 18. Scan Hugging Face
|
||||
|
||||
### Scan a Hugging Face Model, Dataset, Space or Bucket
|
||||
|
||||
@@ -382,7 +388,7 @@ trufflehog huggingface --org <orgname> --user <username>
|
||||
trufflehog huggingface --model <model_id> --include-discussions --include-prs
|
||||
```
|
||||
|
||||
## 18. Scan stdin Input
|
||||
## 19. Scan stdin Input
|
||||
|
||||
```bash
|
||||
aws s3 cp s3://example/gzipped/data.gz - | gunzip -c | trufflehog stdin
|
||||
|
||||
@@ -227,6 +227,9 @@ Do not delete cloned repositories after scanning (can only be used with --clone-
|
||||
.TP
|
||||
\fB--ignore-gists\fR
|
||||
Ignore all gists in scan.
|
||||
.TP
|
||||
\fB--exclude-archived\fR
|
||||
Exclude archived repositories from scan.
|
||||
.SS
|
||||
\fBgithub-experimental --repo=REPO [<flags>]\fR
|
||||
Run an experimental GitHub scan. Must specify at least one experimental sub-module to run: object-discovery.
|
||||
|
||||
@@ -130,6 +130,7 @@ var (
|
||||
githubClonePath = githubScan.Flag("clone-path", "Custom path where the repository should be cloned (default: temp dir).").String()
|
||||
githubNoCleanup = githubScan.Flag("no-cleanup", "Do not delete cloned repositories after scanning (can only be used with --clone-path).").Bool()
|
||||
githubIgnoreGists = githubScan.Flag("ignore-gists", "Ignore all gists in scan.").Bool()
|
||||
githubExcludeArchived = githubScan.Flag("exclude-archived", "Exclude archived repositories from scan.").Bool()
|
||||
|
||||
// GitHub Cross Fork Object Reference Experimental Feature
|
||||
githubExperimentalScan = cli.Command("github-experimental", "Run an experimental GitHub scan. Must specify at least one experimental sub-module to run: object-discovery.")
|
||||
@@ -915,6 +916,7 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
ClonePath: *githubClonePath,
|
||||
NoCleanup: *githubNoCleanup,
|
||||
IgnoreGists: *githubIgnoreGists,
|
||||
ExcludeArchived: *githubExcludeArchived,
|
||||
PrintLegacyJSON: *jsonLegacy,
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ func (e *Engine) ScanGitHub(ctx context.Context, c sources.GithubConfig) (source
|
||||
ClonePath: c.ClonePath,
|
||||
NoCleanup: c.NoCleanup,
|
||||
IgnoreGists: c.IgnoreGists,
|
||||
ExcludeArchived: c.ExcludeArchived,
|
||||
PrintLegacyJson: c.PrintLegacyJSON,
|
||||
}
|
||||
|
||||
|
||||
+511
-500
File diff suppressed because it is too large
Load Diff
@@ -2592,6 +2592,8 @@ func (m *GitHub) validate(all bool) error {
|
||||
|
||||
// no validation rules for ScanAllInstallations
|
||||
|
||||
// no validation rules for ExcludeArchived
|
||||
|
||||
switch v := m.Credential.(type) {
|
||||
case *GitHub_GithubApp:
|
||||
if v == nil {
|
||||
|
||||
@@ -72,6 +72,86 @@ func TestSource_Token(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestSource_ExcludeArchived(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*300)
|
||||
defer cancel()
|
||||
|
||||
secret, err := common.GetTestSecret(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("failed to access secret: %v", err))
|
||||
}
|
||||
|
||||
githubPrivateKeyB64New := secret.MustGetField("GITHUB_PRIVATE_KEY_NEW")
|
||||
githubPrivateKeyBytesNew, err := base64.StdEncoding.DecodeString(githubPrivateKeyB64New)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
githubPrivateKeyNew := string(githubPrivateKeyBytesNew)
|
||||
githubInstallationIDNew := secret.MustGetField("GITHUB_INSTALLATION_ID_NEW")
|
||||
githubAppIDNew := secret.MustGetField("GITHUB_APP_ID_NEW")
|
||||
|
||||
srcWithExclude := &sourcespb.GitHub{
|
||||
Endpoint: "https://api.github.com",
|
||||
Credential: &sourcespb.GitHub_GithubApp{
|
||||
GithubApp: &credentialspb.GitHubApp{
|
||||
PrivateKey: githubPrivateKeyNew,
|
||||
InstallationId: githubInstallationIDNew,
|
||||
AppId: githubAppIDNew,
|
||||
},
|
||||
},
|
||||
ExcludeArchived: true,
|
||||
}
|
||||
connWithExclude, err := anypb.New(srcWithExclude)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sWithExclude := Source{
|
||||
conn: srcWithExclude,
|
||||
memberCache: map[string]struct{}{},
|
||||
repoInfoCache: newRepoInfoCache(),
|
||||
}
|
||||
sWithExclude.Init(ctx, "github integration test source with exclude", 0, 0, false, connWithExclude, 1)
|
||||
sWithExclude.filteredRepoCache = sWithExclude.newFilteredRepoCache(ctx, simple.NewCache[string](), nil, nil)
|
||||
|
||||
err = sWithExclude.enumerateWithApp(ctx, sWithExclude.connector.(*appConnector), noopReporter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
srcWithoutExclude := &sourcespb.GitHub{
|
||||
Endpoint: "https://api.github.com",
|
||||
Credential: &sourcespb.GitHub_GithubApp{
|
||||
GithubApp: &credentialspb.GitHubApp{
|
||||
PrivateKey: githubPrivateKeyNew,
|
||||
InstallationId: githubInstallationIDNew,
|
||||
AppId: githubAppIDNew,
|
||||
},
|
||||
},
|
||||
ExcludeArchived: false,
|
||||
}
|
||||
connWithoutExclude, err := anypb.New(srcWithoutExclude)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sWithoutExclude := Source{
|
||||
conn: srcWithoutExclude,
|
||||
memberCache: map[string]struct{}{},
|
||||
repoInfoCache: newRepoInfoCache(),
|
||||
}
|
||||
sWithoutExclude.Init(ctx, "github integration test source without exclude", 0, 0, false, connWithoutExclude, 1)
|
||||
sWithoutExclude.filteredRepoCache = sWithoutExclude.newFilteredRepoCache(ctx, simple.NewCache[string](), nil, nil)
|
||||
|
||||
err = sWithoutExclude.enumerateWithApp(ctx, sWithoutExclude.connector.(*appConnector), noopReporter())
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Less(t, sWithExclude.filteredRepoCache.Count(), sWithoutExclude.filteredRepoCache.Count(),
|
||||
"ExcludeArchived should result in strictly fewer repos (test org must have at least one archived repo). WithExclude: %d, WithoutExclude: %d",
|
||||
sWithExclude.filteredRepoCache.Count(), sWithoutExclude.filteredRepoCache.Count())
|
||||
|
||||
t.Logf("Repos with ExcludeArchived=true: %d", sWithExclude.filteredRepoCache.Count())
|
||||
t.Logf("Repos with ExcludeArchived=false: %d", sWithoutExclude.filteredRepoCache.Count())
|
||||
}
|
||||
|
||||
func TestSource_ScanComments(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
@@ -2431,3 +2431,170 @@ func TestExtractRepoNameFromURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSource_ExcludeArchivedRepositories(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
excludeArchived bool
|
||||
reposJSON string
|
||||
wantRepoCount int
|
||||
wantRepos []string
|
||||
}{
|
||||
{
|
||||
name: "exclude archived repos when flag is true",
|
||||
excludeArchived: true,
|
||||
reposJSON: `[
|
||||
{"full_name": "test-org/active-repo", "clone_url": "https://github.com/test-org/active-repo.git", "size": 1, "archived": false},
|
||||
{"full_name": "test-org/archived-repo", "clone_url": "https://github.com/test-org/archived-repo.git", "size": 1, "archived": true},
|
||||
{"full_name": "test-org/another-active", "clone_url": "https://github.com/test-org/another-active.git", "size": 1, "archived": false}
|
||||
]`,
|
||||
wantRepoCount: 2, // Only non-archived
|
||||
wantRepos: []string{"test-org/active-repo", "test-org/another-active"},
|
||||
},
|
||||
{
|
||||
name: "include archived repos when flag is false",
|
||||
excludeArchived: false,
|
||||
reposJSON: `[
|
||||
{"full_name": "test-org/active-repo", "clone_url": "https://github.com/test-org/active-repo.git", "size": 1, "archived": false},
|
||||
{"full_name": "test-org/archived-repo", "clone_url": "https://github.com/test-org/archived-repo.git", "size": 1, "archived": true},
|
||||
{"full_name": "test-org/another-active", "clone_url": "https://github.com/test-org/another-active.git", "size": 1, "archived": false}
|
||||
]`,
|
||||
wantRepoCount: 3, // All repos
|
||||
wantRepos: []string{"test-org/active-repo", "test-org/archived-repo", "test-org/another-active"},
|
||||
},
|
||||
{
|
||||
name: "handle all archived repos",
|
||||
excludeArchived: true,
|
||||
reposJSON: `[
|
||||
{"full_name": "test-org/archived-1", "clone_url": "https://github.com/test-org/archived-1.git", "size": 1, "archived": true},
|
||||
{"full_name": "test-org/archived-2", "clone_url": "https://github.com/test-org/archived-2.git", "size": 1, "archived": true}
|
||||
]`,
|
||||
wantRepoCount: 0, // None included
|
||||
wantRepos: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer gock.Off()
|
||||
|
||||
gock.New("https://api.github.com").
|
||||
Get("/orgs/test-org/repos").
|
||||
Reply(200).
|
||||
JSON(tt.reposJSON)
|
||||
|
||||
s := initTestSource(&sourcespb.GitHub{
|
||||
Credential: &sourcespb.GitHub_Token{
|
||||
Token: "test-token",
|
||||
},
|
||||
ExcludeArchived: tt.excludeArchived,
|
||||
})
|
||||
|
||||
err := s.getReposByOrg(context.Background(), "test-org", noopReporter())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tt.wantRepoCount, s.filteredRepoCache.Count())
|
||||
|
||||
// Verify expected repos are in cache
|
||||
for _, repo := range tt.wantRepos {
|
||||
ok := s.filteredRepoCache.Exists(repo)
|
||||
assert.True(t, ok, "expected repo %s to be in cache", repo)
|
||||
}
|
||||
|
||||
// Verify archived repos are NOT in cache when excluding
|
||||
if tt.excludeArchived {
|
||||
allRepos := []string{"test-org/archived-repo", "test-org/archived-1", "test-org/archived-2"}
|
||||
for _, repo := range allRepos {
|
||||
if !slices.Contains(tt.wantRepos, repo) {
|
||||
ok := s.filteredRepoCache.Exists(repo)
|
||||
assert.False(t, ok, "archived repo %s should not be in cache when excluding", repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.False(t, gock.HasUnmatchedRequest())
|
||||
assert.True(t, gock.IsDone())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSource_ExcludeArchivedForkInteraction(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
includeForks bool
|
||||
excludeArchived bool
|
||||
reposJSON string
|
||||
wantRepoCount int
|
||||
wantRepos []string
|
||||
notWantRepos []string
|
||||
}{
|
||||
{
|
||||
name: "fork+archived skipped when forks included and archived excluded",
|
||||
includeForks: true,
|
||||
excludeArchived: true,
|
||||
reposJSON: `[
|
||||
{"full_name": "test-org/active", "clone_url": "https://github.com/test-org/active.git", "size": 1, "archived": false, "fork": false},
|
||||
{"full_name": "test-org/fork-active", "clone_url": "https://github.com/test-org/fork-active.git", "size": 1, "archived": false, "fork": true},
|
||||
{"full_name": "test-org/fork-archived", "clone_url": "https://github.com/test-org/fork-archived.git", "size": 1, "archived": true, "fork": true}
|
||||
]`,
|
||||
wantRepoCount: 2,
|
||||
wantRepos: []string{"test-org/active", "test-org/fork-active"},
|
||||
notWantRepos: []string{"test-org/fork-archived"},
|
||||
},
|
||||
{
|
||||
name: "fork+archived included when archived allowed",
|
||||
includeForks: true,
|
||||
excludeArchived: false,
|
||||
reposJSON: `[
|
||||
{"full_name": "test-org/active", "clone_url": "https://github.com/test-org/active.git", "size": 1, "archived": false, "fork": false},
|
||||
{"full_name": "test-org/fork-archived", "clone_url": "https://github.com/test-org/fork-archived.git", "size": 1, "archived": true, "fork": true}
|
||||
]`,
|
||||
wantRepoCount: 2,
|
||||
wantRepos: []string{"test-org/active", "test-org/fork-archived"},
|
||||
},
|
||||
{
|
||||
name: "fork+archived dropped by fork filter when forks excluded",
|
||||
includeForks: false,
|
||||
excludeArchived: true,
|
||||
reposJSON: `[
|
||||
{"full_name": "test-org/active", "clone_url": "https://github.com/test-org/active.git", "size": 1, "archived": false, "fork": false},
|
||||
{"full_name": "test-org/fork-archived", "clone_url": "https://github.com/test-org/fork-archived.git", "size": 1, "archived": true, "fork": true}
|
||||
]`,
|
||||
wantRepoCount: 1,
|
||||
wantRepos: []string{"test-org/active"},
|
||||
notWantRepos: []string{"test-org/fork-archived"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer gock.Off()
|
||||
|
||||
gock.New("https://api.github.com").
|
||||
Get("/orgs/test-org/repos").
|
||||
Reply(200).
|
||||
JSON(tt.reposJSON)
|
||||
|
||||
s := initTestSource(&sourcespb.GitHub{
|
||||
Credential: &sourcespb.GitHub_Token{
|
||||
Token: "test-token",
|
||||
},
|
||||
IncludeForks: tt.includeForks,
|
||||
ExcludeArchived: tt.excludeArchived,
|
||||
})
|
||||
|
||||
err := s.getReposByOrg(context.Background(), "test-org", noopReporter())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tt.wantRepoCount, s.filteredRepoCache.Count())
|
||||
|
||||
for _, repo := range tt.wantRepos {
|
||||
assert.True(t, s.filteredRepoCache.Exists(repo), "expected repo %s to be in cache", repo)
|
||||
}
|
||||
for _, repo := range tt.notWantRepos {
|
||||
assert.False(t, s.filteredRepoCache.Exists(repo), "repo %s should not be in cache", repo)
|
||||
}
|
||||
|
||||
assert.False(t, gock.HasUnmatchedRequest())
|
||||
assert.True(t, gock.IsDone())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +246,9 @@ func (s *Source) processRepos(ctx context.Context, target string, reporter sourc
|
||||
opts := listOpts.getListOptions()
|
||||
|
||||
var (
|
||||
numRepos, numForks int
|
||||
uniqueOrgs = map[string]struct{}{}
|
||||
numRepos, numForks, numArchived int
|
||||
numArchivedSkipped int
|
||||
uniqueOrgs = map[string]struct{}{}
|
||||
)
|
||||
|
||||
// loop to handle pagination.
|
||||
@@ -262,12 +263,26 @@ func (s *Source) processRepos(ctx context.Context, target string, reporter sourc
|
||||
|
||||
ctx.Logger().V(2).Info("Listed repos", "page", opts.Page, "last_page", res.LastPage)
|
||||
for _, r := range someRepos {
|
||||
if r.GetFork() {
|
||||
if !s.conn.IncludeForks {
|
||||
continue
|
||||
}
|
||||
isFork, isArchived := r.GetFork(), r.GetArchived()
|
||||
|
||||
if isFork && !s.conn.IncludeForks {
|
||||
continue
|
||||
}
|
||||
|
||||
if isArchived && s.conn.ExcludeArchived {
|
||||
numArchivedSkipped++
|
||||
logger.V(3).Info("skipping archived repository", "repo", r.GetFullName())
|
||||
continue
|
||||
}
|
||||
|
||||
if isFork {
|
||||
numForks++
|
||||
}
|
||||
|
||||
if isArchived {
|
||||
numArchived++
|
||||
}
|
||||
|
||||
numRepos++
|
||||
|
||||
// track unique organizations.
|
||||
@@ -299,7 +314,7 @@ func (s *Source) processRepos(ctx context.Context, target string, reporter sourc
|
||||
}
|
||||
|
||||
// final logging of repository stats.
|
||||
logger.V(2).Info("found repos", "total", numRepos, "num_forks", numForks, "num_orgs", len(uniqueOrgs))
|
||||
logger.V(2).Info("found repos", "total", numRepos, "num_forks", numForks, "num_archived", numArchived, "num_archived_skipped", numArchivedSkipped, "num_orgs", len(uniqueOrgs))
|
||||
githubOrgsEnumerated.WithLabelValues(s.name).Add(float64(len(uniqueOrgs)))
|
||||
|
||||
return nil
|
||||
|
||||
@@ -338,6 +338,8 @@ type GithubConfig struct {
|
||||
// NoCleanup allows to keeps cloned repositories in ClonePath after scanning instead of removing them.
|
||||
NoCleanup bool
|
||||
IgnoreGists bool
|
||||
// ExcludeArchived indicates whether to exclude archived repositories from the scan.
|
||||
ExcludeArchived bool
|
||||
// PrintLegacyJSON indicates whether to print legacy JSON output format for this source.
|
||||
PrintLegacyJSON bool
|
||||
}
|
||||
|
||||
@@ -283,6 +283,7 @@ message GitHub {
|
||||
bool print_legacy_json = 25;
|
||||
// When using GitHub App credentials, enumerate repositories from every app installation.
|
||||
bool scan_all_installations = 26;
|
||||
bool exclude_archived = 27;
|
||||
}
|
||||
|
||||
message GitHubExperimental {
|
||||
|
||||
Reference in New Issue
Block a user