Skip to content

Commit 4067970

Browse files
committed
github/secret_scanning: Treat missing token as Ok
If GitHub reports a potential token we should treat false positives as legitimate cases instead of errors. Only real errors like failing to revoke a token should be considered errors.
1 parent e105eac commit 4067970

File tree

1 file changed

+36
-32
lines changed

1 file changed

+36
-32
lines changed

src/controllers/github/secret_scanning.rs

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -148,38 +148,50 @@ struct GitHubSecretAlert {
148148
fn alert_revoke_token(
149149
req: &dyn RequestExt,
150150
alert: &GitHubSecretAlert,
151-
) -> Result<(), Box<dyn AppError>> {
151+
) -> Result<GitHubSecretAlertFeedbackLabel, Box<dyn AppError>> {
152152
let conn = req.db_write()?;
153153

154154
// not using ApiToken::find_by_api_token in order to preserve last_used_at
155155
// 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)
157157
.filter(api_tokens::token.eq(alert.token.as_bytes()))
158158
.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+
};
160165

161166
// send email notification to the token owner
162167
let user = User::find(&conn, token.user_id)?;
163-
info!(
168+
warn!(
164169
"Revoked API token '{}' for user {} ({})",
165170
alert.token, user.gh_login, user.id
166171
);
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(
176175
&email,
177176
&alert.url,
178177
"GitHub",
179178
&alert.source,
180179
&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)
183195
}
184196

185197
#[derive(Deserialize, Serialize)]
@@ -198,8 +210,6 @@ pub enum GitHubSecretAlertFeedbackLabel {
198210

199211
/// Handles the `POST /api/github/secret-scanning/verify` route.
200212
pub fn verify(req: &mut dyn RequestExt) -> EndpointResult {
201-
use GitHubSecretAlertFeedbackLabel::*;
202-
203213
let max_size = 8192;
204214
let length = req
205215
.content_length()
@@ -217,23 +227,17 @@ pub fn verify(req: &mut dyn RequestExt) -> EndpointResult {
217227
let alerts: Vec<GitHubSecretAlert> = json::from_slice(&json)
218228
.map_err(|e| bad_request(&format!("invalid secret alert request: {e:?}")))?;
219229

220-
let feedback: Vec<GitHubSecretAlertFeedback> = alerts
230+
let feedback = alerts
221231
.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+
})
235239
})
236-
.collect();
240+
.collect::<Result<Vec<GitHubSecretAlertFeedback>, Box<dyn AppError>>>()?;
237241

238242
Ok(req.json(&feedback))
239243
}

0 commit comments

Comments
 (0)