@@ -19,7 +19,7 @@ use axum::{
19
19
Router ,
20
20
} ;
21
21
use clowarden_core:: cfg:: Organization ;
22
- use config:: Config ;
22
+ use config:: { Config , ConfigError } ;
23
23
use hmac:: { Hmac , Mac } ;
24
24
use mime:: APPLICATION_JSON ;
25
25
use octorust:: types:: JobStatus ;
@@ -59,6 +59,7 @@ struct RouterState {
59
59
db : DynDB ,
60
60
gh : DynGH ,
61
61
webhook_secret : String ,
62
+ webhook_secret_fallback : Option < String > ,
62
63
jobs_tx : mpsc:: UnboundedSender < Job > ,
63
64
orgs : Vec < Organization > ,
64
65
}
@@ -71,7 +72,7 @@ pub(crate) fn setup_router(
71
72
jobs_tx : mpsc:: UnboundedSender < Job > ,
72
73
) -> Result < Router > {
73
74
// Setup some paths
74
- let static_path = cfg. get_string ( "server.staticPath" ) . unwrap ( ) ;
75
+ let static_path = cfg. get_string ( "server.staticPath" ) ? ;
75
76
let root_index_path = Path :: new ( & static_path) . join ( "index.html" ) ;
76
77
let audit_path = Path :: new ( & static_path) . join ( "audit" ) ;
77
78
let audit_index_path = audit_path. join ( "index.html" ) ;
@@ -107,7 +108,12 @@ pub(crate) fn setup_router(
107
108
108
109
// Setup main router
109
110
let orgs = cfg. get ( "organizations" ) ?;
110
- let webhook_secret = cfg. get_string ( "server.githubApp.webhookSecret" ) . unwrap ( ) ;
111
+ let webhook_secret = cfg. get_string ( "server.githubApp.webhookSecret" ) ?;
112
+ let webhook_secret_fallback = match cfg. get_string ( "server.githubApp.webhookSecretFallback" ) {
113
+ Ok ( secret) => Some ( secret) ,
114
+ Err ( ConfigError :: NotFound ( _) ) => None ,
115
+ Err ( err) => return Err ( err. into ( ) ) ,
116
+ } ;
111
117
let router = Router :: new ( )
112
118
. route ( "/webhook/github" , post ( event) )
113
119
. route ( "/health-check" , get ( health_check) )
@@ -128,6 +134,7 @@ pub(crate) fn setup_router(
128
134
db,
129
135
gh,
130
136
webhook_secret,
137
+ webhook_secret_fallback,
131
138
jobs_tx,
132
139
orgs,
133
140
} ) ;
@@ -147,15 +154,19 @@ async fn health_check() -> impl IntoResponse {
147
154
async fn event (
148
155
State ( gh) : State < DynGH > ,
149
156
State ( webhook_secret) : State < String > ,
157
+ State ( webhook_secret_fallback) : State < Option < String > > ,
150
158
State ( jobs_tx) : State < mpsc:: UnboundedSender < Job > > ,
151
159
State ( orgs) : State < Vec < Organization > > ,
152
160
headers : HeaderMap ,
153
161
body : Bytes ,
154
162
) -> impl IntoResponse {
155
163
// Verify payload signature
164
+ let webhook_secret = webhook_secret. as_bytes ( ) ;
165
+ let webhook_secret_fallback = webhook_secret_fallback. as_ref ( ) . map ( String :: as_bytes) ;
156
166
if verify_signature (
157
167
headers. get ( GITHUB_SIGNATURE_HEADER ) ,
158
- webhook_secret. as_bytes ( ) ,
168
+ webhook_secret,
169
+ webhook_secret_fallback,
159
170
& body[ ..] ,
160
171
)
161
172
. is_err ( )
@@ -279,14 +290,31 @@ async fn search_changes(State(db): State<DynDB>, RawQuery(query): RawQuery) -> i
279
290
}
280
291
281
292
/// Verify that the signature provided is valid.
282
- fn verify_signature ( signature : Option < & HeaderValue > , secret : & [ u8 ] , body : & [ u8 ] ) -> Result < ( ) > {
293
+ fn verify_signature (
294
+ signature : Option < & HeaderValue > ,
295
+ secret : & [ u8 ] ,
296
+ secret_fallback : Option < & [ u8 ] > ,
297
+ body : & [ u8 ] ,
298
+ ) -> Result < ( ) > {
283
299
if let Some ( signature) = signature
284
300
. and_then ( |s| s. to_str ( ) . ok ( ) )
285
301
. and_then ( |s| s. strip_prefix ( "sha256=" ) )
286
302
. and_then ( |s| hex:: decode ( s) . ok ( ) )
287
303
{
304
+ // Try primary secret
288
305
let mut mac = Hmac :: < Sha256 > :: new_from_slice ( secret) ?;
289
306
mac. update ( body) ;
307
+ let result = mac. verify_slice ( & signature[ ..] ) ;
308
+ if result. is_ok ( ) {
309
+ return Ok ( ( ) ) ;
310
+ }
311
+ if secret_fallback. is_none ( ) {
312
+ return result. map_err ( Error :: new) ;
313
+ }
314
+
315
+ // Try fallback secret (if available)
316
+ let mut mac = Hmac :: < Sha256 > :: new_from_slice ( secret_fallback. expect ( "secret should be set" ) ) ?;
317
+ mac. update ( body) ;
290
318
mac. verify_slice ( & signature[ ..] ) . map_err ( Error :: new)
291
319
} else {
292
320
Err ( format_err ! ( "no valid signature found" ) )
0 commit comments