1
1
from datetime import datetime
2
2
from typing import Optional
3
3
4
+ import github
4
5
from pydantic .v1 import BaseModel
5
6
6
7
from prowler .lib .logger import logger
@@ -25,7 +26,50 @@ def _file_exists(self, repo, filename):
25
26
)
26
27
return None
27
28
29
+ def _validate_repository_format (self , repo_name : str ) -> bool :
30
+ """
31
+ Validate repository name format.
32
+
33
+ Args:
34
+ repo_name: Repository name to validate
35
+
36
+ Returns:
37
+ bool: True if format is valid, False otherwise
38
+ """
39
+ if not repo_name or "/" not in repo_name :
40
+ return False
41
+
42
+ parts = repo_name .split ("/" )
43
+ if len (parts ) != 2 :
44
+ return False
45
+
46
+ owner , repo = parts
47
+ # Ensure both owner and repo names are non-empty
48
+ if not owner .strip () or not repo .strip ():
49
+ return False
50
+
51
+ return True
52
+
28
53
def _list_repositories (self ):
54
+ """
55
+ List repositories based on provider scoping configuration.
56
+
57
+ Scoping behavior:
58
+ - No scoping: Returns all accessible repositories for authenticated user
59
+ - Repository scoping: Returns only specified repositories
60
+ Example: --repository owner1/repo1 owner2/repo2
61
+ - Organization scoping: Returns all repositories from specified organizations
62
+ Example: --organization org1 org2
63
+ - Combined scoping: Returns specified repositories + all repos from organizations
64
+ Example: --repository owner1/repo1 --organization org2
65
+
66
+ Returns:
67
+ dict: Dictionary of repository ID to Repo objects
68
+
69
+ Raises:
70
+ github.GithubException: When GitHub API access fails
71
+ github.RateLimitExceededException: When API rate limits are exceeded
72
+ """
29
73
logger .info ("Repository - Listing Repositories..." )
30
74
repos = {}
31
75
try :
@@ -36,17 +80,35 @@ def _list_repositories(self):
36
80
f"Filtering for specific repositories: { self .provider .repositories } "
37
81
)
38
82
for repo_name in self .provider .repositories :
39
- if "/" not in repo_name :
83
+ if not self . _validate_repository_format ( repo_name ) :
40
84
logger .warning (
41
85
f"Repository name '{ repo_name } ' should be in 'owner/repo-name' format. Skipping."
42
86
)
43
87
continue
44
88
try :
45
89
repo = client .get_repo (repo_name )
46
90
self ._process_repository (repo , repos )
91
+ except github .GithubException as error :
92
+ if "404" in str (error ):
93
+ logger .warning (
94
+ f"Repository '{ repo_name } ' not found or not accessible"
95
+ )
96
+ elif "403" in str (error ):
97
+ logger .warning (
98
+ f"Access denied to repository '{ repo_name } ' - insufficient permissions"
99
+ )
100
+ else :
101
+ logger .error (
102
+ f"GitHub API error for repository '{ repo_name } ': { error } "
103
+ )
104
+ except github .RateLimitExceededException as error :
105
+ logger .error (
106
+ f"Rate limit exceeded while accessing repository '{ repo_name } ': { error } "
107
+ )
108
+ raise # Re-raise rate limit errors as they need special handling
47
109
except Exception as error :
48
- logger .warning (
49
- f"Repository ' { repo_name } ' not accessible or not found : { error } "
110
+ logger .error (
111
+ f"{ error . __class__ . __name__ } [ { error . __traceback__ . tb_lineno } ] : { error } "
50
112
)
51
113
52
114
if self .provider .organizations :
@@ -59,7 +121,7 @@ def _list_repositories(self):
59
121
org = client .get_organization (org_name )
60
122
for repo in org .get_repos ():
61
123
self ._process_repository (repo , repos )
62
- except Exception as org_error :
124
+ except github . GithubException as org_error :
63
125
# If organization fails, try as a user
64
126
if "404" in str (org_error ):
65
127
logger .info (
@@ -69,19 +131,48 @@ def _list_repositories(self):
69
131
user = client .get_user (org_name )
70
132
for repo in user .get_repos ():
71
133
self ._process_repository (repo , repos )
134
+ except github .GithubException as user_error :
135
+ if "404" in str (user_error ):
136
+ logger .warning (
137
+ f"'{ org_name } ' not found as organization or user"
138
+ )
139
+ elif "403" in str (user_error ):
140
+ logger .warning (
141
+ f"Access denied to '{ org_name } ' - insufficient permissions"
142
+ )
143
+ else :
144
+ logger .warning (
145
+ f"GitHub API error accessing '{ org_name } ' as user: { user_error } "
146
+ )
72
147
except Exception as user_error :
73
- logger .warning (
74
- f"' { org_name } ' not accessible as organization or user : { user_error } "
148
+ logger .error (
149
+ f"{ user_error . __class__ . __name__ } [ { user_error . __traceback__ . tb_lineno } ] : { user_error } "
75
150
)
76
- else :
151
+ elif "403" in str ( org_error ) :
77
152
logger .warning (
78
- f"Organization '{ org_name } ' not accessible: { org_error } "
153
+ f"Access denied to organization '{ org_name } ' - insufficient permissions "
79
154
)
155
+ else :
156
+ logger .error (
157
+ f"GitHub API error accessing organization '{ org_name } ': { org_error } "
158
+ )
159
+ except github .RateLimitExceededException as error :
160
+ logger .error (
161
+ f"Rate limit exceeded while processing organization '{ org_name } ': { error } "
162
+ )
163
+ raise # Re-raise rate limit errors as they need special handling
80
164
except Exception as error :
81
- logger .warning (f"Error accessing '{ org_name } ': { error } " )
165
+ logger .error (
166
+ f"{ error .__class__ .__name__ } [{ error .__traceback__ .tb_lineno } ]: { error } "
167
+ )
82
168
else :
83
169
for repo in client .get_user ().get_repos ():
84
170
self ._process_repository (repo , repos )
171
+ except github .RateLimitExceededException as error :
172
+ logger .error (f"GitHub API rate limit exceeded: { error } " )
173
+ raise # Re-raise rate limit errors as they need special handling
174
+ except github .GithubException as error :
175
+ logger .error (f"GitHub API error while listing repositories: { error } " )
85
176
except Exception as error :
86
177
logger .error (
87
178
f"{ error .__class__ .__name__ } [{ error .__traceback__ .tb_lineno } ]: { error } "
0 commit comments