Skip to main content

helmet_core/
lib.rs

1//! Helmet is a collection of HTTP headers that help secure your app by setting various HTTP headers.
2//!
3//! `helmet-core` provides the core functionality of Helmet, vie convenient builders to configure the library.
4//!
5//! The library can be adapted to different frameworks by wrapping the `Helmet` struct in a way that suits the framework. For reference implementations see the [ntex-helmet](https://crates.io/crates/ntex-helmet) crate or the [axum-helmet](https://crates.io/crates/axum-helmet) crate.
6//!
7//! It is based on the [Helmet](https://helmetjs.github.io/) library for Node.js and is highly configurable.
8//!
9//! # Usage
10//!
11//! ```no_run
12//! use helmet_core::{ContentSecurityPolicy, CrossOriginOpenerPolicy, Helmet};
13//!
14//! let helmet = Helmet::new()
15//!     .add(
16//!         ContentSecurityPolicy::new()
17//!             .child_src(vec!["'self'", "https://youtube.com"])
18//!             .connect_src(vec!["'self'", "https://youtube.com"])
19//!             .default_src(vec!["'self'", "https://youtube.com"])
20//!             .font_src(vec!["'self'", "https://youtube.com"]),
21//!     )
22//!     .add(CrossOriginOpenerPolicy::same_origin_allow_popups());
23//! ```
24//!
25//! By default Helmet will set the following headers:
26//!
27//! ```text
28//! Content-Security-Policy: default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests
29//! Cross-Origin-Opener-Policy: same-origin
30//! Cross-Origin-Resource-Policy: same-origin
31//! Origin-Agent-Cluster: ?1
32//! Referrer-Policy: no-referrer
33//! Strict-Transport-Security: max-age=15552000; includeSubDomains
34//! X-Content-Type-Options: nosniff
35//! X-DNS-Prefetch-Control: off
36//! X-Download-Options: noopen
37//! X-Frame-Options: sameorigin
38//! X-Permitted-Cross-Domain-Policies: none
39//! X-XSS-Protection: 0
40//! ```
41//!
42//! This might be a good starting point for most users, but it is highly recommended to spend some time with the documentation for each header, and adjust them to your needs.
43//!
44//! # Configuration
45//!
46//! By default if you construct a new instance of `Helmet` it will not set any headers.
47//!
48//! The `helmet-core` crate helps you configure Helmet by providing convenient builders for each header.
49use core::fmt::Display;
50
51/// Header trait
52///
53/// Allows custom headers to be added to the response
54///
55/// # Examples
56///
57/// ```
58/// use helmet_core::Header;
59///
60/// struct MyHeader;
61///
62/// impl Into<Header> for MyHeader {
63///     fn into(self) -> Header {
64///         ("My-Header", "My-Value".to_owned())
65///    }
66/// }
67/// ```
68pub type Header = (&'static str, String);
69
70/// Manages `Cross-Origin-Embedder-Policy` header
71///
72/// The Cross-Origin-Embedder-Policy HTTP response header prevents a document from loading any cross-origin resources that do not explicitly grant the document permission (via CORS headers) to load them.
73///
74/// # Values
75///
76/// - unsafe-none: The document is not subject to any Cross-Origin-Embedder-Policy restrictions.
77/// - require-corp: The document is subject to Cross-Origin-Embedder-Policy restrictions.
78/// - credentialless: The document is subject to Cross-Origin-Embedder-Policy restrictions, and is not allowed to request credentials (e.g. cookies, certificates, HTTP authentication) from the user.
79///
80/// # Examples
81///
82/// ```
83/// use helmet_core::CrossOriginEmbedderPolicy;
84///
85/// let cross_origin_embedder_policy = CrossOriginEmbedderPolicy::unsafe_none();
86///
87/// let cross_origin_embedder_policy = CrossOriginEmbedderPolicy::require_corp();
88///
89/// let cross_origin_embedder_policy = CrossOriginEmbedderPolicy::credentialless();
90/// ```
91#[derive(Clone)]
92pub enum CrossOriginEmbedderPolicy {
93    UnsafeNone,
94    RequireCorp,
95    Credentialless,
96}
97
98impl CrossOriginEmbedderPolicy {
99    pub fn unsafe_none() -> Self {
100        Self::UnsafeNone
101    }
102
103    pub fn require_corp() -> Self {
104        Self::RequireCorp
105    }
106
107    pub fn credentialless() -> Self {
108        Self::Credentialless
109    }
110}
111
112impl CrossOriginEmbedderPolicy {
113    pub fn as_str(&self) -> &'static str {
114        match self {
115            CrossOriginEmbedderPolicy::UnsafeNone => "unsafe-none",
116            CrossOriginEmbedderPolicy::RequireCorp => "require-corp",
117            CrossOriginEmbedderPolicy::Credentialless => "credentialless",
118        }
119    }
120}
121
122impl From<CrossOriginEmbedderPolicy> for Header {
123    fn from(val: CrossOriginEmbedderPolicy) -> Self {
124        ("Cross-Origin-Embedder-Policy", val.as_str().to_owned())
125    }
126}
127
128/// Manages `Cross-Origin-Opener-Policy` header
129///
130/// The Cross-Origin-Opener-Policy HTTP response header restricts how selected resources are allowed to interact with the document's browsing context in response to user navigation. Each resource can declare an opener policy which applies to the resource's corresponding browsing context.
131///
132/// # Values
133///
134/// - same-origin: The resource's browsing context is the same-origin as the document's browsing context.
135/// - same-origin-allow-popups: The resource's browsing context is the same-origin as the document's browsing context, and the resource is allowed to open new browsing contexts.
136/// - unsafe-none: The resource's browsing context is cross-origin with the document's browsing context.
137///
138/// # Examples
139///
140/// ```
141/// use helmet_core::CrossOriginOpenerPolicy;
142///
143/// let cross_origin_opener_policy = CrossOriginOpenerPolicy::same_origin();
144/// ```
145#[derive(Clone)]
146pub enum CrossOriginOpenerPolicy {
147    SameOrigin,
148    SameOriginAllowPopups,
149    UnsafeNone,
150}
151
152impl CrossOriginOpenerPolicy {
153    pub fn same_origin() -> Self {
154        Self::SameOrigin
155    }
156
157    pub fn same_origin_allow_popups() -> Self {
158        Self::SameOriginAllowPopups
159    }
160
161    pub fn unsafe_none() -> Self {
162        Self::UnsafeNone
163    }
164}
165
166impl CrossOriginOpenerPolicy {
167    fn as_str(&self) -> &'static str {
168        match self {
169            CrossOriginOpenerPolicy::SameOrigin => "same-origin",
170            CrossOriginOpenerPolicy::SameOriginAllowPopups => "same-origin-allow-popups",
171            CrossOriginOpenerPolicy::UnsafeNone => "unsafe-none",
172        }
173    }
174}
175
176impl From<CrossOriginOpenerPolicy> for Header {
177    fn from(val: CrossOriginOpenerPolicy) -> Self {
178        ("Cross-Origin-Opener-Policy", val.as_str().to_owned())
179    }
180}
181
182/// Manages `Cross-Origin-Resource-Policy` header
183///
184/// The Cross-Origin-Resource-Policy HTTP response header conveys a desire that the browser blocks no-cors cross-origin/cross-site requests to the given resource.
185///
186/// # Values
187///
188/// - same-origin: The resource is same-origin to the document.
189/// - same-site: The resource is same-site to the document.
190/// - cross-origin: The resource is cross-origin to the document.
191///
192/// # Examples
193///
194/// ```
195/// use helmet_core::CrossOriginResourcePolicy;
196///
197/// let cross_origin_resource_policy = CrossOriginResourcePolicy::same_origin();
198/// ```
199#[derive(Clone)]
200pub enum CrossOriginResourcePolicy {
201    SameOrigin,
202    SameSite,
203    CrossOrigin,
204}
205
206impl CrossOriginResourcePolicy {
207    pub fn same_origin() -> Self {
208        Self::SameOrigin
209    }
210
211    pub fn same_site() -> Self {
212        Self::SameSite
213    }
214
215    pub fn cross_origin() -> Self {
216        Self::CrossOrigin
217    }
218}
219
220impl CrossOriginResourcePolicy {
221    fn as_str(&self) -> &'static str {
222        match self {
223            CrossOriginResourcePolicy::SameOrigin => "same-origin",
224            CrossOriginResourcePolicy::SameSite => "same-site",
225            CrossOriginResourcePolicy::CrossOrigin => "cross-origin",
226        }
227    }
228}
229
230impl From<CrossOriginResourcePolicy> for Header {
231    fn from(val: CrossOriginResourcePolicy) -> Self {
232        ("Cross-Origin-Resource-Policy", val.as_str().to_owned())
233    }
234}
235
236/// Manages `Origin-Agent-Cluster` header
237///
238/// The Origin-Agent-Cluster HTTP request header indicates that the client prefers an "origin agent cluster" (OAC) for the origin of the resource being requested. An OAC is a cluster of servers that are controlled by the same entity as the origin server, and that are geographically close to the client. The OAC is used to provide the client with a better experience, for example by serving content from a server that is close to the client, or by serving content that is optimized for the client's device.
239///
240/// # Values
241///
242/// - 0: The client does not prefer an OAC.
243/// - 1: The client prefers an OAC.
244///
245/// # Examples
246///
247/// ```
248/// use helmet_core::OriginAgentCluster;
249///
250/// let origin_agent_cluster = OriginAgentCluster::new(true);
251/// ```
252#[derive(Clone)]
253pub struct OriginAgentCluster(bool);
254
255impl OriginAgentCluster {
256    pub fn new(prefer_mobile_experience: bool) -> Self {
257        Self(prefer_mobile_experience)
258    }
259}
260
261impl OriginAgentCluster {
262    fn as_str(&self) -> &'static str {
263        if self.0 {
264            "?1"
265        } else {
266            "?0"
267        }
268    }
269}
270
271impl From<OriginAgentCluster> for Header {
272    fn from(val: OriginAgentCluster) -> Self {
273        ("Origin-Agent-Cluster", val.as_str().to_owned())
274    }
275}
276
277/// Manages `Referrer-Policy` header
278///
279/// The Referrer-Policy HTTP response header controls how much referrer information (sent via the Referer header) should be included with requests.
280///
281/// # Values
282///
283/// - no-referrer: The Referer header will be omitted entirely. No referrer information is sent along with requests.
284/// - no-referrer-when-downgrade: The Referer header will be omitted entirely. However, if the protected resource URL scheme is HTTPS, then the full path will still be sent as a referrer.
285/// - origin: Only send the origin of the document as the referrer in all cases. The document https://example.com/page.html will send the referrer https://example.com/.
286/// - origin-when-cross-origin: Send a full URL when performing a same-origin request, but only send the origin of the document for other cases.
287/// - same-origin: A referrer will be sent for same-site origins, but cross-origin requests will contain no referrer information.
288/// - strict-origin: Only send the origin of the document as the referrer when the protocol security level stays the same (HTTPS→HTTPS), but don't send it to a less secure destination (HTTPS→HTTP).
289/// - strict-origin-when-cross-origin: Send a full URL when performing a same-origin request, only send the origin when the protocol security level stays the same (HTTPS→HTTPS), and send no header to a less secure destination (HTTPS→HTTP).
290/// - unsafe-url: Send a full URL (stripped from parameters) when performing a same-origin or cross-origin request. This policy will leak origins and paths from TLS-protected resources to insecure origins. Carefully consider the impact of this setting.
291///
292/// # Examples
293///
294/// ```
295/// use helmet_core::ReferrerPolicy;
296///
297/// let referrer_policy = ReferrerPolicy::no_referrer();
298/// ```
299#[derive(Clone)]
300pub enum ReferrerPolicy {
301    NoReferrer,
302    NoReferrerWhenDowngrade,
303    Origin,
304    OriginWhenCrossOrigin,
305    SameOrigin,
306    StrictOrigin,
307    StrictOriginWhenCrossOrigin,
308    UnsafeUrl,
309}
310
311impl ReferrerPolicy {
312    pub fn no_referrer() -> Self {
313        Self::NoReferrer
314    }
315
316    pub fn no_referrer_when_downgrade() -> Self {
317        Self::NoReferrerWhenDowngrade
318    }
319
320    pub fn origin() -> Self {
321        Self::Origin
322    }
323
324    pub fn origin_when_cross_origin() -> Self {
325        Self::OriginWhenCrossOrigin
326    }
327
328    pub fn same_origin() -> Self {
329        Self::SameOrigin
330    }
331
332    pub fn strict_origin() -> Self {
333        Self::StrictOrigin
334    }
335
336    pub fn strict_origin_when_cross_origin() -> Self {
337        Self::StrictOriginWhenCrossOrigin
338    }
339
340    pub fn unsafe_url() -> Self {
341        Self::UnsafeUrl
342    }
343}
344
345impl ReferrerPolicy {
346    fn as_str(&self) -> &'static str {
347        match self {
348            ReferrerPolicy::NoReferrer => "no-referrer",
349            ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
350            ReferrerPolicy::Origin => "origin",
351            ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin",
352            ReferrerPolicy::SameOrigin => "same-origin",
353            ReferrerPolicy::StrictOrigin => "strict-origin",
354            ReferrerPolicy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
355            ReferrerPolicy::UnsafeUrl => "unsafe-url",
356        }
357    }
358}
359
360impl From<ReferrerPolicy> for Header {
361    fn from(val: ReferrerPolicy) -> Self {
362        ("Referrer-Policy", val.as_str().to_owned())
363    }
364}
365
366/// Manages `Strict-Transport-Security` header
367///
368/// The Strict-Transport-Security HTTP response header (often abbreviated as HSTS) lets a web site tell browsers that it should only be accessed using HTTPS, instead of using HTTP.
369///
370/// # Values
371///
372/// - max-age: The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS.
373/// - includeSubDomains: If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
374/// - preload: If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
375///
376/// # Examples
377///
378/// ```
379/// use helmet_core::StrictTransportSecurity;
380///
381/// let strict_transport_security = StrictTransportSecurity::default();
382///
383/// let custom_strict_transport_security = StrictTransportSecurity::default()
384///    .max_age(31536000)
385///    .include_sub_domains()
386///    .preload();
387/// ```
388#[derive(Clone)]
389pub struct StrictTransportSecurity {
390    max_age: u32,
391    include_sub_domains: bool,
392    preload: bool,
393}
394
395impl StrictTransportSecurity {
396    pub fn new() -> Self {
397        Self::default()
398    }
399
400    pub fn max_age(mut self, max_age: u32) -> Self {
401        self.max_age = max_age;
402        self
403    }
404
405    pub fn include_sub_domains(mut self) -> Self {
406        self.include_sub_domains = true;
407        self
408    }
409
410    pub fn preload(mut self) -> Self {
411        self.preload = true;
412        self
413    }
414}
415
416impl Default for StrictTransportSecurity {
417    fn default() -> Self {
418        Self {
419            max_age: 31536000,
420            include_sub_domains: false,
421            preload: false,
422        }
423    }
424}
425
426impl Display for StrictTransportSecurity {
427    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428        write!(f, "max-age={}", self.max_age)?;
429        if self.include_sub_domains {
430            write!(f, "; includeSubDomains")?;
431        }
432        if self.preload {
433            write!(f, "; preload")?;
434        }
435        Ok(())
436    }
437}
438
439impl From<StrictTransportSecurity> for Header {
440    fn from(val: StrictTransportSecurity) -> Self {
441        ("Strict-Transport-Security", val.to_string())
442    }
443}
444
445/// Manages `X-Content-Type-Options` header
446///
447/// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed. This allows to opt-out of MIME type sniffing, or, in other words, it is a way to say that the webmasters knew what they were doing.
448///
449/// # Values
450///
451/// - nosniff: Prevents the browser from MIME-sniffing a response away from the declared content-type. This also applies to Google Chrome, when downloading extensions.
452///
453/// # Examples
454///
455/// ```
456/// use helmet_core::XContentTypeOptions;
457///
458/// let x_content_type_options = XContentTypeOptions::nosniff();
459/// ```
460#[derive(Clone)]
461pub enum XContentTypeOptions {
462    NoSniff,
463}
464
465impl XContentTypeOptions {
466    pub fn nosniff() -> Self {
467        Self::NoSniff
468    }
469}
470
471impl XContentTypeOptions {
472    fn as_str(&self) -> &'static str {
473        match self {
474            XContentTypeOptions::NoSniff => "nosniff",
475        }
476    }
477}
478
479impl Display for XContentTypeOptions {
480    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
481        match self {
482            XContentTypeOptions::NoSniff => write!(f, "nosniff"),
483        }
484    }
485}
486
487impl From<XContentTypeOptions> for Header {
488    fn from(val: XContentTypeOptions) -> Self {
489        ("X-Content-Type-Options", val.as_str().to_owned())
490    }
491}
492
493/// Manages `X-DNS-Prefetch-Control` header
494///
495/// The X-DNS-Prefetch-Control HTTP response header controls DNS prefetching, a feature by which browsers proactively perform domain name resolution on both links that the user may choose to follow as well as URLs for items referenced by the document, including images, CSS, JavaScript, and so forth.
496///
497/// # Values
498///
499/// - off: Disable DNS prefetching.
500/// - on: Enable DNS prefetching, allowing the browser to proactively perform domain name resolution on both links that the user may choose to follow as well as URLs for items referenced by the document, including images, CSS, JavaScript, and so forth.
501///
502/// # Examples
503///
504/// ```
505/// use helmet_core::XDNSPrefetchControl;
506///
507/// let x_dns_prefetch_control = XDNSPrefetchControl::off();
508/// ```
509#[derive(Clone)]
510pub enum XDNSPrefetchControl {
511    Off,
512    On,
513}
514
515impl XDNSPrefetchControl {
516    pub fn off() -> Self {
517        Self::Off
518    }
519
520    pub fn on() -> Self {
521        Self::On
522    }
523}
524
525impl XDNSPrefetchControl {
526    fn as_str(&self) -> &'static str {
527        match self {
528            XDNSPrefetchControl::Off => "off",
529            XDNSPrefetchControl::On => "on",
530        }
531    }
532}
533
534impl Display for XDNSPrefetchControl {
535    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
536        match self {
537            XDNSPrefetchControl::Off => write!(f, "off"),
538            XDNSPrefetchControl::On => write!(f, "on"),
539        }
540    }
541}
542
543impl From<XDNSPrefetchControl> for Header {
544    fn from(val: XDNSPrefetchControl) -> Self {
545        ("X-DNS-Prefetch-Control", val.as_str().to_owned())
546    }
547}
548
549/// Manages `X-Download-Options` header
550///
551/// The X-Download-Options HTTP response header indicates that the browser (Internet Explorer) should not display the option to "Open" a file that has been downloaded from an application, to prevent phishing attacks that could trick users into opening potentially malicious content that could infect their computer.
552///
553/// # Values
554///
555/// - noopen: Prevents Internet Explorer from executing downloads in your site’s context.
556///
557/// # Examples
558///
559/// ```
560/// use helmet_core::XDownloadOptions;
561///
562/// let x_download_options = XDownloadOptions::noopen();
563/// ```
564#[derive(Clone)]
565pub enum XDownloadOptions {
566    NoOpen,
567}
568
569impl XDownloadOptions {
570    pub fn noopen() -> Self {
571        Self::NoOpen
572    }
573}
574
575impl XDownloadOptions {
576    fn as_str(&self) -> &'static str {
577        match self {
578            XDownloadOptions::NoOpen => "noopen",
579        }
580    }
581}
582
583impl Display for XDownloadOptions {
584    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585        match self {
586            XDownloadOptions::NoOpen => write!(f, "noopen"),
587        }
588    }
589}
590
591impl From<XDownloadOptions> for Header {
592    fn from(val: XDownloadOptions) -> Self {
593        ("X-Download-Options", val.as_str().to_owned())
594    }
595}
596
597/// Manages `X-Frame-Options` header
598///
599/// The X-Frame-Options HTTP response header can be used to to avoid click-jacking attacks by preventing the content to be included in other websites.
600///
601/// # Values
602///
603/// - deny: The page cannot be displayed in a frame, regardless of the site attempting to do so.
604/// - sameorigin: The page can only be displayed in a frame on the same origin as the page itself.
605/// - allow-from: **Deprecated.** Ignored by all modern browsers. Use `ContentSecurityPolicy::new().frame_ancestors(...)` instead.
606///
607/// # Examples
608///
609/// ```
610/// use helmet_core::XFrameOptions;
611///
612/// let x_frame_options = XFrameOptions::deny();
613///
614/// let x_frame_options = XFrameOptions::same_origin();
615/// ```
616#[derive(Clone)]
617pub enum XFrameOptions {
618    Deny,
619    SameOrigin,
620    #[deprecated(
621        note = "ALLOW-FROM is ignored by modern browsers. Use ContentSecurityPolicy::new().frame_ancestors(...) instead."
622    )]
623    AllowFrom(String),
624}
625
626impl XFrameOptions {
627    pub fn deny() -> Self {
628        Self::Deny
629    }
630
631    pub fn same_origin() -> Self {
632        Self::SameOrigin
633    }
634
635    #[deprecated(
636        note = "ALLOW-FROM is ignored by modern browsers. Use ContentSecurityPolicy::new().frame_ancestors(...) instead."
637    )]
638    pub fn allow_from(uri: &str) -> Self {
639        #[allow(deprecated)]
640        Self::AllowFrom(uri.to_string())
641    }
642}
643
644impl Display for XFrameOptions {
645    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
646        #[allow(deprecated)]
647        match self {
648            XFrameOptions::Deny => write!(f, "DENY"),
649            XFrameOptions::SameOrigin => write!(f, "SAMEORIGIN"),
650            XFrameOptions::AllowFrom(uri) => write!(f, "ALLOW-FROM {}", uri),
651        }
652    }
653}
654
655impl From<XFrameOptions> for Header {
656    fn from(val: XFrameOptions) -> Self {
657        ("X-Frame-Options", val.to_string())
658    }
659}
660
661/// Manages `X-Permitted-Cross-Domain-Policies` header
662///
663/// The X-Permitted-Cross-Domain-Policies HTTP response header determines whether cross-domain policy files (crossdomain.xml and clientaccesspolicy.xml) will be ignored by Flash and Adobe Acrobat in subsequent requests.
664///
665/// # Values
666///
667/// - none: No policy file is allowed.
668/// - master-only: Only a master policy file, but no other policy files, is allowed.
669/// - by-content-type: A policy file is allowed if its MIME type matches the Content-Type of the requested resource.
670/// - by-ftp-filename: A policy file is allowed if its URL matches the URL of the requested resource.
671/// - all: Any policy file is allowed.
672///
673/// # Examples
674///
675/// ```
676/// use helmet_core::XPermittedCrossDomainPolicies;
677///
678/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::none();
679///
680/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::master_only();
681///
682/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::by_content_type();
683///
684/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::by_ftp_filename();
685///
686/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::all();
687/// ```
688#[derive(Clone)]
689pub enum XPermittedCrossDomainPolicies {
690    None,
691    MasterOnly,
692    ByContentType,
693    ByFtpFilename,
694    All,
695}
696
697impl XPermittedCrossDomainPolicies {
698    pub fn none() -> Self {
699        Self::None
700    }
701
702    pub fn master_only() -> Self {
703        Self::MasterOnly
704    }
705
706    pub fn by_content_type() -> Self {
707        Self::ByContentType
708    }
709
710    pub fn by_ftp_filename() -> Self {
711        Self::ByFtpFilename
712    }
713
714    pub fn all() -> Self {
715        Self::All
716    }
717}
718
719impl XPermittedCrossDomainPolicies {
720    fn as_str(&self) -> &'static str {
721        match self {
722            XPermittedCrossDomainPolicies::None => "none",
723            XPermittedCrossDomainPolicies::MasterOnly => "master-only",
724            XPermittedCrossDomainPolicies::ByContentType => "by-content-type",
725            XPermittedCrossDomainPolicies::ByFtpFilename => "by-ftp-filename",
726            XPermittedCrossDomainPolicies::All => "all",
727        }
728    }
729}
730
731impl Display for XPermittedCrossDomainPolicies {
732    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
733        match self {
734            XPermittedCrossDomainPolicies::None => write!(f, "none"),
735            XPermittedCrossDomainPolicies::MasterOnly => write!(f, "master-only"),
736            XPermittedCrossDomainPolicies::ByContentType => write!(f, "by-content-type"),
737            XPermittedCrossDomainPolicies::ByFtpFilename => write!(f, "by-ftp-filename"),
738            XPermittedCrossDomainPolicies::All => write!(f, "all"),
739        }
740    }
741}
742
743impl From<XPermittedCrossDomainPolicies> for Header {
744    fn from(val: XPermittedCrossDomainPolicies) -> Self {
745        ("X-Permitted-Cross-Domain-Policies", val.as_str().to_owned())
746    }
747}
748
749/// Manages `X-XSS-Protection` header
750///
751/// The HTTP X-XSS-Protection response header is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although these protections are largely unnecessary in modern browsers when sites implement a strong Content-Security-Policy that disables the use of inline JavaScript ('unsafe-inline'), they can still provide protections for users of older web browsers that don't yet support CSP.
752///
753/// # Values
754///
755/// - 0: Disables XSS filtering.
756/// - 1: Enables XSS filtering (usually default in browsers).
757/// - 1; mode=block: Enables XSS filtering. Rather than sanitizing the page, the browser will prevent rendering of the page if an attack is detected.
758/// - 1; report=<reporting-URI>: Enables XSS filtering. If a cross-site scripting attack is detected, the browser will sanitize the page and report the violation. This uses the functionality of the CSP report-uri directive to send a report.
759///
760/// # Examples
761///
762/// ```
763/// use helmet_core::XXSSProtection;
764///
765/// let x_xss_protection = XXSSProtection::on();
766///
767/// let x_xss_protection = XXSSProtection::off();
768///
769/// let x_xss_protection = XXSSProtection::on().mode_block();
770///
771/// let x_xss_protection = XXSSProtection::on().report("https://example.com");
772///
773/// let x_xss_protection = XXSSProtection::on().mode_block().report("https://example.com");
774/// ```
775#[derive(Clone)]
776pub struct XXSSProtection {
777    on: bool,
778    mode_block: bool,
779    report: Option<String>,
780}
781
782impl XXSSProtection {
783    /// Disables XSS filtering.
784    pub fn off() -> Self {
785        Self {
786            on: false,
787            mode_block: false,
788            report: None,
789        }
790    }
791
792    /// Enables XSS filtering (usually default in browsers).
793    pub fn on() -> Self {
794        Self {
795            on: true,
796            mode_block: false,
797            report: None,
798        }
799    }
800
801    /// Enables XSS filtering. Rather than sanitizing the page, the browser will prevent rendering of the page if an attack is detected.
802    pub fn mode_block(mut self) -> Self {
803        self.mode_block = true;
804        self
805    }
806
807    /// Enables XSS filtering. If a cross-site scripting attack is detected, the browser will sanitize the page and report the violation. This uses the functionality of the CSP report-uri directive to send a report.
808    pub fn report(mut self, report: &str) -> Self {
809        self.report = Some(report.to_string());
810        self
811    }
812}
813
814impl Display for XXSSProtection {
815    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
816        if self.on {
817            write!(f, "1")?;
818            if self.mode_block {
819                write!(f, "; mode=block")?;
820            }
821            if let Some(report) = &self.report {
822                write!(f, "; report={}", report)?;
823            }
824        } else {
825            write!(f, "0")?;
826        }
827        Ok(())
828    }
829}
830
831impl From<XXSSProtection> for Header {
832    fn from(val: XXSSProtection) -> Self {
833        ("X-XSS-Protection", val.to_string())
834    }
835}
836
837/// Manages `X-Powered-By` header
838///
839/// ntex does not set `X-Powered-By` header by default.
840/// Instead of silencing the header, Helmet allows you to set it to a custom value.
841/// This can be useful against primitive fingerprinting.
842///
843/// # Examples
844///
845/// ```
846/// use helmet_core::XPoweredBy;
847///
848/// let x_powered_by = XPoweredBy::new("PHP 4.2.0");
849/// ```
850#[derive(Clone)]
851pub struct XPoweredBy(String);
852
853impl XPoweredBy {
854    /// Set the `X-Powered-By` header to a custom value.
855    pub fn new(comment: &str) -> Self {
856        Self(comment.to_string())
857    }
858}
859
860impl Display for XPoweredBy {
861    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
862        write!(f, "{}", self.0)?;
863        Ok(())
864    }
865}
866
867impl From<XPoweredBy> for Header {
868    fn from(val: XPoweredBy) -> Self {
869        ("X-Powered-By", val.to_string())
870    }
871}
872
873/// Manages `Content-Security-Policy` header
874///
875/// The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks (XSS).
876///
877/// # Directives
878///
879/// - child-src: Defines valid sources for web workers and nested browsing contexts loaded using elements such as `<frame>` and `<iframe>`.
880/// - connect-src: Applies to XMLHttpRequest (AJAX), WebSocket or EventSource. If not allowed the browser emulates a 400 HTTP status code.
881/// - default-src: The default-src is the default policy for loading content such as JavaScript, Images, CSS, Font's, AJAX requests, Frames, HTML5 Media. See the list of directives to see which values are allowed as default.
882/// - font-src: Defines valid sources for fonts loaded using @font-face.
883/// - frame-src: Defines valid sources for nested browsing contexts loading using elements such as `<frame>` and `<iframe>`.
884/// - img-src: Defines valid sources of images and favicons.
885/// - manifest-src: Specifies which manifest can be applied to the resource.
886/// - media-src: Defines valid sources for loading media using the `<audio>` and `<video>` elements.
887/// - object-src: Defines valid sources for the `<object>`, `<embed>`, and `<applet>` elements.
888/// - prefetch-src: Specifies which referrer to use when fetching the resource.
889/// - script-src: Defines valid sources for JavaScript.
890/// - script-src-elem: Defines valid sources for JavaScript inline event handlers.
891/// - script-src-attr: Defines valid sources for JavaScript inline event handlers.
892/// - style-src: Defines valid sources for stylesheets.
893/// - style-src-elem: Defines valid sources for stylesheets inline event handlers.
894/// - style-src-attr: Defines valid sources for stylesheets inline event handlers.
895/// - worker-src: Defines valid sources for Worker, SharedWorker, or ServiceWorker scripts.
896/// - base-uri: Restricts the URLs which can be used in a document's `<base>` element.
897/// - sandbox: Enables a sandbox for the requested resource similar to the iframe sandbox attribute. The sandbox applies a same origin policy, prevents popups, plugins and script execution is blocked. You can keep the sandbox value empty to keep all restrictions in place, or add values: allow-forms allow-same-origin allow-scripts allow-popups, allow-modals, allow-orientation-lock, allow-pointer-lock, allow-presentation, allow-popups-to-escape-sandbox, allow-top-navigation, allow-top-navigation-by-user-activation.
898/// - form-action: Restricts the URLs which can be used as the target of a form submissions from a given context.
899/// - frame-ancestors: Specifies valid parents that may embed a page using `<frame>`, `<iframe>`, `<object>`, `<embed>`, or `<applet>`.
900/// - report-to: Specifies the endpoint name (defined via `Reporting-Endpoints` header) to send violation reports to.
901/// - report-uri: Specifies URL(s) to send violation reports to (deprecated but still widely supported).
902/// - require-trusted-types-for: Specifies which trusted types are required by a resource.
903/// - trusted-types: Specifies which trusted types are defined by a resource.
904/// - upgrade-insecure-requests: Block HTTP requests on insecure elements.
905///
906/// # Examples
907///
908/// ```
909/// use helmet_core::ContentSecurityPolicy;
910///
911/// let content_security_policy = ContentSecurityPolicy::default()
912///    .child_src(vec!["'self'", "https://youtube.com"])
913///    .connect_src(vec!["'self'", "https://youtube.com"])
914///    .default_src(vec!["'self'", "https://youtube.com"])
915///    .font_src(vec!["'self'", "https://youtube.com"]);
916/// ```
917///
918/// ## Report only
919///
920/// In report only mode, the browser will not block the request, but will send a report to the specified URI.
921///
922/// Make sure to set the `report-to` and/or `report-uri` directives.
923///
924/// ```
925/// use helmet_core::ContentSecurityPolicy;
926///
927/// let content_security_policy = ContentSecurityPolicy::default()
928///    .child_src(vec!["'self'", "https://youtube.com"])
929///    .report_to("csp-endpoint")
930///    .report_uri(vec!["https://example.com/report"])
931///    .report_only();
932/// ```
933#[derive(Clone)]
934pub struct ContentSecurityPolicy<'a> {
935    child_src: Option<Vec<&'a str>>,
936    connect_src: Option<Vec<&'a str>>,
937    default_src: Option<Vec<&'a str>>,
938    font_src: Option<Vec<&'a str>>,
939    frame_src: Option<Vec<&'a str>>,
940    img_src: Option<Vec<&'a str>>,
941    manifest_src: Option<Vec<&'a str>>,
942    media_src: Option<Vec<&'a str>>,
943    object_src: Option<Vec<&'a str>>,
944    prefetch_src: Option<Vec<&'a str>>,
945    script_src: Option<Vec<&'a str>>,
946    script_src_elem: Option<Vec<&'a str>>,
947    script_src_attr: Option<Vec<&'a str>>,
948    style_src: Option<Vec<&'a str>>,
949    style_src_elem: Option<Vec<&'a str>>,
950    style_src_attr: Option<Vec<&'a str>>,
951    worker_src: Option<Vec<&'a str>>,
952    base_uri: Option<Vec<&'a str>>,
953    sandbox: Option<Vec<&'a str>>,
954    form_action: Option<Vec<&'a str>>,
955    frame_ancestors: Option<Vec<&'a str>>,
956    report_to: Option<&'a str>,
957    report_uri: Option<Vec<&'a str>>,
958    require_trusted_types_for: Option<Vec<&'a str>>,
959    trusted_types: Option<Vec<&'a str>>,
960    upgrade_insecure_requests: bool,
961    report_only: bool,
962}
963
964impl<'a> ContentSecurityPolicy<'a> {
965    pub fn new() -> Self {
966        Self {
967            child_src: None,
968            connect_src: None,
969            default_src: None,
970            font_src: None,
971            frame_src: None,
972            img_src: None,
973            manifest_src: None,
974            media_src: None,
975            object_src: None,
976            prefetch_src: None,
977            script_src: None,
978            script_src_elem: None,
979            script_src_attr: None,
980            style_src: None,
981            style_src_elem: None,
982            style_src_attr: None,
983            worker_src: None,
984            base_uri: None,
985            sandbox: None,
986            form_action: None,
987            frame_ancestors: None,
988            report_to: None,
989            report_uri: None,
990            require_trusted_types_for: None,
991            trusted_types: None,
992            upgrade_insecure_requests: false,
993            report_only: false,
994        }
995    }
996
997    /// child-src: Defines valid sources for web workers and nested browsing contexts loaded using elements such as `<frame>` and `<iframe>`.
998    pub fn child_src(mut self, values: Vec<&'a str>) -> Self {
999        self.child_src = Some(values);
1000        self
1001    }
1002
1003    /// connect-src: Applies to XMLHttpRequest (AJAX), WebSocket or EventSource. If not allowed the browser emulates a 400 HTTP status code.
1004    pub fn connect_src(mut self, values: Vec<&'a str>) -> Self {
1005        self.connect_src = Some(values);
1006        self
1007    }
1008
1009    /// default-src: The default-src is the default policy for loading content such as JavaScript, Images, CSS, Font's, AJAX requests, Frames, HTML5 Media. See the list of directives to see which values are allowed as default.
1010    pub fn default_src(mut self, values: Vec<&'a str>) -> Self {
1011        self.default_src = Some(values);
1012        self
1013    }
1014
1015    /// font-src: Defines valid sources for fonts loaded using @font-face.
1016    pub fn font_src(mut self, values: Vec<&'a str>) -> Self {
1017        self.font_src = Some(values);
1018        self
1019    }
1020
1021    /// frame-src: Defines valid sources for nested browsing contexts loading using elements such as `<frame>` and `<iframe>`.
1022    pub fn frame_src(mut self, values: Vec<&'a str>) -> Self {
1023        self.frame_src = Some(values);
1024        self
1025    }
1026
1027    /// img-src: Defines valid sources of images and favicons.
1028    pub fn img_src(mut self, values: Vec<&'a str>) -> Self {
1029        self.img_src = Some(values);
1030        self
1031    }
1032
1033    /// manifest-src: Specifies which manifest can be applied to the resource.
1034    pub fn manifest_src(mut self, values: Vec<&'a str>) -> Self {
1035        self.manifest_src = Some(values);
1036        self
1037    }
1038
1039    /// media-src: Defines valid sources for loading media using the `<audio>` and `<video>` elements.
1040    pub fn media_src(mut self, values: Vec<&'a str>) -> Self {
1041        self.media_src = Some(values);
1042        self
1043    }
1044
1045    /// object-src: Defines valid sources for the `<object>`, `<embed>`, and `<applet>` elements.
1046    pub fn object_src(mut self, values: Vec<&'a str>) -> Self {
1047        self.object_src = Some(values);
1048        self
1049    }
1050
1051    /// prefetch-src: Specifies which referrer to use when fetching the resource.
1052    pub fn prefetch_src(mut self, values: Vec<&'a str>) -> Self {
1053        self.prefetch_src = Some(values);
1054        self
1055    }
1056
1057    /// script-src: Defines valid sources for JavaScript.
1058    pub fn script_src(mut self, values: Vec<&'a str>) -> Self {
1059        self.script_src = Some(values);
1060        self
1061    }
1062
1063    /// script-src-elem: Defines valid sources for JavaScript inline event handlers.
1064    pub fn script_src_elem(mut self, values: Vec<&'a str>) -> Self {
1065        self.script_src_elem = Some(values);
1066        self
1067    }
1068
1069    /// script-src-attr: Defines valid sources for JavaScript inline event handlers.
1070    pub fn script_src_attr(mut self, values: Vec<&'a str>) -> Self {
1071        self.script_src_attr = Some(values);
1072        self
1073    }
1074
1075    /// style-src: Defines valid sources for stylesheets.
1076    pub fn style_src(mut self, values: Vec<&'a str>) -> Self {
1077        self.style_src = Some(values);
1078        self
1079    }
1080
1081    /// style-src-elem: Defines valid sources for stylesheets inline event handlers.
1082    pub fn style_src_elem(mut self, values: Vec<&'a str>) -> Self {
1083        self.style_src_elem = Some(values);
1084        self
1085    }
1086
1087    /// style-src-attr: Defines valid sources for stylesheets inline event handlers.
1088    pub fn style_src_attr(mut self, values: Vec<&'a str>) -> Self {
1089        self.style_src_attr = Some(values);
1090        self
1091    }
1092
1093    /// worker-src: Defines valid sources for Worker, SharedWorker, or ServiceWorker scripts.
1094    pub fn worker_src(mut self, values: Vec<&'a str>) -> Self {
1095        self.worker_src = Some(values);
1096        self
1097    }
1098
1099    /// base-uri: Restricts the URLs which can be used in a document's `<base>` element.
1100    pub fn base_uri(mut self, values: Vec<&'a str>) -> Self {
1101        self.base_uri = Some(values);
1102        self
1103    }
1104
1105    /// sandbox: Enables a sandbox for the requested resource similar to the iframe sandbox attribute. The sandbox applies a same origin policy, prevents popups, plugins and script execution is blocked. You can keep the sandbox value empty to keep all restrictions in place, or add values: allow-forms allow-same-origin allow-scripts allow-popups, allow-modals, allow-orientation-lock, allow-pointer-lock, allow-presentation, allow-popups-to-escape-sandbox, allow-top-navigation, allow-top-navigation-by-user-activation.
1106    pub fn sandbox(mut self, values: Vec<&'a str>) -> Self {
1107        self.sandbox = Some(values);
1108        self
1109    }
1110
1111    /// form-action: Restricts the URLs which can be used as the target of a form submissions from a given context.
1112    pub fn form_action(mut self, values: Vec<&'a str>) -> Self {
1113        self.form_action = Some(values);
1114        self
1115    }
1116
1117    /// frame-ancestors: Specifies valid parents that may embed a page using `<frame>`, `<iframe>`, `<object>`, `<embed>`, or `<applet>`.
1118    pub fn frame_ancestors(mut self, values: Vec<&'a str>) -> Self {
1119        self.frame_ancestors = Some(values);
1120        self
1121    }
1122
1123    /// report-to: Specifies the endpoint name (defined via `Reporting-Endpoints` header) to send violation reports to.
1124    pub fn report_to(mut self, endpoint: &'a str) -> Self {
1125        self.report_to = Some(endpoint);
1126        self
1127    }
1128
1129    /// report-uri: Specifies URL(s) to send violation reports to (deprecated but still widely supported).
1130    pub fn report_uri(mut self, values: Vec<&'a str>) -> Self {
1131        self.report_uri = Some(values);
1132        self
1133    }
1134
1135    /// require-trusted-types-for: Specifies which trusted types are required by a resource.
1136    pub fn require_trusted_types_for(mut self, values: Vec<&'a str>) -> Self {
1137        self.require_trusted_types_for = Some(values);
1138        self
1139    }
1140
1141    /// trusted-types: Specifies which trusted types are defined by a resource.
1142    pub fn trusted_types(mut self, values: Vec<&'a str>) -> Self {
1143        self.trusted_types = Some(values);
1144        self
1145    }
1146
1147    /// Block HTTP requests on insecure elements.
1148    pub fn upgrade_insecure_requests(mut self) -> Self {
1149        self.upgrade_insecure_requests = true;
1150        self
1151    }
1152
1153    /// Enable report only mode
1154    ///
1155    /// When set to true, the `Content-Security-Policy-Report-Only` header is set instead of `Content-Security-Policy`.
1156    ///
1157    /// Defaults to false.
1158    ///
1159    /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
1160    pub fn report_only(mut self) -> Self {
1161        self.report_only = true;
1162        self
1163    }
1164}
1165
1166impl Display for ContentSecurityPolicy<'_> {
1167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1168        let mut directives: Vec<String> = Vec::new();
1169        let directive = |name: &str, values: &[&str]| format!("{} {}", name, values.join(" "));
1170
1171        if let Some(v) = &self.default_src {
1172            directives.push(directive("default-src", v));
1173        }
1174        if let Some(v) = &self.base_uri {
1175            directives.push(directive("base-uri", v));
1176        }
1177        if let Some(v) = &self.child_src {
1178            directives.push(directive("child-src", v));
1179        }
1180        if let Some(v) = &self.connect_src {
1181            directives.push(directive("connect-src", v));
1182        }
1183        if let Some(v) = &self.font_src {
1184            directives.push(directive("font-src", v));
1185        }
1186        if let Some(v) = &self.form_action {
1187            directives.push(directive("form-action", v));
1188        }
1189        if let Some(v) = &self.frame_ancestors {
1190            directives.push(directive("frame-ancestors", v));
1191        }
1192        if let Some(v) = &self.frame_src {
1193            directives.push(directive("frame-src", v));
1194        }
1195        if let Some(v) = &self.img_src {
1196            directives.push(directive("img-src", v));
1197        }
1198        if let Some(v) = &self.manifest_src {
1199            directives.push(directive("manifest-src", v));
1200        }
1201        if let Some(v) = &self.media_src {
1202            directives.push(directive("media-src", v));
1203        }
1204        if let Some(v) = &self.object_src {
1205            directives.push(directive("object-src", v));
1206        }
1207        if let Some(v) = &self.prefetch_src {
1208            directives.push(directive("prefetch-src", v));
1209        }
1210        if let Some(v) = &self.script_src {
1211            directives.push(directive("script-src", v));
1212        }
1213        if let Some(v) = &self.script_src_elem {
1214            directives.push(directive("script-src-elem", v));
1215        }
1216        if let Some(v) = &self.script_src_attr {
1217            directives.push(directive("script-src-attr", v));
1218        }
1219        if let Some(v) = &self.style_src {
1220            directives.push(directive("style-src", v));
1221        }
1222        if let Some(v) = &self.style_src_elem {
1223            directives.push(directive("style-src-elem", v));
1224        }
1225        if let Some(v) = &self.style_src_attr {
1226            directives.push(directive("style-src-attr", v));
1227        }
1228        if let Some(v) = &self.worker_src {
1229            directives.push(directive("worker-src", v));
1230        }
1231        if let Some(v) = &self.sandbox {
1232            directives.push(directive("sandbox", v));
1233        }
1234        if let Some(v) = &self.report_to {
1235            directives.push(format!("report-to {}", v));
1236        }
1237        if let Some(v) = &self.report_uri {
1238            directives.push(directive("report-uri", v));
1239        }
1240        if let Some(v) = &self.require_trusted_types_for {
1241            directives.push(directive("require-trusted-types-for", v));
1242        }
1243        if let Some(v) = &self.trusted_types {
1244            directives.push(directive("trusted-types", v));
1245        }
1246        if self.upgrade_insecure_requests {
1247            directives.push("upgrade-insecure-requests".to_string());
1248        }
1249
1250        write!(f, "{}", directives.join("; "))
1251    }
1252}
1253
1254impl Default for ContentSecurityPolicy<'_> {
1255    /// Default policy for the Content-Security-Policy header.
1256    ///
1257    /// values:
1258    /// ```text
1259    /// default-src 'self';
1260    /// base-uri 'self';
1261    /// font-src 'self' https: data:;
1262    /// form-action 'self';
1263    /// frame-ancestors 'self';
1264    /// img-src 'self' data:;
1265    /// object-src 'none';
1266    /// script-src 'self';
1267    /// script-src-attr 'none';
1268    /// style-src 'self' https: 'unsafe-inline';
1269    /// upgrade-insecure-requests
1270    /// ```
1271    fn default() -> Self {
1272        Self::new()
1273            .default_src(vec!["'self'"])
1274            .base_uri(vec!["'self'"])
1275            .font_src(vec!["'self'", "https:", "data:"])
1276            .form_action(vec!["'self'"])
1277            .frame_ancestors(vec!["'self'"])
1278            .img_src(vec!["'self'", "data:"])
1279            .object_src(vec!["'none'"])
1280            .script_src(vec!["'self'"])
1281            .script_src_attr(vec!["'none'"])
1282            .style_src(vec!["'self'", "https:", "'unsafe-inline'"])
1283            .upgrade_insecure_requests()
1284    }
1285}
1286
1287impl From<ContentSecurityPolicy<'_>> for Header {
1288    fn from(val: ContentSecurityPolicy<'_>) -> Self {
1289        (
1290            if val.report_only {
1291                "Content-Security-Policy-Report-Only"
1292            } else {
1293                "Content-Security-Policy"
1294            },
1295            val.to_string(),
1296        )
1297    }
1298}
1299
1300/// Error returned when a header name or value cannot be converted to a valid HTTP header.
1301#[derive(Debug)]
1302pub enum HelmetError {
1303    /// The header name is not a valid HTTP header name.
1304    InvalidHeaderName(String),
1305    /// The header value is not a valid HTTP header value.
1306    InvalidHeaderValue(String),
1307}
1308
1309impl std::fmt::Display for HelmetError {
1310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1311        match self {
1312            HelmetError::InvalidHeaderName(name) => write!(f, "invalid header name: {}", name),
1313            HelmetError::InvalidHeaderValue(msg) => write!(f, "invalid header value: {}", msg),
1314        }
1315    }
1316}
1317
1318impl std::error::Error for HelmetError {}
1319
1320/// Helmet security headers middleware for ntex services
1321///
1322/// # Examples
1323///
1324/// ```
1325/// use helmet_core::Helmet;
1326///
1327/// let helmet = Helmet::default();
1328/// ```
1329///
1330/// ## Adding custom headers
1331///
1332/// ```
1333/// use helmet_core::{Helmet, StrictTransportSecurity};
1334///
1335/// let helmet = Helmet::new()
1336///    .add(StrictTransportSecurity::new().max_age(31536000).include_sub_domains());
1337/// ```
1338#[derive(Clone)]
1339pub struct Helmet {
1340    pub headers: Vec<Header>,
1341}
1342
1343#[allow(clippy::should_implement_trait)]
1344impl Helmet {
1345    /// Create new `Helmet` instance without any headers applied
1346    pub fn new() -> Self {
1347        Self {
1348            headers: Vec::new(),
1349        }
1350    }
1351
1352    /// Add header to the middleware
1353    pub fn add(mut self, header: impl Into<Header>) -> Self {
1354        self.headers.push(header.into());
1355        self
1356    }
1357}
1358
1359impl Default for Helmet {
1360    /// Default `Helmet` instance with all headers applied
1361    ///
1362    /// ```text
1363    /// Content-Security-Policy: default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests
1364    /// Cross-Origin-Opener-Policy: same-origin
1365    /// Cross-Origin-Resource-Policy: same-origin
1366    /// Origin-Agent-Cluster: ?1
1367    /// Referrer-Policy: no-referrer
1368    /// Strict-Transport-Security: max-age=15552000; includeSubDomains
1369    /// X-Content-Type-Options: nosniff
1370    /// X-DNS-Prefetch-Control: off
1371    /// X-Download-Options: noopen
1372    /// X-Frame-Options: sameorigin
1373    /// X-Permitted-Cross-Domain-Policies: none
1374    /// X-XSS-Protection: 0
1375    /// ```
1376    fn default() -> Self {
1377        Self::new()
1378            .add(ContentSecurityPolicy::default())
1379            .add(CrossOriginOpenerPolicy::same_origin())
1380            .add(CrossOriginResourcePolicy::same_origin())
1381            .add(OriginAgentCluster(true))
1382            .add(ReferrerPolicy::no_referrer())
1383            .add(
1384                StrictTransportSecurity::new()
1385                    .max_age(15552000)
1386                    .include_sub_domains(),
1387            )
1388            .add(XContentTypeOptions::nosniff())
1389            .add(XDNSPrefetchControl::off())
1390            .add(XDownloadOptions::noopen())
1391            .add(XFrameOptions::same_origin())
1392            .add(XPermittedCrossDomainPolicies::none())
1393            .add(XXSSProtection::off())
1394    }
1395}
1396
1397#[cfg(test)]
1398mod tests {
1399    use super::*;
1400
1401    #[test]
1402    fn csp_default_output() {
1403        assert_eq!(
1404            ContentSecurityPolicy::default().to_string(),
1405            "default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests"
1406        );
1407    }
1408
1409    #[test]
1410    fn csp_default_override_replaces_directive() {
1411        assert_eq!(
1412            ContentSecurityPolicy::default()
1413                .script_src(vec!["'self'", "'unsafe-inline'", "'unsafe-eval'"])
1414                .to_string(),
1415            "default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests"
1416        );
1417    }
1418
1419    #[test]
1420    fn csp_default_override_multiple_directives() {
1421        assert_eq!(
1422            ContentSecurityPolicy::default()
1423                .script_src(vec!["'self'", "'unsafe-inline'"])
1424                .img_src(vec!["*"])
1425                .to_string(),
1426            "default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src *; object-src 'none'; script-src 'self' 'unsafe-inline'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests"
1427        );
1428    }
1429
1430    #[test]
1431    fn csp_new_is_empty() {
1432        assert_eq!(ContentSecurityPolicy::new().to_string(), "");
1433    }
1434}