@@ -11,11 +11,13 @@ use figment::{
1111use ipnet:: IpNet ;
1212use serde:: { Deserialize , Deserializer } ;
1313use tracing:: Level ;
14+ use url:: Url ;
1415use validator:: { Validate , ValidationError } ;
1516
1617use crate :: {
1718 core:: { cryptography:: Encryption , security:: JwtSigningConfig } ,
1819 error:: Result ,
20+ v1:: utils:: validation_error,
1921} ;
2022
2123fn deserialize_main_secret < ' de , D > ( deserializer : D ) -> Result < Encryption , D :: Error >
@@ -74,6 +76,36 @@ fn default_redis_pending_duration_secs() -> u64 {
7476 45
7577}
7678
79+ fn validate_operational_webhook_url ( url : & str ) -> Result < ( ) , ValidationError > {
80+ match Url :: parse ( url) {
81+ Ok ( url) => {
82+ // Verify scheme is http or https
83+ if url. scheme ( ) != "http" && url. scheme ( ) != "https" {
84+ return Err ( validation_error (
85+ Some ( "operational_webhook_address" ) ,
86+ Some ( "URL scheme must be http or https" ) ,
87+ ) ) ;
88+ }
89+
90+ // Verify there's a host
91+ if url. host ( ) . is_none ( ) {
92+ return Err ( validation_error (
93+ Some ( "operational_webhook_address" ) ,
94+ Some ( "URL must include a valid host" ) ,
95+ ) ) ;
96+ }
97+ }
98+ Err ( _) => {
99+ return Err ( validation_error (
100+ Some ( "operational_webhook_address" ) ,
101+ Some ( "Invalid URL format" ) ,
102+ ) ) ;
103+ }
104+ }
105+
106+ Ok ( ( ) )
107+ }
108+
77109#[ derive( Clone , Debug , Deserialize , Validate ) ]
78110#[ validate(
79111 schema( function = "validate_config_complete" ) ,
@@ -85,6 +117,7 @@ pub struct ConfigurationInner {
85117
86118 /// The address to send operational webhooks to. When None, operational webhooks will not be
87119 /// sent. When Some, the API server with the given URL will be used to send operational webhooks.
120+ #[ validate( custom = "validate_operational_webhook_url" ) ]
88121 pub operational_webhook_address : Option < String > ,
89122
90123 /// The main secret used by Svix. Used for client-side encryption of sensitive data, etc.
0 commit comments