@@ -18,12 +18,12 @@ def get_repo_ss_alerts(api_endpoint, github_pat, repo_name):
1818 - List of _all_ secret scanning alerts on the repository (both default and generic secret types)
1919 """
2020 # First call: get default secret types (without any filters), use after= to force object based cursor instead of page based
21- url_default = f"{ api_endpoint } /repos/{ repo_name } /secret-scanning/alerts?per_page=100&after="
21+ url_default = f"{ api_endpoint } /repos/{ repo_name } /secret-scanning/alerts?per_page=100&after=&hide_secret=true "
2222 ss_alerts_default = api_helpers .make_api_call (url_default , github_pat )
2323
2424 # Second call: get generic secret types with hardcoded list, use after= to force object based cursor instead of page based
2525 generic_secret_types = "password,http_basic_authentication_header,http_bearer_authentication_header,mongodb_connection_string,mysql_connection_string,openssh_private_key,pgp_private_key,postgres_connection_string,rsa_private_key"
26- url_generic = f"{ api_endpoint } /repos/{ repo_name } /secret-scanning/alerts?per_page=100&after=&secret_type={ generic_secret_types } "
26+ url_generic = f"{ api_endpoint } /repos/{ repo_name } /secret-scanning/alerts?per_page=100&after=&secret_type={ generic_secret_types } &hide_secret=true "
2727 ss_alerts_generic = api_helpers .make_api_call (url_generic , github_pat )
2828
2929 # Combine results and deduplicate
@@ -73,31 +73,79 @@ def write_repo_ss_list(secrets_list):
7373 [
7474 "number" ,
7575 "created_at" ,
76+ "updated_at" ,
7677 "html_url" ,
7778 "state" ,
7879 "resolution" ,
7980 "resolved_at" ,
8081 "resolved_by_username" ,
8182 "resolved_by_type" ,
8283 "resolved_by_isadmin" ,
84+ "resolution_comment" ,
8385 "secret_type" ,
8486 "secret_type_display_name" ,
87+ "validity" ,
88+ "publicly_leaked" ,
89+ "multi_repo" ,
90+ "is_base64_encoded" ,
91+ "first_location_path" ,
92+ "first_location_start_line" ,
93+ "first_location_commit_sha" ,
94+ "push_protection_bypassed" ,
95+ "push_protection_bypassed_by" ,
96+ "push_protection_bypassed_at" ,
97+ "push_protection_bypass_request_reviewer" ,
98+ "push_protection_bypass_request_reviewer_comment" ,
99+ "push_protection_bypass_request_comment" ,
100+ "push_protection_bypass_request_html_url" ,
101+ "assigned_to" ,
85102 ]
86103 )
87104 for alert in secrets_list :
105+ first_location = alert .get ("first_location_detected" ) or {}
106+
88107 writer .writerow (
89108 [
90109 alert ["number" ],
91110 alert ["created_at" ],
111+ alert ["updated_at" ],
92112 alert ["html_url" ],
93113 alert ["state" ],
94114 alert ["resolution" ],
95115 alert ["resolved_at" ],
96- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["login" ],
97- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["type" ],
98- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["site_admin" ],
116+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["login" ]),
117+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["type" ]),
118+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["site_admin" ]),
119+ alert .get ("resolution_comment" , "" ),
99120 alert ["secret_type" ],
100121 alert ["secret_type_display_name" ],
122+ alert ["validity" ],
123+ str (alert ["publicly_leaked" ]),
124+ str (alert ["multi_repo" ]),
125+ str (alert ["is_base64_encoded" ]),
126+ first_location .get ("path" )
127+ or first_location .get ("pull_request_body_url" )
128+ or first_location .get ("issue_body_url" )
129+ or first_location .get ("discussion_body_url" )
130+ or "" ,
131+ ("" if first_location is None else first_location .get ("start_line" , "" )),
132+ ("" if first_location is None else first_location .get ("commit_sha" , "" )),
133+ str (alert ["push_protection_bypassed" ]),
134+ (
135+ ""
136+ if alert .get ("push_protection_bypassed_by" ) is None
137+ else alert ["push_protection_bypassed_by" ].get ("login" , "" )
138+ ),
139+ alert .get ("push_protection_bypassed_at" , "" ),
140+ (
141+ ""
142+ if alert .get ("push_protection_bypass_request_reviewer" ) is None
143+ else alert ["push_protection_bypass_request_reviewer" ].get ("login" , "" )
144+ ),
145+ alert .get ("push_protection_bypass_request_reviewer_comment" , "" ),
146+ alert .get ("push_protection_bypass_request_comment" , "" ),
147+ alert .get ("push_protection_bypass_request_html_url" , "" ),
148+ ("" if alert .get ("assigned_to" ) is None else alert ["assigned_to" ].get ("login" , "" )),
101149 ]
102150 )
103151
@@ -115,32 +163,32 @@ def get_org_ss_alerts(api_endpoint, github_pat, org_name):
115163 - List of _all_ secret scanning alerts on the organization (both default and generic secret types)
116164 """
117165 # First call: get default secret types (without any filters), use after= to force object based cursor instead of page based
118- url_default = f"{ api_endpoint } /orgs/{ org_name } /secret-scanning/alerts?per_page=100&after="
166+ url_default = f"{ api_endpoint } /orgs/{ org_name } /secret-scanning/alerts?per_page=100&after=&hide_secret=true "
119167 ss_alerts_default = api_helpers .make_api_call (url_default , github_pat )
120168
121169 # Second call: get generic secret types with hardcoded list, use after= to force object based cursor instead of page based
122170 generic_secret_types = "password,http_basic_authentication_header,http_bearer_authentication_header,mongodb_connection_string,mysql_connection_string,openssh_private_key,pgp_private_key,postgres_connection_string,rsa_private_key"
123- url_generic = (
124- f"{ api_endpoint } /orgs/{ org_name } /secret-scanning/alerts?per_page=100&after=&secret_type={ generic_secret_types } "
125- )
171+ url_generic = f"{ api_endpoint } /orgs/{ org_name } /secret-scanning/alerts?per_page=100&after=&secret_type={ generic_secret_types } &hide_secret=true"
126172 ss_alerts_generic = api_helpers .make_api_call (url_generic , github_pat )
127173
128- # Combine results and deduplicate
174+ # Combine results and deduplicate using composite key (repo + alert number)
129175 combined_alerts = []
130- alert_numbers_seen = set ()
176+ alert_keys_seen = set () # Composite key: (repo, alert_number )
131177 duplicates_found = False
132178
133179 # Add default alerts
134180 for alert in ss_alerts_default :
135- alert_numbers_seen .add (alert ["number" ])
181+ alert_key = (alert ["repository" ]["full_name" ], alert ["number" ])
182+ alert_keys_seen .add (alert_key )
136183 combined_alerts .append (alert )
137184
138185 # Add generic alerts, checking for duplicates
139186 for alert in ss_alerts_generic :
140- if alert ["number" ] in alert_numbers_seen :
187+ alert_key = (alert ["repository" ]["full_name" ], alert ["number" ])
188+ if alert_key in alert_keys_seen :
141189 duplicates_found = True
142190 else :
143- alert_numbers_seen .add (alert [ "number" ] )
191+ alert_keys_seen .add (alert_key )
144192 combined_alerts .append (alert )
145193
146194 # Warn if duplicates were found
@@ -172,15 +220,32 @@ def write_org_ss_list(secrets_list):
172220 [
173221 "number" ,
174222 "created_at" ,
223+ "updated_at" ,
175224 "html_url" ,
176225 "state" ,
177226 "resolution" ,
178227 "resolved_at" ,
179228 "resolved_by_username" ,
180229 "resolved_by_type" ,
181230 "resolved_by_isadmin" ,
231+ "resolution_comment" ,
182232 "secret_type" ,
183233 "secret_type_display_name" ,
234+ "validity" ,
235+ "publicly_leaked" ,
236+ "multi_repo" ,
237+ "is_base64_encoded" ,
238+ "first_location_path" ,
239+ "first_location_start_line" ,
240+ "first_location_commit_sha" ,
241+ "push_protection_bypassed" ,
242+ "push_protection_bypassed_by" ,
243+ "push_protection_bypassed_at" ,
244+ "push_protection_bypass_request_reviewer" ,
245+ "push_protection_bypass_request_reviewer_comment" ,
246+ "push_protection_bypass_request_comment" ,
247+ "push_protection_bypass_request_html_url" ,
248+ "assigned_to" ,
184249 "repo_name" ,
185250 "repo_owner" ,
186251 "repo_owner_type" ,
@@ -191,19 +256,50 @@ def write_org_ss_list(secrets_list):
191256 ]
192257 )
193258 for alert in secrets_list :
259+ first_location = alert .get ("first_location_detected" ) or {}
260+
194261 writer .writerow (
195262 [
196263 alert ["number" ],
197264 alert ["created_at" ],
265+ alert ["updated_at" ],
198266 alert ["html_url" ],
199267 alert ["state" ],
200268 alert ["resolution" ],
201269 alert ["resolved_at" ],
202- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["login" ],
203- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["type" ],
204- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["site_admin" ],
270+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["login" ]),
271+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["type" ]),
272+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["site_admin" ]),
273+ alert .get ("resolution_comment" , "" ),
205274 alert ["secret_type" ],
206275 alert ["secret_type_display_name" ],
276+ alert ["validity" ],
277+ str (alert ["publicly_leaked" ]),
278+ str (alert ["multi_repo" ]),
279+ str (alert ["is_base64_encoded" ]),
280+ first_location .get ("path" )
281+ or first_location .get ("pull_request_body_url" )
282+ or first_location .get ("issue_body_url" )
283+ or first_location .get ("discussion_body_url" )
284+ or "" ,
285+ ("" if first_location is None else first_location .get ("start_line" , "" )),
286+ ("" if first_location is None else first_location .get ("commit_sha" , "" )),
287+ str (alert ["push_protection_bypassed" ]),
288+ (
289+ ""
290+ if alert .get ("push_protection_bypassed_by" ) is None
291+ else alert ["push_protection_bypassed_by" ].get ("login" , "" )
292+ ),
293+ alert .get ("push_protection_bypassed_at" , "" ),
294+ (
295+ ""
296+ if alert .get ("push_protection_bypass_request_reviewer" ) is None
297+ else alert ["push_protection_bypass_request_reviewer" ].get ("login" , "" )
298+ ),
299+ alert .get ("push_protection_bypass_request_reviewer_comment" , "" ),
300+ alert .get ("push_protection_bypass_request_comment" , "" ),
301+ alert .get ("push_protection_bypass_request_html_url" , "" ),
302+ ("" if alert .get ("assigned_to" ) is None else alert ["assigned_to" ].get ("login" , "" )),
207303 alert ["repository" ]["full_name" ],
208304 alert ["repository" ]["owner" ]["login" ],
209305 alert ["repository" ]["owner" ]["type" ],
@@ -229,30 +325,42 @@ def get_enterprise_ss_alerts(api_endpoint, github_pat, enterprise_slug):
229325 - List of _all_ secret scanning alerts on the enterprise (both default and generic secret types)
230326 """
231327 # First call: get default secret types (without any filters), use after= to force object based cursor instead of page based
232- url_default = f"{ api_endpoint } /enterprises/{ enterprise_slug } /secret-scanning/alerts?per_page=100&after="
328+ url_default = (
329+ f"{ api_endpoint } /enterprises/{ enterprise_slug } /secret-scanning/alerts?per_page=100&after=&hide_secret=true"
330+ )
233331 ss_alerts_default = api_helpers .make_api_call (url_default , github_pat )
234332
235333 # Second call: get generic secret types with hardcoded list, use after= to force object based cursor instead of page based
236334 generic_secret_types = "password,http_basic_authentication_header,http_bearer_authentication_header,mongodb_connection_string,mysql_connection_string,openssh_private_key,pgp_private_key,postgres_connection_string,rsa_private_key"
237- url_generic = f"{ api_endpoint } /enterprises/{ enterprise_slug } /secret-scanning/alerts?per_page=100&after=&secret_type={ generic_secret_types } "
335+ url_generic = f"{ api_endpoint } /enterprises/{ enterprise_slug } /secret-scanning/alerts?per_page=100&after=&secret_type={ generic_secret_types } &hide_secret=true "
238336 ss_alerts_generic = api_helpers .make_api_call (url_generic , github_pat )
239337
240- # Combine results and deduplicate
338+ # Combine results and deduplicate using composite key (org + repo + alert number)
241339 combined_alerts = []
242- alert_numbers_seen = set ()
340+ alert_keys_seen = set () # Composite key: (org, repo, alert_number )
243341 duplicates_found = False
244342
245343 # Add default alerts
246344 for alert in ss_alerts_default :
247- alert_numbers_seen .add (alert ["number" ])
345+ alert_key = (
346+ alert ["repository" ]["owner" ]["login" ],
347+ alert ["repository" ]["name" ],
348+ alert ["number" ],
349+ )
350+ alert_keys_seen .add (alert_key )
248351 combined_alerts .append (alert )
249352
250353 # Add generic alerts, checking for duplicates
251354 for alert in ss_alerts_generic :
252- if alert ["number" ] in alert_numbers_seen :
355+ alert_key = (
356+ alert ["repository" ]["owner" ]["login" ],
357+ alert ["repository" ]["name" ],
358+ alert ["number" ],
359+ )
360+ if alert_key in alert_keys_seen :
253361 duplicates_found = True
254362 else :
255- alert_numbers_seen .add (alert [ "number" ] )
363+ alert_keys_seen .add (alert_key )
256364 combined_alerts .append (alert )
257365
258366 # Warn if duplicates were found
@@ -284,15 +392,32 @@ def write_enterprise_ss_list(secrets_list):
284392 [
285393 "number" ,
286394 "created_at" ,
395+ "updated_at" ,
287396 "html_url" ,
288397 "state" ,
289398 "resolution" ,
290399 "resolved_at" ,
291400 "resolved_by_username" ,
292401 "resolved_by_type" ,
293402 "resolved_by_isadmin" ,
403+ "resolution_comment" ,
294404 "secret_type" ,
295405 "secret_type_display_name" ,
406+ "validity" ,
407+ "publicly_leaked" ,
408+ "multi_repo" ,
409+ "is_base64_encoded" ,
410+ "first_location_path" ,
411+ "first_location_start_line" ,
412+ "first_location_commit_sha" ,
413+ "push_protection_bypassed" ,
414+ "push_protection_bypassed_by" ,
415+ "push_protection_bypassed_at" ,
416+ "push_protection_bypass_request_reviewer" ,
417+ "push_protection_bypass_request_reviewer_comment" ,
418+ "push_protection_bypass_request_comment" ,
419+ "push_protection_bypass_request_html_url" ,
420+ "assigned_to" ,
296421 "repo_name" ,
297422 "repo_owner" ,
298423 "repo_owner_type" ,
@@ -303,19 +428,50 @@ def write_enterprise_ss_list(secrets_list):
303428 ]
304429 )
305430 for alert in secrets_list :
431+ first_location = alert .get ("first_location_detected" ) or {}
432+
306433 writer .writerow (
307434 [
308435 alert ["number" ],
309436 alert ["created_at" ],
437+ alert ["updated_at" ],
310438 alert ["html_url" ],
311439 alert ["state" ],
312440 alert ["resolution" ],
313441 alert ["resolved_at" ],
314- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["login" ],
315- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["type" ],
316- "" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["site_admin" ],
442+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["login" ]),
443+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["type" ]),
444+ ("" if alert ["resolved_by" ] is None else alert ["resolved_by" ]["site_admin" ]),
445+ alert .get ("resolution_comment" , "" ),
317446 alert ["secret_type" ],
318447 alert ["secret_type_display_name" ],
448+ alert ["validity" ],
449+ str (alert ["publicly_leaked" ]),
450+ str (alert ["multi_repo" ]),
451+ str (alert ["is_base64_encoded" ]),
452+ first_location .get ("path" )
453+ or first_location .get ("pull_request_body_url" )
454+ or first_location .get ("issue_body_url" )
455+ or first_location .get ("discussion_body_url" )
456+ or "" ,
457+ ("" if first_location is None else first_location .get ("start_line" , "" )),
458+ ("" if first_location is None else first_location .get ("commit_sha" , "" )),
459+ str (alert ["push_protection_bypassed" ]),
460+ (
461+ ""
462+ if alert .get ("push_protection_bypassed_by" ) is None
463+ else alert ["push_protection_bypassed_by" ].get ("login" , "" )
464+ ),
465+ alert .get ("push_protection_bypassed_at" , "" ),
466+ (
467+ ""
468+ if alert .get ("push_protection_bypass_request_reviewer" ) is None
469+ else alert ["push_protection_bypass_request_reviewer" ].get ("login" , "" )
470+ ),
471+ alert .get ("push_protection_bypass_request_reviewer_comment" , "" ),
472+ alert .get ("push_protection_bypass_request_comment" , "" ),
473+ alert .get ("push_protection_bypass_request_html_url" , "" ),
474+ ("" if alert .get ("assigned_to" ) is None else alert ["assigned_to" ].get ("login" , "" )),
319475 alert ["repository" ]["full_name" ],
320476 alert ["repository" ]["owner" ]["login" ],
321477 alert ["repository" ]["owner" ]["type" ],
0 commit comments