@@ -32,20 +32,20 @@ mod https_client;
3232mod sync_service;
3333
3434pub use https_client:: { AppIdValidator , HttpsClientConfig } ;
35- pub use sync_service:: { fetch_peers_from_bootnode , WaveKvSyncService } ;
35+ pub use sync_service:: { WaveKvSyncService , fetch_peers_from_bootnode } ;
3636use tracing:: warn;
3737
3838use std:: { collections:: BTreeMap , net:: Ipv4Addr , path:: Path , time:: Duration } ;
3939
4040use aes_gcm:: {
41- aead:: { Aead , Payload } ,
4241 Aes256Gcm , KeyInit , Nonce ,
42+ aead:: { Aead , Payload } ,
4343} ;
4444use anyhow:: { Context , Result } ;
4545use rand:: RngCore ;
4646use serde:: { Deserialize , Serialize } ;
4747use tokio:: sync:: watch;
48- use wavekv:: { node:: NodeState , types:: NodeId , Node } ;
48+ use wavekv:: { Node , node:: NodeState , types:: NodeId } ;
4949
5050/// Per-port flags applied by the gateway when proxying to a CVM port.
5151#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq , Eq , Default ) ]
@@ -698,6 +698,8 @@ impl KvStore {
698698 /// This stores the URL in KvStore (for address lookup) and also adds the node
699699 /// to the wavekv peer list (so SyncManager knows to sync with it).
700700 pub fn register_peer_url ( & self , node_id : NodeId , url : & str ) -> Result < ( ) > {
701+ validate_peer_url ( url) ?;
702+
701703 // Store URL in persistent KvStore
702704 self . persistent
703705 . write ( )
@@ -1102,3 +1104,39 @@ impl KvStore {
11021104 self . persistent . watch_prefix ( keys:: CERT_PREFIX )
11031105 }
11041106}
1107+
1108+ fn validate_peer_url ( url : & str ) -> Result < ( ) > {
1109+ let parsed = reqwest:: Url :: parse ( url) . context ( "invalid peer URL" ) ?;
1110+ anyhow:: ensure!(
1111+ matches!( parsed. scheme( ) , "http" | "https" ) ,
1112+ "peer URL scheme must be http or https"
1113+ ) ;
1114+ anyhow:: ensure!( parsed. host_str( ) . is_some( ) , "peer URL must include a host" ) ;
1115+ anyhow:: ensure!(
1116+ parsed. username( ) . is_empty( ) && parsed. password( ) . is_none( ) ,
1117+ "peer URL must not contain credentials"
1118+ ) ;
1119+ Ok ( ( ) )
1120+ }
1121+
1122+ #[ cfg( test) ]
1123+ mod peer_url_tests {
1124+ use super :: validate_peer_url;
1125+
1126+ #[ test]
1127+ fn accepts_http_sync_urls ( ) {
1128+ assert ! ( validate_peer_url( "https://gateway.example:8011/sync" ) . is_ok( ) ) ;
1129+ assert ! ( validate_peer_url( "http://127.0.0.1:8011" ) . is_ok( ) ) ;
1130+ }
1131+
1132+ #[ test]
1133+ fn rejects_malformed_or_unsafe_sync_urls ( ) {
1134+ for url in [
1135+ "not-a-sync-url" ,
1136+ "ftp://gateway.example/sync" ,
1137+ "https://user:secret@gateway.example/sync" ,
1138+ ] {
1139+ assert ! ( validate_peer_url( url) . is_err( ) , "accepted {url}" ) ;
1140+ }
1141+ }
1142+ }
0 commit comments