@@ -148,38 +148,50 @@ struct GitHubSecretAlert {
148
148
fn alert_revoke_token (
149
149
req : & dyn RequestExt ,
150
150
alert : & GitHubSecretAlert ,
151
- ) -> Result < ( ) , Box < dyn AppError > > {
151
+ ) -> Result < GitHubSecretAlertFeedbackLabel , Box < dyn AppError > > {
152
152
let conn = req. db_write ( ) ?;
153
153
154
154
// not using ApiToken::find_by_api_token in order to preserve last_used_at
155
155
// the token field has a uniqueness constraint so get_result() should be safe to use
156
- let token: ApiToken = diesel:: update ( api_tokens:: table)
156
+ let token = diesel:: update ( api_tokens:: table)
157
157
. filter ( api_tokens:: token. eq ( alert. token . as_bytes ( ) ) )
158
158
. set ( api_tokens:: revoked. eq ( true ) )
159
- . get_result :: < ApiToken > ( & * conn) ?;
159
+ . get_result :: < ApiToken > ( & * conn)
160
+ . optional ( ) ?;
161
+
162
+ let Some ( token) = token else {
163
+ return Ok ( GitHubSecretAlertFeedbackLabel :: FalsePositive ) ;
164
+ } ;
160
165
161
166
// send email notification to the token owner
162
167
let user = User :: find ( & conn, token. user_id ) ?;
163
- info ! (
168
+ warn ! (
164
169
"Revoked API token '{}' for user {} ({})" ,
165
170
alert. token, user. gh_login, user. id
166
171
) ;
167
- match user. email ( & conn) ? {
168
- None => {
169
- info ! (
170
- "No email address for user {} ({}), cannot send email notification" ,
171
- user. gh_login, user. id
172
- ) ;
173
- Ok ( ( ) )
174
- }
175
- Some ( email) => req. app ( ) . emails . send_token_exposed_notification (
172
+
173
+ if let Some ( email) = user. email ( & conn) ? {
174
+ let result = req. app ( ) . emails . send_token_exposed_notification (
176
175
& email,
177
176
& alert. url ,
178
177
"GitHub" ,
179
178
& alert. source ,
180
179
& token. name ,
181
- ) ,
182
- }
180
+ ) ;
181
+ if let Err ( error) = result {
182
+ warn ! (
183
+ gh_login = %user. gh_login, user_id = %user. id, ?error,
184
+ "Failed to send email notification" ,
185
+ ) ;
186
+ }
187
+ } else {
188
+ warn ! (
189
+ gh_login = %user. gh_login, user_id = %user. id,
190
+ "Failed to send email notification: No address found" ,
191
+ ) ;
192
+ } ;
193
+
194
+ Ok ( GitHubSecretAlertFeedbackLabel :: TruePositive )
183
195
}
184
196
185
197
#[ derive( Deserialize , Serialize ) ]
@@ -198,8 +210,6 @@ pub enum GitHubSecretAlertFeedbackLabel {
198
210
199
211
/// Handles the `POST /api/github/secret-scanning/verify` route.
200
212
pub fn verify ( req : & mut dyn RequestExt ) -> EndpointResult {
201
- use GitHubSecretAlertFeedbackLabel :: * ;
202
-
203
213
let max_size = 8192 ;
204
214
let length = req
205
215
. content_length ( )
@@ -217,23 +227,17 @@ pub fn verify(req: &mut dyn RequestExt) -> EndpointResult {
217
227
let alerts: Vec < GitHubSecretAlert > = json:: from_slice ( & json)
218
228
. map_err ( |e| bad_request ( & format ! ( "invalid secret alert request: {e:?}" ) ) ) ?;
219
229
220
- let feedback: Vec < GitHubSecretAlertFeedback > = alerts
230
+ let feedback = alerts
221
231
. into_iter ( )
222
- . map ( |alert| GitHubSecretAlertFeedback {
223
- token_raw : alert. token . clone ( ) ,
224
- token_type : alert. r#type . clone ( ) ,
225
- label : match alert_revoke_token ( req, & alert) {
226
- Ok ( ( ) ) => TruePositive ,
227
- Err ( e) => {
228
- warn ! (
229
- "Error revoking API token in GitHub secret alert: {} ({e:?})" ,
230
- alert. token
231
- ) ;
232
- FalsePositive
233
- }
234
- } ,
232
+ . map ( |alert| {
233
+ let label = alert_revoke_token ( req, & alert) ?;
234
+ Ok ( GitHubSecretAlertFeedback {
235
+ token_raw : alert. token . clone ( ) ,
236
+ token_type : alert. r#type . clone ( ) ,
237
+ label,
238
+ } )
235
239
} )
236
- . collect ( ) ;
240
+ . collect :: < Result < Vec < GitHubSecretAlertFeedback > , Box < dyn AppError > > > ( ) ? ;
237
241
238
242
Ok ( req. json ( & feedback) )
239
243
}
0 commit comments