@@ -22,7 +22,12 @@ use serde_json::Value as JsonValue;
2222use serde_with:: skip_serializing_none;
2323use url:: Url ;
2424
25- use std:: { collections:: HashMap , fmt, fs:: read_to_string, path:: PathBuf } ;
25+ use std:: {
26+ collections:: HashMap ,
27+ fmt:: { self , Display } ,
28+ fs:: read_to_string,
29+ path:: PathBuf ,
30+ } ;
2631
2732/// Items to help with parsing content into a [`Config`].
2833pub mod parse;
@@ -593,6 +598,121 @@ fn default_file_drop_enabled() -> bool {
593598 true
594599}
595600
601+ /// A Content-Security-Policy directive source list.
602+ /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources>.
603+ #[ derive( Debug , PartialEq , Clone , Deserialize , Serialize ) ]
604+ #[ cfg_attr( feature = "schema" , derive( JsonSchema ) ) ]
605+ #[ serde( rename_all = "camelCase" , untagged) ]
606+ pub enum CspDirectiveSources {
607+ /// An inline list of CSP sources. Same as [`Self::List`], but concatenated with a space separator.
608+ Inline ( String ) ,
609+ /// A list of CSP sources. The collection will be concatenated with a space separator for the CSP string.
610+ List ( Vec < String > ) ,
611+ }
612+
613+ impl Default for CspDirectiveSources {
614+ fn default ( ) -> Self {
615+ Self :: List ( Vec :: new ( ) )
616+ }
617+ }
618+
619+ impl From < CspDirectiveSources > for Vec < String > {
620+ fn from ( sources : CspDirectiveSources ) -> Self {
621+ match sources {
622+ CspDirectiveSources :: Inline ( source) => source. split ( ' ' ) . map ( |s| s. to_string ( ) ) . collect ( ) ,
623+ CspDirectiveSources :: List ( l) => l,
624+ }
625+ }
626+ }
627+
628+ impl CspDirectiveSources {
629+ /// Whether the given source is configured on this directive or not.
630+ pub fn contains ( & self , source : & str ) -> bool {
631+ match self {
632+ Self :: Inline ( s) => s. contains ( & format ! ( "{} " , source) ) || s. contains ( & format ! ( " {}" , source) ) ,
633+ Self :: List ( l) => l. contains ( & source. into ( ) ) ,
634+ }
635+ }
636+
637+ /// Appends the given source to this directive.
638+ pub fn push < S : AsRef < str > > ( & mut self , source : S ) {
639+ match self {
640+ Self :: Inline ( s) => {
641+ s. push ( ' ' ) ;
642+ s. push_str ( source. as_ref ( ) ) ;
643+ }
644+ Self :: List ( l) => {
645+ l. push ( source. as_ref ( ) . to_string ( ) ) ;
646+ }
647+ }
648+ }
649+
650+ /// Extends this CSP directive source list with the given array of sources.
651+ pub fn extend ( & mut self , sources : Vec < String > ) {
652+ for s in sources {
653+ self . push ( s) ;
654+ }
655+ }
656+ }
657+
658+ /// A Content-Security-Policy definition.
659+ /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
660+ #[ derive( Debug , PartialEq , Clone , Deserialize , Serialize ) ]
661+ #[ cfg_attr( feature = "schema" , derive( JsonSchema ) ) ]
662+ #[ serde( rename_all = "camelCase" , untagged) ]
663+ pub enum Csp {
664+ /// The entire CSP policy in a single text string.
665+ Policy ( String ) ,
666+ /// An object mapping a directive with its sources values as a list of strings.
667+ DirectiveMap ( HashMap < String , CspDirectiveSources > ) ,
668+ }
669+
670+ impl From < HashMap < String , CspDirectiveSources > > for Csp {
671+ fn from ( map : HashMap < String , CspDirectiveSources > ) -> Self {
672+ Self :: DirectiveMap ( map)
673+ }
674+ }
675+
676+ impl From < Csp > for HashMap < String , CspDirectiveSources > {
677+ fn from ( csp : Csp ) -> Self {
678+ match csp {
679+ Csp :: Policy ( policy) => {
680+ let mut map = HashMap :: new ( ) ;
681+ for directive in policy. split ( ';' ) {
682+ let mut tokens = directive. trim ( ) . split ( ' ' ) ;
683+ if let Some ( directive) = tokens. next ( ) {
684+ let sources = tokens. map ( |s| s. to_string ( ) ) . collect :: < Vec < String > > ( ) ;
685+ map. insert ( directive. to_string ( ) , CspDirectiveSources :: List ( sources) ) ;
686+ }
687+ }
688+ map
689+ }
690+ Csp :: DirectiveMap ( m) => m,
691+ }
692+ }
693+ }
694+
695+ impl Display for Csp {
696+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
697+ match self {
698+ Self :: Policy ( s) => write ! ( f, "{}" , s) ,
699+ Self :: DirectiveMap ( m) => {
700+ let len = m. len ( ) ;
701+ let mut i = 0 ;
702+ for ( directive, sources) in m {
703+ let sources: Vec < String > = sources. clone ( ) . into ( ) ;
704+ write ! ( f, "{} {}" , directive, sources. join( " " ) ) ?;
705+ i += 1 ;
706+ if i != len {
707+ write ! ( f, "; " ) ?;
708+ }
709+ }
710+ Ok ( ( ) )
711+ }
712+ }
713+ }
714+ }
715+
596716/// Security configuration.
597717#[ skip_serializing_none]
598718#[ derive( Debug , Default , PartialEq , Clone , Deserialize , Serialize ) ]
@@ -604,12 +724,12 @@ pub struct SecurityConfig {
604724 ///
605725 /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
606726 /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
607- pub csp : Option < String > ,
727+ pub csp : Option < Csp > ,
608728 /// The Content Security Policy that will be injected on all HTML files on development.
609729 ///
610730 /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
611731 /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
612- pub dev_csp : Option < String > ,
732+ pub dev_csp : Option < Csp > ,
613733 /// Freeze the `Object.prototype` when using the custom protocol.
614734 #[ serde( default ) ]
615735 pub freeze_prototype : bool ,
@@ -2399,10 +2519,49 @@ mod build {
23992519 }
24002520 }
24012521
2522+ impl ToTokens for CspDirectiveSources {
2523+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
2524+ let prefix = quote ! { :: tauri:: utils:: config:: CspDirectiveSources } ;
2525+
2526+ tokens. append_all ( match self {
2527+ Self :: Inline ( sources) => {
2528+ let sources = sources. as_str ( ) ;
2529+ quote ! ( #prefix:: Inline ( #sources. into( ) ) )
2530+ }
2531+ Self :: List ( list) => {
2532+ let list = vec_lit ( list, str_lit) ;
2533+ quote ! ( #prefix:: List ( #list) )
2534+ }
2535+ } )
2536+ }
2537+ }
2538+
2539+ impl ToTokens for Csp {
2540+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
2541+ let prefix = quote ! { :: tauri:: utils:: config:: Csp } ;
2542+
2543+ tokens. append_all ( match self {
2544+ Self :: Policy ( policy) => {
2545+ let policy = policy. as_str ( ) ;
2546+ quote ! ( #prefix:: Policy ( #policy. into( ) ) )
2547+ }
2548+ Self :: DirectiveMap ( list) => {
2549+ let map = map_lit (
2550+ quote ! { :: std:: collections:: HashMap } ,
2551+ list,
2552+ str_lit,
2553+ identity,
2554+ ) ;
2555+ quote ! ( #prefix:: DirectiveMap ( #map) )
2556+ }
2557+ } )
2558+ }
2559+ }
2560+
24022561 impl ToTokens for SecurityConfig {
24032562 fn to_tokens ( & self , tokens : & mut TokenStream ) {
2404- let csp = opt_str_lit ( self . csp . as_ref ( ) ) ;
2405- let dev_csp = opt_str_lit ( self . dev_csp . as_ref ( ) ) ;
2563+ let csp = opt_lit ( self . csp . as_ref ( ) ) ;
2564+ let dev_csp = opt_lit ( self . dev_csp . as_ref ( ) ) ;
24062565 let freeze_prototype = self . freeze_prototype ;
24072566
24082567 literal_struct ! ( tokens, SecurityConfig , csp, dev_csp, freeze_prototype) ;
0 commit comments