ntex_helmet/
lib.rs

1//! `ntex-helmet`` is a collection of HTTP headers that help secure your ntex app by setting various HTTP headers.
2//!
3//! `ntex_helmet::Helmet`` is a middleware that automatically sets these headers.
4//!
5//! It is based on the [Helmet](https://helmetjs.github.io/) library for Node.js and is highly configurable.
6//!
7//! # Usage
8//!
9//! ```no_run
10//! use ntex::web;
11//! use ntex_helmet::Helmet;
12//!
13//! #[ntex::main]
14//! async fn main() -> std::io::Result<()> {
15//!     web::HttpServer::new(move || {
16//!         web::App::new()
17//!            .wrap(Helmet::default())
18//!            .service(web::resource("/").to(|| async { "Hello, world!" }))
19//!     })
20//!     .bind(("127.0.0.1", 8080))?
21//!     .run()
22//!     .await
23//! }
24//! ```
25//!
26//! By default Helmet will set the following headers:
27//!
28//! ```text
29//! 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
30//! Cross-Origin-Opener-Policy: same-origin
31//! Cross-Origin-Resource-Policy: same-origin
32//! Origin-Agent-Cluster: ?1
33//! Referrer-Policy: no-referrer
34//! Strict-Transport-Security: max-age=15552000; includeSubDomains
35//! X-Content-Type-Options: nosniff
36//! X-DNS-Prefetch-Control: off
37//! X-Download-Options: noopen
38//! X-Frame-Options: sameorigin
39//! X-Permitted-Cross-Domain-Policies: none
40//! X-XSS-Protection: 0
41//! ```
42//!
43//! 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.
44//!
45//! # Configuration
46//!
47//! By default if you construct a new instance of `Helmet` it will not set any headers.
48//!
49//! It is possible to configure `Helmet` to set only the headers you want, by using the `add` method to add headers.
50//!
51//! ```no_run
52//! use ntex::web;
53//! use ntex_helmet::{ContentSecurityPolicy, CrossOriginOpenerPolicy, Helmet};
54//!
55//! #[ntex::main]
56//! async fn main() -> std::io::Result<()> {
57//!     web::HttpServer::new(move || {
58//!         web::App::new()
59//!             .wrap(
60//!                 Helmet::new()
61//!                     .add(
62//!                         ContentSecurityPolicy::new()
63//!                             .child_src(vec!["'self'", "https://youtube.com"])
64//!                             .connect_src(vec!["'self'", "https://youtube.com"])
65//!                             .default_src(vec!["'self'", "https://youtube.com"])
66//!                             .font_src(vec!["'self'", "https://youtube.com"]),
67//!                     )
68//!                     .add(CrossOriginOpenerPolicy::same_origin_allow_popups()),
69//!             )
70//!             .service(web::resource("/").to(|| async { "Hello, world!" }))
71//!     })
72//!     .bind(("127.0.0.1", 4200))?
73//!     .run()
74//!     .await
75//! }
76//! ```
77use ntex::{
78    http::{
79        header::{HeaderName, HeaderValue},
80        HeaderMap,
81    },
82    web::{WebRequest, WebResponse},
83    Middleware, Service, ServiceCtx,
84};
85
86use helmet_core::Helmet as HelmetCore;
87
88// re-export helmet_core::*, except for the `Helmet` struct
89pub use helmet_core::*;
90
91pub struct HelmetMiddleware<S> {
92    service: S,
93    headers: HeaderMap,
94}
95
96impl<S, E> Service<WebRequest<E>> for HelmetMiddleware<S>
97where
98    S: Service<WebRequest<E>, Response = WebResponse>,
99    E: 'static,
100{
101    type Response = WebResponse;
102    type Error = S::Error;
103
104    async fn call(
105        &self,
106        req: WebRequest<E>,
107        ctx: ServiceCtx<'_, Self>,
108    ) -> Result<Self::Response, Self::Error> {
109        let mut res = ctx.call(&self.service, req).await?;
110
111        // set response headers
112        for (name, value) in self.headers.iter() {
113            res.headers_mut().append(name.clone(), value.clone());
114        }
115
116        Ok(res)
117    }
118}
119
120/// Helmet middleware
121/// ```rust
122/// use ntex::web;
123/// use ntex_helmet::Helmet;
124#[derive(Default)]
125pub struct Helmet(HelmetCore);
126
127#[allow(clippy::should_implement_trait)]
128impl Helmet {
129    pub fn new() -> Self {
130        Self(HelmetCore::new())
131    }
132
133    pub fn add(self, middleware: impl Into<helmet_core::Header>) -> Self {
134        Self(self.0.add(middleware))
135    }
136}
137
138impl<S> Middleware<S> for Helmet {
139    type Service = HelmetMiddleware<S>;
140
141    fn create(&self, service: S) -> Self::Service {
142        let mut headers = HeaderMap::new();
143        for header in self.0.headers.iter() {
144            let name = HeaderName::try_from(header.0).expect("invalid header name");
145            let value = HeaderValue::from_str(&header.1).expect("invalid header value");
146            headers.append(name, value);
147        }
148
149        HelmetMiddleware { service, headers }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use ntex::{
156        web::test::{ok_service, TestRequest},
157        Pipeline,
158    };
159
160    use helmet_core::{
161        ContentSecurityPolicy, CrossOriginEmbedderPolicy, CrossOriginOpenerPolicy,
162        CrossOriginResourcePolicy, OriginAgentCluster, ReferrerPolicy, StrictTransportSecurity,
163        XContentTypeOptions, XDNSPrefetchControl, XDownloadOptions, XFrameOptions,
164        XPermittedCrossDomainPolicies, XPoweredBy, XXSSProtection,
165    };
166
167    use super::*;
168
169    #[ntex::test]
170    async fn test_cross_origin_embedder_policy_unsafe_none() {
171        let mw = Pipeline::new(
172            Helmet::new()
173                .add(CrossOriginEmbedderPolicy::unsafe_none())
174                .create(ok_service()),
175        );
176
177        let req = TestRequest::default().to_srv_request();
178        let resp = mw.call(req).await.unwrap();
179        assert_eq!(
180            resp.headers().get("Cross-Origin-Embedder-Policy").unwrap(),
181            "unsafe-none"
182        );
183    }
184
185    #[ntex::test]
186    async fn test_cross_origin_embedder_policy_require_corp() {
187        let mw = Pipeline::new(
188            Helmet::new()
189                .add(CrossOriginEmbedderPolicy::require_corp())
190                .create(ok_service()),
191        );
192
193        let req = TestRequest::default().to_srv_request();
194        let resp = mw.call(req).await.unwrap();
195        assert_eq!(
196            resp.headers().get("Cross-Origin-Embedder-Policy").unwrap(),
197            "require-corp"
198        );
199    }
200
201    #[ntex::test]
202    async fn test_cross_origin_embedder_policy_credentialless() {
203        let mw = Pipeline::new(
204            Helmet::new()
205                .add(CrossOriginEmbedderPolicy::credentialless())
206                .create(ok_service()),
207        );
208
209        let req = TestRequest::default().to_srv_request();
210        let resp = mw.call(req).await.unwrap();
211        assert_eq!(
212            resp.headers().get("Cross-Origin-Embedder-Policy").unwrap(),
213            "credentialless"
214        );
215    }
216
217    #[ntex::test]
218    async fn test_cross_origin_opener_policy_same_origin() {
219        let mw = Pipeline::new(
220            Helmet::new()
221                .add(CrossOriginOpenerPolicy::same_origin())
222                .create(ok_service()),
223        );
224
225        let req = TestRequest::default().to_srv_request();
226        let resp = mw.call(req).await.unwrap();
227        assert_eq!(
228            resp.headers().get("Cross-Origin-Opener-Policy").unwrap(),
229            "same-origin"
230        );
231    }
232
233    #[ntex::test]
234    async fn test_cross_origin_opener_policy_same_origin_allow_popups() {
235        let mw = Pipeline::new(
236            Helmet::new()
237                .add(CrossOriginOpenerPolicy::same_origin_allow_popups())
238                .create(ok_service()),
239        );
240
241        let req = TestRequest::default().to_srv_request();
242        let resp = mw.call(req).await.unwrap();
243        assert_eq!(
244            resp.headers().get("Cross-Origin-Opener-Policy").unwrap(),
245            "same-origin-allow-popups"
246        );
247    }
248
249    #[ntex::test]
250    async fn test_cross_origin_opener_policy_unsafe_none() {
251        let mw = Pipeline::new(
252            Helmet::new()
253                .add(CrossOriginOpenerPolicy::unsafe_none())
254                .create(ok_service()),
255        );
256
257        let req = TestRequest::default().to_srv_request();
258        let resp = mw.call(req).await.unwrap();
259        assert_eq!(
260            resp.headers().get("Cross-Origin-Opener-Policy").unwrap(),
261            "unsafe-none"
262        );
263    }
264
265    #[ntex::test]
266    async fn test_cross_origin_resource_policy_same_origin() {
267        let mw = Pipeline::new(
268            Helmet::new()
269                .add(CrossOriginResourcePolicy::same_origin())
270                .create(ok_service()),
271        );
272
273        let req = TestRequest::default().to_srv_request();
274        let resp = mw.call(req).await.unwrap();
275        assert_eq!(
276            resp.headers().get("Cross-Origin-Resource-Policy").unwrap(),
277            "same-origin"
278        );
279    }
280
281    #[ntex::test]
282    async fn test_cross_origin_resource_policy_cross_origin() {
283        let mw = Pipeline::new(
284            Helmet::new()
285                .add(CrossOriginResourcePolicy::cross_origin())
286                .create(ok_service()),
287        );
288
289        let req = TestRequest::default().to_srv_request();
290        let resp = mw.call(req).await.unwrap();
291        assert_eq!(
292            resp.headers().get("Cross-Origin-Resource-Policy").unwrap(),
293            "cross-origin"
294        );
295    }
296
297    #[ntex::test]
298    async fn test_cross_origin_resource_policy_same_site() {
299        let mw = Pipeline::new(
300            Helmet::new()
301                .add(CrossOriginResourcePolicy::same_site())
302                .create(ok_service()),
303        );
304
305        let req = TestRequest::default().to_srv_request();
306        let resp = mw.call(req).await.unwrap();
307        assert_eq!(
308            resp.headers().get("Cross-Origin-Resource-Policy").unwrap(),
309            "same-site"
310        );
311    }
312
313    #[ntex::test]
314    async fn test_origin_agent_cluster_prefer_mobile() {
315        let mw = Pipeline::new(
316            Helmet::new()
317                .add(OriginAgentCluster::new(true))
318                .create(ok_service()),
319        );
320
321        let req = TestRequest::default().to_srv_request();
322        let resp = mw.call(req).await.unwrap();
323        assert_eq!(
324            resp.headers()
325                .get("Origin-Agent-Cluster")
326                .unwrap()
327                .to_str()
328                .unwrap(),
329            "?1"
330        );
331    }
332
333    #[ntex::test]
334    async fn test_origin_agent_cluster_not_prefer_mobile() {
335        let mw = Pipeline::new(
336            Helmet::new()
337                .add(OriginAgentCluster::new(false))
338                .create(ok_service()),
339        );
340
341        let req = TestRequest::default().to_srv_request();
342        let resp = mw.call(req).await.unwrap();
343        assert_eq!(
344            resp.headers()
345                .get("Origin-Agent-Cluster")
346                .unwrap()
347                .to_str()
348                .unwrap(),
349            "?0"
350        );
351    }
352
353    #[ntex::test]
354    async fn test_referrer_policy_no_referrer() {
355        let mw = Pipeline::new(
356            Helmet::new()
357                .add(ReferrerPolicy::no_referrer())
358                .create(ok_service()),
359        );
360
361        let req = TestRequest::default().to_srv_request();
362        let resp = mw.call(req).await.unwrap();
363        assert_eq!(
364            resp.headers().get("Referrer-Policy").unwrap(),
365            "no-referrer"
366        );
367    }
368
369    #[ntex::test]
370    async fn test_referrer_policy_no_referrer_when_downgrade() {
371        let mw = Pipeline::new(
372            Helmet::new()
373                .add(ReferrerPolicy::no_referrer_when_downgrade())
374                .create(ok_service()),
375        );
376
377        let req = TestRequest::default().to_srv_request();
378        let resp = mw.call(req).await.unwrap();
379        assert_eq!(
380            resp.headers().get("Referrer-Policy").unwrap(),
381            "no-referrer-when-downgrade"
382        );
383    }
384
385    #[ntex::test]
386    async fn test_referrer_policy_origin() {
387        let mw = Pipeline::new(
388            Helmet::new()
389                .add(ReferrerPolicy::origin())
390                .create(ok_service()),
391        );
392
393        let req = TestRequest::default().to_srv_request();
394        let resp = mw.call(req).await.unwrap();
395        assert_eq!(resp.headers().get("Referrer-Policy").unwrap(), "origin");
396    }
397
398    #[ntex::test]
399    async fn test_referrer_policy_origin_when_cross_origin() {
400        let mw = Pipeline::new(
401            Helmet::new()
402                .add(ReferrerPolicy::origin_when_cross_origin())
403                .create(ok_service()),
404        );
405
406        let req = TestRequest::default()
407            .header("Origin", "https://example.com")
408            .to_srv_request();
409        let resp = mw.call(req).await.unwrap();
410        assert_eq!(
411            resp.headers().get("Referrer-Policy").unwrap(),
412            "origin-when-cross-origin"
413        );
414    }
415
416    #[ntex::test]
417    async fn test_referrer_policy_same_origin() {
418        let mw = Pipeline::new(
419            Helmet::new()
420                .add(ReferrerPolicy::same_origin())
421                .create(ok_service()),
422        );
423
424        let req = TestRequest::default()
425            .header("Origin", "https://example.com")
426            .to_srv_request();
427        let resp = mw.call(req).await.unwrap();
428        assert_eq!(
429            resp.headers().get("Referrer-Policy").unwrap(),
430            "same-origin"
431        );
432    }
433
434    #[ntex::test]
435    async fn test_referrer_policy_strict_origin() {
436        let mw = Pipeline::new(
437            Helmet::new()
438                .add(ReferrerPolicy::strict_origin())
439                .create(ok_service()),
440        );
441
442        let req = TestRequest::default()
443            .header("Origin", "https://example.com")
444            .to_srv_request();
445        let resp = mw.call(req).await.unwrap();
446        assert_eq!(
447            resp.headers().get("Referrer-Policy").unwrap(),
448            "strict-origin"
449        );
450    }
451
452    #[ntex::test]
453    async fn test_referrer_policy_strict_origin_when_cross_origin() {
454        let mw = Pipeline::new(
455            Helmet::new()
456                .add(ReferrerPolicy::strict_origin_when_cross_origin())
457                .create(ok_service()),
458        );
459
460        let req = TestRequest::default()
461            .header("Origin", "https://example.com")
462            .to_srv_request();
463        let resp = mw.call(req).await.unwrap();
464        assert_eq!(
465            resp.headers().get("Referrer-Policy").unwrap(),
466            "strict-origin-when-cross-origin"
467        );
468    }
469
470    #[ntex::test]
471    async fn test_referrer_policy_unsafe_url() {
472        let mw = Pipeline::new(
473            Helmet::new()
474                .add(ReferrerPolicy::unsafe_url())
475                .create(ok_service()),
476        );
477
478        let req = TestRequest::default()
479            .header("Origin", "https://example.com")
480            .to_srv_request();
481        let resp = mw.call(req).await.unwrap();
482        assert_eq!(resp.headers().get("Referrer-Policy").unwrap(), "unsafe-url");
483    }
484
485    #[ntex::test]
486    async fn test_strict_transport_security_max_age() {
487        let mw = Pipeline::new(
488            Helmet::new()
489                .add(StrictTransportSecurity::new().max_age(31536000))
490                .create(ok_service()),
491        );
492
493        let req = TestRequest::default().to_srv_request();
494        let resp = mw.call(req).await.unwrap();
495
496        assert_eq!(
497            resp.headers()
498                .get("Strict-Transport-Security")
499                .unwrap()
500                .to_str()
501                .unwrap(),
502            "max-age=31536000"
503        );
504    }
505
506    #[ntex::test]
507    async fn test_strict_transport_security_max_age_include_sub_domains() {
508        let mw = Pipeline::new(
509            Helmet::new()
510                .add(
511                    StrictTransportSecurity::new()
512                        .max_age(31536000)
513                        .include_sub_domains(),
514                )
515                .create(ok_service()),
516        );
517
518        let req = TestRequest::default().to_srv_request();
519        let resp = mw.call(req).await.unwrap();
520
521        assert_eq!(
522            resp.headers()
523                .get("Strict-Transport-Security")
524                .unwrap()
525                .to_str()
526                .unwrap(),
527            "max-age=31536000; includeSubDomains"
528        );
529    }
530
531    #[ntex::test]
532    async fn test_strict_transport_security_max_age_preload() {
533        let mw = Pipeline::new(
534            Helmet::new()
535                .add(StrictTransportSecurity::new().max_age(31536000).preload())
536                .create(ok_service()),
537        );
538
539        let req = TestRequest::default().to_srv_request();
540        let resp = mw.call(req).await.unwrap();
541
542        assert_eq!(
543            resp.headers()
544                .get("Strict-Transport-Security")
545                .unwrap()
546                .to_str()
547                .unwrap(),
548            "max-age=31536000; preload"
549        );
550    }
551
552    #[ntex::test]
553    async fn test_strict_transport_security_max_age_include_sub_domains_preload() {
554        let mw = Pipeline::new(
555            Helmet::new()
556                .add(
557                    StrictTransportSecurity::new()
558                        .max_age(31536000)
559                        .include_sub_domains()
560                        .preload(),
561                )
562                .create(ok_service()),
563        );
564
565        let req = TestRequest::default().to_srv_request();
566        let resp = mw.call(req).await.unwrap();
567
568        assert_eq!(
569            resp.headers()
570                .get("Strict-Transport-Security")
571                .unwrap()
572                .to_str()
573                .unwrap(),
574            "max-age=31536000; includeSubDomains; preload"
575        );
576    }
577
578    #[ntex::test]
579    async fn test_x_content_type_options_nosniff() {
580        let mw = Pipeline::new(
581            Helmet::new()
582                .add(XContentTypeOptions::nosniff())
583                .create(ok_service()),
584        );
585
586        let req = TestRequest::default().to_srv_request();
587        let resp = mw.call(req).await.unwrap();
588
589        assert_eq!(
590            resp.headers()
591                .get("X-Content-Type-Options")
592                .unwrap()
593                .to_str()
594                .unwrap(),
595            "nosniff"
596        );
597    }
598
599    #[ntex::test]
600    async fn test_x_dns_prefetch_control_off() {
601        let mw = Pipeline::new(
602            Helmet::new()
603                .add(XDNSPrefetchControl::off())
604                .create(ok_service()),
605        );
606
607        let req = TestRequest::default().to_srv_request();
608        let resp = mw.call(req).await.unwrap();
609
610        assert_eq!(
611            resp.headers()
612                .get("X-DNS-Prefetch-Control")
613                .unwrap()
614                .to_str()
615                .unwrap(),
616            "off"
617        );
618    }
619
620    #[ntex::test]
621    async fn test_x_dns_prefetch_control_on() {
622        let mw = Pipeline::new(
623            Helmet::new()
624                .add(XDNSPrefetchControl::on())
625                .create(ok_service()),
626        );
627
628        let req = TestRequest::default().to_srv_request();
629        let resp = mw.call(req).await.unwrap();
630
631        assert_eq!(
632            resp.headers()
633                .get("X-DNS-Prefetch-Control")
634                .unwrap()
635                .to_str()
636                .unwrap(),
637            "on"
638        );
639    }
640
641    #[ntex::test]
642    async fn test_x_download_options_noopen() {
643        let mw = Pipeline::new(
644            Helmet::new()
645                .add(XDownloadOptions::noopen())
646                .create(ok_service()),
647        );
648
649        let req = TestRequest::default().to_srv_request();
650        let resp = mw.call(req).await.unwrap();
651
652        assert_eq!(
653            resp.headers()
654                .get("X-Download-Options")
655                .unwrap()
656                .to_str()
657                .unwrap(),
658            "noopen"
659        );
660    }
661
662    #[ntex::test]
663    async fn test_x_frame_options_deny() {
664        let mw = Pipeline::new(
665            Helmet::new()
666                .add(XFrameOptions::deny())
667                .create(ok_service()),
668        );
669
670        let req = TestRequest::default()
671            .header("Origin", "https://example.com")
672            .to_srv_request();
673        let resp = mw.call(req).await.unwrap();
674
675        assert_eq!(
676            resp.headers()
677                .get("X-Frame-Options")
678                .unwrap()
679                .to_str()
680                .unwrap(),
681            "DENY"
682        );
683    }
684
685    #[ntex::test]
686    async fn test_x_frame_options_same_origin() {
687        let mw = Pipeline::new(
688            Helmet::new()
689                .add(XFrameOptions::same_origin())
690                .create(ok_service()),
691        );
692
693        let req = TestRequest::default()
694            .header("Origin", "https://example.com")
695            .to_srv_request();
696        let resp = mw.call(req).await.unwrap();
697
698        assert_eq!(
699            resp.headers()
700                .get("X-Frame-Options")
701                .unwrap()
702                .to_str()
703                .unwrap(),
704            "SAMEORIGIN"
705        );
706    }
707
708    #[ntex::test]
709    async fn test_x_frame_options_allow_from() {
710        let mw = Pipeline::new(
711            Helmet::new()
712                .add(XFrameOptions::allow_from("https://example.com"))
713                .create(ok_service()),
714        );
715
716        let req = TestRequest::default()
717            .header("Origin", "https://example.com")
718            .to_srv_request();
719        let resp = mw.call(req).await.unwrap();
720
721        assert_eq!(
722            resp.headers()
723                .get("X-Frame-Options")
724                .unwrap()
725                .to_str()
726                .unwrap(),
727            "ALLOW-FROM https://example.com"
728        );
729    }
730
731    #[ntex::test]
732    async fn test_x_permitted_cross_domain_policies_none() {
733        let mw = Pipeline::new(
734            Helmet::new()
735                .add(XPermittedCrossDomainPolicies::none())
736                .create(ok_service()),
737        );
738
739        let req = TestRequest::default().to_srv_request();
740        let resp = mw.call(req).await.unwrap();
741        assert_eq!(
742            resp.headers()
743                .get("X-Permitted-Cross-Domain-Policies")
744                .unwrap()
745                .to_str()
746                .unwrap(),
747            "none"
748        );
749    }
750
751    #[ntex::test]
752    async fn test_x_permitted_cross_domain_policies_master_only() {
753        let mw = Pipeline::new(
754            Helmet::new()
755                .add(XPermittedCrossDomainPolicies::master_only())
756                .create(ok_service()),
757        );
758
759        let req = TestRequest::default().to_srv_request();
760        let resp = mw.call(req).await.unwrap();
761        assert_eq!(
762            resp.headers()
763                .get("X-Permitted-Cross-Domain-Policies")
764                .unwrap()
765                .to_str()
766                .unwrap(),
767            "master-only"
768        );
769    }
770
771    #[ntex::test]
772    async fn test_x_permitted_cross_domain_policies_by_content_type() {
773        let mw = Pipeline::new(
774            Helmet::new()
775                .add(XPermittedCrossDomainPolicies::by_content_type())
776                .create(ok_service()),
777        );
778
779        let req = TestRequest::default().to_srv_request();
780        let resp = mw.call(req).await.unwrap();
781        assert_eq!(
782            resp.headers()
783                .get("X-Permitted-Cross-Domain-Policies")
784                .unwrap()
785                .to_str()
786                .unwrap(),
787            "by-content-type"
788        );
789    }
790
791    #[ntex::test]
792    async fn test_x_permitted_cross_domain_policies_by_ftp_filename() {
793        let mw = Pipeline::new(
794            Helmet::new()
795                .add(XPermittedCrossDomainPolicies::by_ftp_filename())
796                .create(ok_service()),
797        );
798
799        let req = TestRequest::default()
800            .header("Origin", "https://example.com")
801            .to_srv_request();
802        let resp = mw.call(req).await.unwrap();
803        assert_eq!(
804            resp.headers()
805                .get("X-Permitted-Cross-Domain-Policies")
806                .unwrap()
807                .to_str()
808                .unwrap(),
809            "by-ftp-filename"
810        );
811    }
812
813    #[ntex::test]
814    async fn test_x_permitted_cross_domain_policies_all() {
815        let mw = Pipeline::new(
816            Helmet::new()
817                .add(XPermittedCrossDomainPolicies::all())
818                .create(ok_service()),
819        );
820
821        let req = TestRequest::default().to_srv_request();
822        let resp = mw.call(req).await.unwrap();
823        assert_eq!(
824            resp.headers()
825                .get("X-Permitted-Cross-Domain-Policies")
826                .unwrap()
827                .to_str()
828                .unwrap(),
829            "all"
830        );
831    }
832
833    #[ntex::test]
834    async fn test_x_xss_protection_zero() {
835        let mw = Pipeline::new(
836            Helmet::new()
837                .add(XXSSProtection::off())
838                .create(ok_service()),
839        );
840
841        let req = TestRequest::default().to_srv_request();
842        let resp = mw.call(req).await.unwrap();
843
844        assert_eq!(resp.headers().get("X-XSS-Protection").unwrap(), "0");
845    }
846
847    #[ntex::test]
848    async fn test_x_xss_protection_one() {
849        let mw = Pipeline::new(Helmet::new().add(XXSSProtection::on()).create(ok_service()));
850
851        let req = TestRequest::default()
852            .header("Origin", "https://example.com")
853            .to_srv_request();
854        let resp = mw.call(req).await.unwrap();
855
856        assert_eq!(resp.headers().get("X-XSS-Protection").unwrap(), "1");
857    }
858
859    #[ntex::test]
860    async fn test_x_xss_protection_one_mode_block() {
861        let mw = Pipeline::new(
862            Helmet::new()
863                .add(XXSSProtection::on().mode_block())
864                .create(ok_service()),
865        );
866
867        let req = TestRequest::default().to_srv_request();
868        let resp = mw.call(req).await.unwrap();
869
870        assert_eq!(
871            resp.headers().get("X-XSS-Protection").unwrap(),
872            "1; mode=block"
873        );
874    }
875
876    #[ntex::test]
877    async fn test_x_xss_protection_one_mode_block_report() {
878        let mw = Pipeline::new(
879            Helmet::new()
880                .add(
881                    XXSSProtection::on()
882                        .mode_block()
883                        .report("https://example.com/report-xss-attack"),
884                )
885                .create(ok_service()),
886        );
887
888        let req = TestRequest::default()
889            .header("Origin", "https://example.com")
890            .to_srv_request();
891        let resp = mw.call(req).await.unwrap();
892
893        assert_eq!(
894            resp.headers()
895                .get("X-XSS-Protection")
896                .unwrap()
897                .to_str()
898                .unwrap(),
899            "1; mode=block; report=https://example.com/report-xss-attack"
900        );
901    }
902
903    #[ntex::test]
904    async fn test_content_security_policy_default() {
905        let mw = Pipeline::new(
906            Helmet::new()
907                .add(ContentSecurityPolicy::default())
908                .create(ok_service()),
909        );
910
911        let req = TestRequest::default()
912            .header("Origin", "https://example.com")
913            .to_srv_request();
914        let resp = mw.call(req).await.unwrap();
915
916        assert_eq!(
917            resp.headers()
918                .get("Content-Security-Policy")
919                .unwrap()
920                .to_str()
921                .unwrap(),
922            "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"
923        );
924    }
925
926    #[ntex::test]
927    async fn test_x_powered_by() {
928        let mw = Pipeline::new(
929            Helmet::new()
930                .add(XPoweredBy::new("PHP 4.2.0"))
931                .create(ok_service()),
932        );
933
934        let req = TestRequest::default().to_srv_request();
935        let resp = mw.call(req).await.unwrap();
936
937        assert_eq!(resp.headers().get("X-Powered-By").unwrap(), "PHP 4.2.0");
938    }
939
940    #[ntex::test]
941    async fn test_content_security_policy_child_src() {
942        let mw = Pipeline::new(
943            Helmet::new()
944                .add(ContentSecurityPolicy::new().child_src(vec!["'self'", "https://youtube.com"]))
945                .create(ok_service()),
946        );
947
948        let req = TestRequest::default().to_srv_request();
949        let resp = mw.call(req).await.unwrap();
950
951        assert_eq!(
952            resp.headers()
953                .get("Content-Security-Policy")
954                .unwrap()
955                .to_str()
956                .unwrap(),
957            "child-src 'self' https://youtube.com"
958        );
959    }
960
961    #[ntex::test]
962    async fn test_content_security_policy_connect_src() {
963        let mw = Pipeline::new(
964            Helmet::new()
965                .add(
966                    ContentSecurityPolicy::new().connect_src(vec!["'self'", "https://youtube.com"]),
967                )
968                .create(ok_service()),
969        );
970
971        let req = TestRequest::default().to_srv_request();
972        let resp = mw.call(req).await.unwrap();
973
974        assert_eq!(
975            resp.headers()
976                .get("Content-Security-Policy")
977                .unwrap()
978                .to_str()
979                .unwrap(),
980            "connect-src 'self' https://youtube.com"
981        );
982    }
983
984    #[ntex::test]
985    async fn test_content_security_policy_default_src() {
986        let mw = Pipeline::new(
987            Helmet::new()
988                .add(
989                    ContentSecurityPolicy::new().default_src(vec!["'self'", "https://youtube.com"]),
990                )
991                .create(ok_service()),
992        );
993
994        let req = TestRequest::default().to_srv_request();
995        let resp = mw.call(req).await.unwrap();
996        assert_eq!(
997            resp.headers()
998                .get("Content-Security-Policy")
999                .unwrap()
1000                .to_str()
1001                .unwrap(),
1002            "default-src 'self' https://youtube.com"
1003        );
1004    }
1005
1006    #[ntex::test]
1007    async fn test_content_security_policy_font_src() {
1008        let mw = Pipeline::new(
1009            Helmet::new()
1010                .add(ContentSecurityPolicy::new().font_src(vec!["'self'", "https://youtube.com"]))
1011                .create(ok_service()),
1012        );
1013
1014        let req = TestRequest::default().to_srv_request();
1015        let resp = mw.call(req).await.unwrap();
1016        assert_eq!(
1017            resp.headers()
1018                .get("Content-Security-Policy")
1019                .unwrap()
1020                .to_str()
1021                .unwrap(),
1022            "font-src 'self' https://youtube.com"
1023        );
1024    }
1025
1026    #[ntex::test]
1027    async fn test_content_security_policy_frame_src() {
1028        let mw = Pipeline::new(
1029            Helmet::new()
1030                .add(ContentSecurityPolicy::new().frame_src(vec!["'self'", "https://youtube.com"]))
1031                .create(ok_service()),
1032        );
1033
1034        let req = TestRequest::default().to_srv_request();
1035        let resp = mw.call(req).await.unwrap();
1036        assert_eq!(
1037            resp.headers()
1038                .get("Content-Security-Policy")
1039                .unwrap()
1040                .to_str()
1041                .unwrap(),
1042            "frame-src 'self' https://youtube.com"
1043        );
1044    }
1045
1046    #[ntex::test]
1047    async fn test_content_security_policy_img_src() {
1048        let mw = Pipeline::new(
1049            Helmet::new()
1050                .add(ContentSecurityPolicy::new().img_src(vec!["'self'", "https://youtube.com"]))
1051                .create(ok_service()),
1052        );
1053
1054        let req = TestRequest::default().to_srv_request();
1055        let resp = mw.call(req).await.unwrap();
1056        assert_eq!(
1057            resp.headers().get("Content-Security-Policy").unwrap(),
1058            "img-src 'self' https://youtube.com"
1059        );
1060    }
1061
1062    #[ntex::test]
1063    async fn test_content_security_policy_manifest_src() {
1064        let mw = Pipeline::new(
1065            Helmet::new()
1066                .add(
1067                    ContentSecurityPolicy::new()
1068                        .manifest_src(vec!["'self'", "https://youtube.com"]),
1069                )
1070                .create(ok_service()),
1071        );
1072
1073        let req = TestRequest::default().to_srv_request();
1074        let resp = mw.call(req).await.unwrap();
1075        assert_eq!(
1076            resp.headers().get("Content-Security-Policy").unwrap(),
1077            "manifest-src 'self' https://youtube.com"
1078        );
1079    }
1080
1081    #[ntex::test]
1082    async fn test_content_security_policy_media_src() {
1083        let mw = Pipeline::new(
1084            Helmet::new()
1085                .add(ContentSecurityPolicy::new().media_src(vec!["'self'", "https://youtube.com"]))
1086                .create(ok_service()),
1087        );
1088
1089        let req = TestRequest::default().to_srv_request();
1090        let resp = mw.call(req).await.unwrap();
1091        assert_eq!(
1092            resp.headers().get("Content-Security-Policy").unwrap(),
1093            "media-src 'self' https://youtube.com"
1094        );
1095    }
1096
1097    #[ntex::test]
1098    async fn test_content_security_policy_object_src() {
1099        let mw = Pipeline::new(
1100            Helmet::new()
1101                .add(ContentSecurityPolicy::new().object_src(vec!["'self'", "https://youtube.com"]))
1102                .create(ok_service()),
1103        );
1104
1105        let req = TestRequest::default().to_srv_request();
1106        let resp = mw.call(req).await.unwrap();
1107        assert_eq!(
1108            resp.headers().get("Content-Security-Policy").unwrap(),
1109            "object-src 'self' https://youtube.com"
1110        );
1111    }
1112
1113    #[ntex::test]
1114    async fn test_content_security_policy_prefetch_src() {
1115        let mw = Pipeline::new(
1116            Helmet::new()
1117                .add(
1118                    ContentSecurityPolicy::new()
1119                        .prefetch_src(vec!["'self'", "https://youtube.com"]),
1120                )
1121                .create(ok_service()),
1122        );
1123
1124        let req = TestRequest::default().to_srv_request();
1125        let resp = mw.call(req).await.unwrap();
1126        assert_eq!(
1127            resp.headers().get("Content-Security-Policy").unwrap(),
1128            "prefetch-src 'self' https://youtube.com"
1129        );
1130    }
1131
1132    #[ntex::test]
1133    async fn test_content_security_policy_script_src() {
1134        let mw = Pipeline::new(
1135            Helmet::new()
1136                .add(ContentSecurityPolicy::new().script_src(vec!["'self'", "https://youtube.com"]))
1137                .create(ok_service()),
1138        );
1139
1140        let req = TestRequest::default().to_srv_request();
1141        let resp = mw.call(req).await.unwrap();
1142        assert_eq!(
1143            resp.headers().get("Content-Security-Policy").unwrap(),
1144            "script-src 'self' https://youtube.com"
1145        );
1146    }
1147
1148    #[ntex::test]
1149    async fn test_content_security_policy_script_src_elem() {
1150        let mw = Pipeline::new(
1151            Helmet::new()
1152                .add(
1153                    ContentSecurityPolicy::new()
1154                        .script_src_elem(vec!["'self'", "https://youtube.com"]),
1155                )
1156                .create(ok_service()),
1157        );
1158
1159        let req = TestRequest::default().to_srv_request();
1160        let resp = mw.call(req).await.unwrap();
1161        assert_eq!(
1162            resp.headers().get("Content-Security-Policy").unwrap(),
1163            "script-src-elem 'self' https://youtube.com"
1164        );
1165    }
1166
1167    #[ntex::test]
1168    async fn test_content_security_policy_script_src_attr() {
1169        let mw = Pipeline::new(
1170            Helmet::new()
1171                .add(
1172                    ContentSecurityPolicy::new()
1173                        .script_src_attr(vec!["'self'", "https://youtube.com"]),
1174                )
1175                .create(ok_service()),
1176        );
1177
1178        let req = TestRequest::default().to_srv_request();
1179        let resp = mw.call(req).await.unwrap();
1180        assert_eq!(
1181            resp.headers().get("Content-Security-Policy").unwrap(),
1182            "script-src-attr 'self' https://youtube.com"
1183        );
1184    }
1185
1186    #[ntex::test]
1187    async fn test_content_security_policy_style_src() {
1188        let mw = Pipeline::new(
1189            Helmet::new()
1190                .add(ContentSecurityPolicy::new().style_src(vec!["'self'", "https://youtube.com"]))
1191                .create(ok_service()),
1192        );
1193
1194        let req = TestRequest::default().to_srv_request();
1195        let resp = mw.call(req).await.unwrap();
1196        assert_eq!(
1197            resp.headers().get("Content-Security-Policy").unwrap(),
1198            "style-src 'self' https://youtube.com"
1199        );
1200    }
1201
1202    #[ntex::test]
1203    async fn test_content_security_policy_style_src_attr() {
1204        let mw = Pipeline::new(
1205            Helmet::new()
1206                .add(
1207                    ContentSecurityPolicy::new()
1208                        .style_src_attr(vec!["'self'", "https://youtube.com"]),
1209                )
1210                .create(ok_service()),
1211        );
1212
1213        let req = TestRequest::default().to_srv_request();
1214        let resp = mw.call(req).await.unwrap();
1215        assert_eq!(
1216            resp.headers().get("Content-Security-Policy").unwrap(),
1217            "style-src-attr 'self' https://youtube.com"
1218        );
1219    }
1220
1221    #[ntex::test]
1222    async fn test_content_security_policy_style_src_elem() {
1223        let mw = Pipeline::new(
1224            Helmet::new()
1225                .add(
1226                    ContentSecurityPolicy::new()
1227                        .style_src_elem(vec!["'self'", "https://youtube.com"]),
1228                )
1229                .create(ok_service()),
1230        );
1231
1232        let req = TestRequest::default().to_srv_request();
1233        let resp = mw.call(req).await.unwrap();
1234        assert_eq!(
1235            resp.headers().get("Content-Security-Policy").unwrap(),
1236            "style-src-elem 'self' https://youtube.com"
1237        );
1238    }
1239
1240    #[ntex::test]
1241    async fn test_content_security_policy_worker_src() {
1242        let mw = Pipeline::new(
1243            Helmet::new()
1244                .add(ContentSecurityPolicy::new().worker_src(vec!["'self'", "https://youtube.com"]))
1245                .create(ok_service()),
1246        );
1247
1248        let req = TestRequest::default().to_srv_request();
1249        let resp = mw.call(req).await.unwrap();
1250        assert_eq!(
1251            resp.headers().get("Content-Security-Policy").unwrap(),
1252            "worker-src 'self' https://youtube.com"
1253        );
1254    }
1255
1256    #[ntex::test]
1257    async fn test_content_security_policy_base_uri() {
1258        let mw = Pipeline::new(
1259            Helmet::new()
1260                .add(ContentSecurityPolicy::new().base_uri(vec!["'self'", "https://youtube.com"]))
1261                .create(ok_service()),
1262        );
1263
1264        let req = TestRequest::default().to_srv_request();
1265        let resp = mw.call(req).await.unwrap();
1266        assert_eq!(
1267            resp.headers().get("Content-Security-Policy").unwrap(),
1268            "base-uri 'self' https://youtube.com"
1269        );
1270    }
1271
1272    #[ntex::test]
1273    async fn test_content_security_policy_sandbox() {
1274        let mw = Pipeline::new(
1275            Helmet::new()
1276                .add(ContentSecurityPolicy::new().sandbox(vec!["allow-forms", "allow-scripts"]))
1277                .create(ok_service()),
1278        );
1279
1280        let req = TestRequest::default().to_srv_request();
1281        let resp = mw.call(req).await.unwrap();
1282        assert_eq!(
1283            resp.headers().get("Content-Security-Policy").unwrap(),
1284            "sandbox allow-forms allow-scripts"
1285        );
1286    }
1287
1288    #[ntex::test]
1289    async fn test_content_security_policy_form_action() {
1290        let mw = Pipeline::new(
1291            Helmet::new()
1292                .add(
1293                    ContentSecurityPolicy::new().form_action(vec!["'self'", "https://youtube.com"]),
1294                )
1295                .create(ok_service()),
1296        );
1297
1298        let req = TestRequest::default()
1299            .header("Origin", "https://example.com")
1300            .to_srv_request();
1301        let resp = mw.call(req).await.unwrap();
1302        assert_eq!(
1303            resp.headers().get("Content-Security-Policy").unwrap(),
1304            "form-action 'self' https://youtube.com"
1305        );
1306    }
1307
1308    #[ntex::test]
1309    async fn test_content_security_policy_frame_ancestors() {
1310        let mw = Pipeline::new(
1311            Helmet::new()
1312                .add(
1313                    ContentSecurityPolicy::new()
1314                        .frame_ancestors(vec!["'self'", "https://youtube.com"]),
1315                )
1316                .create(ok_service()),
1317        );
1318
1319        let req = TestRequest::default()
1320            .header("Origin", "https://example.com")
1321            .to_srv_request();
1322        let resp = mw.call(req).await.unwrap();
1323        assert_eq!(
1324            resp.headers().get("Content-Security-Policy").unwrap(),
1325            "frame-ancestors 'self' https://youtube.com"
1326        );
1327    }
1328
1329    #[ntex::test]
1330    async fn test_content_security_policy_report_to() {
1331        let mw = Pipeline::new(
1332            Helmet::new()
1333                .add(ContentSecurityPolicy::new().report_to(vec!["default", "endpoint", "group"]))
1334                .create(ok_service()),
1335        );
1336
1337        let req = TestRequest::default().to_srv_request();
1338        let resp = mw.call(req).await.unwrap();
1339
1340        assert_eq!(
1341            resp.headers()
1342                .get("Content-Security-Policy")
1343                .unwrap()
1344                .to_str()
1345                .unwrap(),
1346            "report-to default endpoint group; report-uri default endpoint group"
1347        );
1348    }
1349
1350    #[ntex::test]
1351    async fn test_content_security_policy_trusted_types() {
1352        let mw = Pipeline::new(
1353            Helmet::new()
1354                .add(
1355                    ContentSecurityPolicy::new()
1356                        .trusted_types(vec!["'self'", "https://youtube.com"]),
1357                )
1358                .create(ok_service()),
1359        );
1360
1361        let req = TestRequest::default().to_srv_request();
1362        let resp = mw.call(req).await.unwrap();
1363
1364        assert_eq!(
1365            resp.headers()
1366                .get("Content-Security-Policy")
1367                .unwrap()
1368                .to_str()
1369                .unwrap(),
1370            "trusted-types 'self' https://youtube.com"
1371        );
1372    }
1373
1374    #[ntex::test]
1375    async fn test_content_security_policy_require_trusted_types_for() {
1376        let mw = Pipeline::new(
1377            Helmet::new()
1378                .add(
1379                    ContentSecurityPolicy::new().require_trusted_types_for(vec!["script", "style"]),
1380                )
1381                .create(ok_service()),
1382        );
1383
1384        let req = TestRequest::default().to_srv_request();
1385        let resp = mw.call(req).await.unwrap();
1386
1387        assert_eq!(
1388            resp.headers()
1389                .get("Content-Security-Policy")
1390                .unwrap()
1391                .to_str()
1392                .unwrap(),
1393            "require-trusted-types-for script style"
1394        );
1395    }
1396
1397    #[ntex::test]
1398    async fn test_content_security_policy_upgrade_insecure_requests() {
1399        let mw = Pipeline::new(
1400            Helmet::new()
1401                .add(ContentSecurityPolicy::new().upgrade_insecure_requests())
1402                .create(ok_service()),
1403        );
1404
1405        let req = TestRequest::default().to_srv_request();
1406        let resp = mw.call(req).await.unwrap();
1407        assert_eq!(
1408            resp.headers().get("Content-Security-Policy").unwrap(),
1409            "upgrade-insecure-requests"
1410        );
1411    }
1412
1413    #[ntex::test]
1414    async fn test_helmet_default() {
1415        let mw = Pipeline::new(Helmet::default().create(ok_service()));
1416
1417        let req = TestRequest::default().to_srv_request();
1418        let resp = mw.call(req).await.unwrap();
1419
1420        assert_eq!(
1421            resp.headers().get("Content-Security-Policy").unwrap(),
1422            "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"
1423        );
1424        assert_eq!(
1425            resp.headers().get("Cross-Origin-Opener-Policy").unwrap(),
1426            "same-origin"
1427        );
1428        assert_eq!(
1429            resp.headers().get("Cross-Origin-Resource-Policy").unwrap(),
1430            "same-origin"
1431        );
1432        assert_eq!(resp.headers().get("Origin-Agent-Cluster").unwrap(), "?1");
1433        assert_eq!(
1434            resp.headers().get("Referrer-Policy").unwrap(),
1435            "no-referrer"
1436        );
1437        assert_eq!(
1438            resp.headers().get("Strict-Transport-Security").unwrap(),
1439            "max-age=15552000; includeSubDomains"
1440        );
1441        assert_eq!(
1442            resp.headers().get("X-Content-Type-Options").unwrap(),
1443            "nosniff"
1444        );
1445        assert_eq!(resp.headers().get("X-DNS-Prefetch-Control").unwrap(), "off");
1446        assert_eq!(resp.headers().get("X-Download-Options").unwrap(), "noopen");
1447        assert_eq!(resp.headers().get("X-Frame-Options").unwrap(), "SAMEORIGIN");
1448        assert_eq!(
1449            resp.headers()
1450                .get("X-Permitted-Cross-Domain-Policies")
1451                .unwrap(),
1452            "none"
1453        );
1454    }
1455
1456    #[ntex::test]
1457    async fn test_content_security_policy_report_only() {
1458        let mw = Pipeline::new(
1459            Helmet::new()
1460                .add(
1461                    ContentSecurityPolicy::new()
1462                        .report_only()
1463                        .base_uri(vec!["'self'"]),
1464                )
1465                .create(ok_service()),
1466        );
1467
1468        let req = TestRequest::default().to_srv_request();
1469        let resp = mw.call(req).await.unwrap();
1470
1471        assert!(resp.headers().get("Content-Security-Policy").is_none());
1472
1473        assert_eq!(
1474            resp.headers()
1475                .get("Content-Security-Policy-Report-Only")
1476                .unwrap()
1477                .to_str()
1478                .unwrap(),
1479            "base-uri 'self'"
1480        );
1481    }
1482}