From 39b0f75c389bf7d014e01e64dd5541057a887cea Mon Sep 17 00:00:00 2001 From: Atrox Date: Tue, 12 Apr 2022 21:33:27 +0200 Subject: [PATCH] overhaul library --- Cargo.toml | 2 +- README.md | 13 +- src/header/content_security_policy.rs | 34 +- src/header/cross_origin_embedder_policy.rs | 3 +- src/header/cross_origin_opener_policy.rs | 6 +- src/header/cross_origin_resource_policy.rs | 6 +- src/header/expect_ct.rs | 9 +- src/header/mod.rs | 30 +- src/header/origin_agent_cluster.rs | 7 +- src/header/referrer_policy.rs | 6 +- src/header/strict_transport_security.rs | 22 +- src/header/x_content_type_options.rs | 3 +- src/header/x_dns_prefetch_control.rs | 7 +- src/header/x_download_options.rs | 9 +- src/header/x_frame_options.rs | 6 +- .../x_permitted_cross_domain_policies.rs | 12 +- src/header/x_xss_protection.rs | 7 +- src/lib.rs | 311 ++++-------------- 18 files changed, 155 insertions(+), 338 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6222840..246106c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tower-helmet" description = "Helps with securing your tower servers with various HTTP headers " -version = "0.1.0" +version = "0.2.0" authors = ["Atrox "] edition = "2018" license = "MIT" diff --git a/README.md b/README.md index 141f69a..47527a4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ use tower_helmet::header::{ContentSecurityPolicy, ExpectCt, XFrameOptions}; use tower_helmet::HelmetLayer; // default layer with all security headers active -let layer = HelmetLayer::default(); +let layer = HelmetLayer::with_defaults(); // default layer with customizations applied let mut directives = HashMap::new(); @@ -32,13 +32,10 @@ let csp = ContentSecurityPolicy { ..Default::default() }; -let layer = HelmetLayer::default() - .disable_strict_transport_security() - .disable_cross_origin_embedder_policy() - .content_security_policy(csp); +let layer = HelmetLayer::with_defaults().enable(csp); // completely blank layer, selectively enable and add headers -let layer = HelmetLayer::new() - .x_frame_options(XFrameOptions::SameOrigin) - .expect_ct(ExpectCt::default()); +let layer = HelmetLayer::blank() + .enable(XFrameOptions::SameOrigin) + .enable(ExpectCt::default()); ``` diff --git a/src/header/content_security_policy.rs b/src/header/content_security_policy.rs index cf90134..0ed28aa 100644 --- a/src/header/content_security_policy.rs +++ b/src/header/content_security_policy.rs @@ -1,8 +1,10 @@ -use crate::IntoHeader; +use std::collections::HashMap; + use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; use lazy_static::lazy_static; -use std::collections::HashMap; + +use crate::IntoHeader; lazy_static! { static ref DEFAULT_DIRECTIVES: HashMap<&'static str, Vec<&'static str>> = { @@ -22,13 +24,13 @@ lazy_static! { }; } -/// `ContentSecurityPolicy` sets the `Content-Security-Policy` header which helps mitigate cross-site scripting attacks, among other things. -/// See [MDN's introductory article on Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). +/// `ContentSecurityPolicy` sets the `Content-Security-Policy` header which helps mitigate +/// cross-site scripting attacks, among other things. See [MDN's introductory article on Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). /// /// This middleware performs very little validation. You should rely on CSP checkers like [CSP Evaluator](https://csp-evaluator.withgoogle.com/) instead. /// -/// If no directive is supplied and `use_defaults` is `true`, the following policy is set (whitespace added for readability): -/// ```text +/// If no directive is supplied and `use_defaults` is `true`, the following policy is set +/// (whitespace added for readability): ```text /// default-src 'self'; /// base-uri 'self'; /// block-all-mixed-content; @@ -41,9 +43,6 @@ lazy_static! { /// style-src 'self' https: 'unsafe-inline'; /// upgrade-insecure-requests /// ``` -/// -/// Examples: -/// TODO pub struct ContentSecurityPolicy<'a> { pub use_defaults: bool, /// Each key is the directive name in kebab case (such as `default-src`). @@ -116,20 +115,3 @@ impl<'a> IntoHeader for ContentSecurityPolicy<'a> { HeaderValue::from_str(header.trim()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_wow() { - for (k, v) in DEFAULT_DIRECTIVES.iter() { - println!("{}:{:?}", k, v) - } - - let csp = ContentSecurityPolicy::default(); - - assert_eq!(csp.header_name(), "Content-Security-Policy"); - assert_eq!(csp.header_value().unwrap(), "frame-ancestors 'self'; object-src 'none'; style-src 'self' https: 'unsafe-inline'; default-src 'self'; img-src 'self' data:; script-src-attr 'none'; upgrade-insecure-requests ; script-src 'self'; block-all-mixed-content ; base-uri 'self'; font-src 'self' https: data:"); - } -} diff --git a/src/header/cross_origin_embedder_policy.rs b/src/header/cross_origin_embedder_policy.rs index 5cb212a..b430002 100644 --- a/src/header/cross_origin_embedder_policy.rs +++ b/src/header/cross_origin_embedder_policy.rs @@ -1,7 +1,8 @@ -use crate::IntoHeader; use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; +use crate::IntoHeader; + /// `CrossOriginEmbedderPolicy` sets the `Cross-Origin-Embedder-Policy` header to `require-corp`. /// See [MDN's article on this header](https://developer.cdn.mozilla.net/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) for more. pub struct CrossOriginEmbedderPolicy; diff --git a/src/header/cross_origin_opener_policy.rs b/src/header/cross_origin_opener_policy.rs index 3c8428a..a8d6e83 100644 --- a/src/header/cross_origin_opener_policy.rs +++ b/src/header/cross_origin_opener_policy.rs @@ -1,7 +1,9 @@ -use crate::IntoHeader; +use std::fmt::{Display, Formatter}; + use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -use std::fmt::{Display, Formatter}; + +use crate::IntoHeader; /// `CrossOriginOpenerPolicy` sets the `Cross-Origin-Opener-Policy` header. /// For more, see [MDN's article on this header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy). diff --git a/src/header/cross_origin_resource_policy.rs b/src/header/cross_origin_resource_policy.rs index 2ca2cae..f4b738c 100644 --- a/src/header/cross_origin_resource_policy.rs +++ b/src/header/cross_origin_resource_policy.rs @@ -1,7 +1,9 @@ -use crate::IntoHeader; +use std::fmt::{Display, Formatter}; + use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -use std::fmt::{Display, Formatter}; + +use crate::IntoHeader; /// `CrossOriginResourcePolicy` sets the `Cross-Origin-Resource-Policy` header. /// For more, see ["Consider deploying Cross-Origin Resource Policy](https://resourcepolicy.fyi/) and [MDN's article on this header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy). diff --git a/src/header/expect_ct.rs b/src/header/expect_ct.rs index 50f545d..3883524 100644 --- a/src/header/expect_ct.rs +++ b/src/header/expect_ct.rs @@ -1,14 +1,17 @@ -use crate::IntoHeader; +use std::time::Duration; + use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -use std::time::Duration; + +use crate::IntoHeader; /// `ExpectCt` sets the `Expect-CT` header which helps mitigate misissued SSL certificates. /// See [MDN's article on Certificate Transparency](https://developer.mozilla.org/en-US/docs/Web/Security/Certificate_Transparency) and the [`Expect-CT` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT) for more. pub struct ExpectCt { /// `max_age` is the number of seconds to expect Certificate Transparency. pub max_age: Duration, - /// If `true`, the user agent (usually a browser) should refuse future connections that violate its Certificate Transparency policy. + /// If `true`, the user agent (usually a browser) should refuse future connections that violate + /// its Certificate Transparency policy. pub enforce: bool, /// If set, complying user agents will report Certificate Transparency failures to this URL. pub report_uri: Option, diff --git a/src/header/mod.rs b/src/header/mod.rs index 93e90af..421337e 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -13,19 +13,17 @@ mod x_frame_options; mod x_permitted_cross_domain_policies; mod x_xss_protection; -pub use self::{ - content_security_policy::ContentSecurityPolicy, - cross_origin_embedder_policy::CrossOriginEmbedderPolicy, - cross_origin_opener_policy::CrossOriginOpenerPolicy, - cross_origin_resource_policy::CrossOriginResourcePolicy, - expect_ct::ExpectCt, - origin_agent_cluster::OriginAgentCluster, - referrer_policy::{ReferrerPolicy, ReferrerPolicyValue}, - strict_transport_security::StrictTransportSecurity, - x_content_type_options::XContentTypeOptions, - x_dns_prefetch_control::XDnsPrefetchControl, - x_download_options::XDownloadOptions, - x_frame_options::XFrameOptions, - x_permitted_cross_domain_policies::XPermittedCrossDomainPolicies, - x_xss_protection::XXSSProtection, -}; +pub use self::content_security_policy::ContentSecurityPolicy; +pub use self::cross_origin_embedder_policy::CrossOriginEmbedderPolicy; +pub use self::cross_origin_opener_policy::CrossOriginOpenerPolicy; +pub use self::cross_origin_resource_policy::CrossOriginResourcePolicy; +pub use self::expect_ct::ExpectCt; +pub use self::origin_agent_cluster::OriginAgentCluster; +pub use self::referrer_policy::{ReferrerPolicy, ReferrerPolicyValue}; +pub use self::strict_transport_security::StrictTransportSecurity; +pub use self::x_content_type_options::XContentTypeOptions; +pub use self::x_dns_prefetch_control::XDnsPrefetchControl; +pub use self::x_download_options::XDownloadOptions; +pub use self::x_frame_options::XFrameOptions; +pub use self::x_permitted_cross_domain_policies::XPermittedCrossDomainPolicies; +pub use self::x_xss_protection::XXSSProtection; diff --git a/src/header/origin_agent_cluster.rs b/src/header/origin_agent_cluster.rs index 631bc19..c1192ed 100644 --- a/src/header/origin_agent_cluster.rs +++ b/src/header/origin_agent_cluster.rs @@ -1,9 +1,10 @@ -use crate::IntoHeader; use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -/// `OriginAgentCluster` sets the `Origin-Agent-Cluster` header, which provides a mechanism to allow web applications to isolate their origins. -/// Read more about it [in the spec](https://whatpr.org/html/6214/origin.html#origin-keyed-agent-clusters). +use crate::IntoHeader; + +/// `OriginAgentCluster` sets the `Origin-Agent-Cluster` header, which provides a mechanism to allow +/// web applications to isolate their origins. Read more about it [in the spec](https://whatpr.org/html/6214/origin.html#origin-keyed-agent-clusters). pub struct OriginAgentCluster; impl Default for OriginAgentCluster { diff --git a/src/header/referrer_policy.rs b/src/header/referrer_policy.rs index de483ba..27ae912 100644 --- a/src/header/referrer_policy.rs +++ b/src/header/referrer_policy.rs @@ -1,7 +1,9 @@ -use crate::IntoHeader; +use std::fmt::{Display, Formatter}; + use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -use std::fmt::{Display, Formatter}; + +use crate::IntoHeader; /// `ReferrerPolicy` sets the `Referrer-Policy` header which controls what information is set in [the `Referer` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer). /// See ["Referer header: privacy and security concerns"](https://developer.mozilla.org/en-US/docs/Web/Security/Referer_header:_privacy_and_security_concerns) and [the header's documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) on MDN for more. diff --git a/src/header/strict_transport_security.rs b/src/header/strict_transport_security.rs index 46f9965..c53effc 100644 --- a/src/header/strict_transport_security.rs +++ b/src/header/strict_transport_security.rs @@ -1,17 +1,21 @@ -use crate::IntoHeader; -use http::header::{HeaderName, InvalidHeaderValue}; -use http::HeaderValue; use std::time::Duration; -/// `StrictTransportSecurity` sets the `Strict-Transport-Security` header which tells browsers to prefer HTTPS over insecure HTTP. -/// See [the documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) for more. +use http::header::{HeaderName, InvalidHeaderValue}; +use http::HeaderValue; + +use crate::IntoHeader; + +/// `StrictTransportSecurity` sets the `Strict-Transport-Security` header which tells browsers to +/// prefer HTTPS over insecure HTTP. See [the documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) for more. pub struct StrictTransportSecurity { - /// `max_age` is the number of seconds browsers should remember to prefer HTTPS. It defaults to `15552000`, which is 180 days. + /// `max_age` is the number of seconds browsers should remember to prefer HTTPS. It defaults to + /// `15552000`, which is 180 days. pub max_age: Duration, - /// `include_subdomains` dictates whether to include the `includeSubDomains` directive, which makes this policy extend to subdomains. It defaults to `true`. + /// `include_subdomains` dictates whether to include the `includeSubDomains` directive, which + /// makes this policy extend to subdomains. It defaults to `true`. pub include_subdomains: bool, - /// If true, it adds the `preload` directive, expressing intent to add your HSTS policy to browsers. - /// See [the "Preloading Strict Transport Security" section on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#Preloading_Strict_Transport_Security) for more. + /// If true, it adds the `preload` directive, expressing intent to add your HSTS policy to + /// browsers. See [the "Preloading Strict Transport Security" section on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#Preloading_Strict_Transport_Security) for more. pub preload: bool, } diff --git a/src/header/x_content_type_options.rs b/src/header/x_content_type_options.rs index f577515..08f8bd0 100644 --- a/src/header/x_content_type_options.rs +++ b/src/header/x_content_type_options.rs @@ -1,7 +1,8 @@ -use crate::IntoHeader; use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; +use crate::IntoHeader; + /// `XContentTypeOptions` sets the `X-Content-Type-Options` header to `nosniff`. /// This mitigates [MIME type sniffing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing) which can cause security vulnerabilities. /// See [documentation for this header on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) for more. diff --git a/src/header/x_dns_prefetch_control.rs b/src/header/x_dns_prefetch_control.rs index 352325e..2dba4cf 100644 --- a/src/header/x_dns_prefetch_control.rs +++ b/src/header/x_dns_prefetch_control.rs @@ -1,9 +1,10 @@ -use crate::IntoHeader; use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -/// `XDnsPrefetchControl` sets the `X-DNS-Prefetch-Control` header to help control DNS prefetching, which can improve user privacy at the expense of performance. -/// See [documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) for more. +use crate::IntoHeader; + +/// `XDnsPrefetchControl` sets the `X-DNS-Prefetch-Control` header to help control DNS prefetching, +/// which can improve user privacy at the expense of performance. See [documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) for more. #[derive(Default)] pub struct XDnsPrefetchControl( /// Is indictating whether to enable DNS prefetching. diff --git a/src/header/x_download_options.rs b/src/header/x_download_options.rs index f0c845b..16ccc7e 100644 --- a/src/header/x_download_options.rs +++ b/src/header/x_download_options.rs @@ -1,10 +1,11 @@ -use crate::IntoHeader; use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -/// `XDownloadOptions` sets the `X-Download-Options` header, which is specific to Internet Explorer 8. -/// It forces potentially-unsafe downloads to be saved, mitigating execution of HTML in your site's context. -/// For more, see [this old post on MSDN](https://docs.microsoft.com/en-us/archive/blogs/ie/ie8-security-part-v-comprehensive-protection). +use crate::IntoHeader; + +/// `XDownloadOptions` sets the `X-Download-Options` header, which is specific to Internet Explorer +/// 8. It forces potentially-unsafe downloads to be saved, mitigating execution of HTML in your +/// site's context. For more, see [this old post on MSDN](https://docs.microsoft.com/en-us/archive/blogs/ie/ie8-security-part-v-comprehensive-protection). pub struct XDownloadOptions; impl Default for XDownloadOptions { diff --git a/src/header/x_frame_options.rs b/src/header/x_frame_options.rs index a4e7bc9..372a7fb 100644 --- a/src/header/x_frame_options.rs +++ b/src/header/x_frame_options.rs @@ -1,7 +1,9 @@ -use crate::IntoHeader; +use std::fmt::{Display, Formatter}; + use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -use std::fmt::{Display, Formatter}; + +use crate::IntoHeader; /// `XFrameOptions` sets the `X-Frame-Options` header to help you mitigate [clickjacking attacks](https://en.wikipedia.org/wiki/Clickjacking). /// This header is superseded by [the `frame-ancestors` Content Security Policy directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) but is still useful on old browsers. diff --git a/src/header/x_permitted_cross_domain_policies.rs b/src/header/x_permitted_cross_domain_policies.rs index 8eb36b7..efcc056 100644 --- a/src/header/x_permitted_cross_domain_policies.rs +++ b/src/header/x_permitted_cross_domain_policies.rs @@ -1,10 +1,12 @@ -use crate::IntoHeader; -use http::header::{HeaderName, InvalidHeaderValue}; -use http::HeaderValue; use std::fmt::{Display, Formatter}; -/// `XPermittedCrossDomainPolicies` sets the `X-Permitted-Cross-Domain-Policies` header, which tells some clients (mostly Adobe products) your domain's policy for loading cross-domain content. -/// See [the description on OWASP](https://owasp.org/www-project-secure-headers/) for more. +use http::header::{HeaderName, InvalidHeaderValue}; +use http::HeaderValue; + +use crate::IntoHeader; + +/// `XPermittedCrossDomainPolicies` sets the `X-Permitted-Cross-Domain-Policies` header, which tells +/// some clients (mostly Adobe products) your domain's policy for loading cross-domain content. See [the description on OWASP](https://owasp.org/www-project-secure-headers/) for more. pub enum XPermittedCrossDomainPolicies { None, MasterOnly, diff --git a/src/header/x_xss_protection.rs b/src/header/x_xss_protection.rs index aed0d6d..102121e 100644 --- a/src/header/x_xss_protection.rs +++ b/src/header/x_xss_protection.rs @@ -1,9 +1,10 @@ -use crate::IntoHeader; use http::header::{HeaderName, InvalidHeaderValue}; use http::HeaderValue; -/// `XXSSProtection` disables browsers' buggy cross-site scripting filter by setting the `X-XSS-Protection` header to `0`. -/// See [discussion about disabling the header here](https://github.com/helmetjs/helmet/issues/230) and [documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection). +use crate::IntoHeader; + +/// `XXSSProtection` disables browsers' buggy cross-site scripting filter by setting the +/// `X-XSS-Protection` header to `0`. See [discussion about disabling the header here](https://github.com/helmetjs/helmet/issues/230) and [documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection). pub struct XXSSProtection; impl Default for XXSSProtection { diff --git a/src/lib.rs b/src/lib.rs index 8c5465e..dbdd169 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,308 +1,125 @@ //! # Overview //! -//! `tower-helmet` helps you secure your tower server by setting various HTTP headers. _It's not a silver bullet_, but it can help! +//! `tower-helmet` helps you secure your tower server by setting various HTTP headers. _It's not a +//! silver bullet_, but it can help! //! -//! You can find a list of all available headers under the [header] module. By default (with [HelmetLayer::default]) **all of them** are enabled. -//! Please take a good look at [ContentSecurityPolicy]. Most of the time you will need to adapt this one to your needs. +//! You can find a list of all available headers under the [header] module. By default (with +//! [HelmetLayer::with_defaults]) **all of them** are enabled. Please take a good look at +//! [ContentSecurityPolicy]. Most of the time you will need to adapt this one to your needs. //! //! # Examples //! //! ``` +//! use std::collections::HashMap; +//! //! use tower_helmet::header::{ContentSecurityPolicy, ExpectCt, XFrameOptions}; //! use tower_helmet::HelmetLayer; //! //! // default layer with all security headers active -//! let layer = HelmetLayer::default(); +//! let layer = HelmetLayer::with_defaults(); //! -//! // default layer with customizations applied +//! // default layer with csp customizations applied //! let mut directives = HashMap::new(); //! directives.insert("default-src", vec!["'self'", "https://example.com"]); //! directives.insert("img-src", vec!["'self'", "data:", "https://example.com"]); -//! directives.insert("script-src", vec!["'self'", "'unsafe-inline'", "https://example.com"]); +//! directives.insert( +//! "script-src", +//! vec!["'self'", "'unsafe-inline'", "https://example.com"], +//! ); //! let csp = ContentSecurityPolicy { -//! directives, -//! ..Default::default() +//! directives, +//! ..Default::default() //! }; //! -//! let layer = HelmetLayer::default() -//! .disable_strict_transport_security() -//! .disable_cross_origin_embedder_policy() -//! .content_security_policy(csp); +//! let layer = HelmetLayer::with_defaults().enable(csp); //! //! // completely blank layer, selectively enable and add headers -//! let layer = HelmetLayer::new() -//! .x_frame_options(XFrameOptions::SameOrigin) -//! .expect_ct(ExpectCt::default()); +//! let layer = HelmetLayer::blank() +//! .enable(XFrameOptions::SameOrigin) +//! .enable(ExpectCt::default()); //! ``` pub mod header; -use futures::ready; -use http::header::{HeaderName, InvalidHeaderValue}; -use http::{HeaderMap, HeaderValue, Request, Response}; -use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; + +use futures::ready; +use http::header::{AsHeaderName, HeaderName, InvalidHeaderValue}; +use http::{HeaderMap, HeaderValue, Request, Response}; +use pin_project_lite::pin_project; use tower_layer::Layer; use tower_service::Service; -use header::{ +use crate::header::{ ContentSecurityPolicy, CrossOriginEmbedderPolicy, CrossOriginOpenerPolicy, CrossOriginResourcePolicy, ExpectCt, OriginAgentCluster, ReferrerPolicy, StrictTransportSecurity, XContentTypeOptions, XDnsPrefetchControl, XDownloadOptions, XFrameOptions, XPermittedCrossDomainPolicies, XXSSProtection, }; -trait IntoHeader { +pub trait IntoHeader { fn header_name(&self) -> HeaderName; fn header_value(&self) -> Result; } /// HelmetLayer -pub struct HelmetLayer<'a> { - content_security_policy: Option>, - cross_origin_embedder_policy: Option, - cross_origin_opener_policy: Option, - cross_origin_resource_policy: Option, - expect_ct: Option, - origin_agent_cluster: Option, - referrer_policy: Option, - strict_transport_security: Option, - x_content_type_options: Option, - x_dns_prefetch_control: Option, - x_download_options: Option, - x_frame_options: Option, - x_permitted_cross_domain_policies: Option, - x_xss_protection: Option, +pub struct HelmetLayer { + headers: HeaderMap, } -impl<'a> Default for HelmetLayer<'a> { - fn default() -> Self { - HelmetLayer { - content_security_policy: Some(ContentSecurityPolicy::default()), - cross_origin_embedder_policy: Some(CrossOriginEmbedderPolicy::default()), - cross_origin_opener_policy: Some(CrossOriginOpenerPolicy::default()), - cross_origin_resource_policy: Some(CrossOriginResourcePolicy::default()), - expect_ct: Some(ExpectCt::default()), - origin_agent_cluster: Some(OriginAgentCluster::default()), - referrer_policy: Some(ReferrerPolicy::default()), - strict_transport_security: Some(StrictTransportSecurity::default()), - x_content_type_options: Some(XContentTypeOptions::default()), - x_dns_prefetch_control: Some(XDnsPrefetchControl::default()), - x_download_options: Some(XDownloadOptions::default()), - x_frame_options: Some(XFrameOptions::default()), - x_permitted_cross_domain_policies: Some(XPermittedCrossDomainPolicies::default()), - x_xss_protection: Some(XXSSProtection::default()), - } - } -} - -impl<'a> HelmetLayer<'a> { - pub fn new() -> Self { - HelmetLayer { - content_security_policy: None, - cross_origin_embedder_policy: None, - cross_origin_opener_policy: None, - cross_origin_resource_policy: None, - expect_ct: None, - origin_agent_cluster: None, - referrer_policy: None, - strict_transport_security: None, - x_content_type_options: None, - x_dns_prefetch_control: None, - x_download_options: None, - x_frame_options: None, - x_permitted_cross_domain_policies: None, - x_xss_protection: None, +impl HelmetLayer { + /// Helmet without any headers added in by default. See [`enable`] for enabling headers. + pub fn blank() -> Self { + Self { + headers: HeaderMap::new(), } } - pub fn recommended_defaults() -> Self { - HelmetLayer::default() + /// Helmet with most of the headers already added with the base configuration. + pub fn with_defaults() -> Self { + let mut layer = Self::blank(); + layer + .enable(ContentSecurityPolicy::default()) + .enable(CrossOriginEmbedderPolicy::default()) + .enable(CrossOriginOpenerPolicy::default()) + .enable(CrossOriginResourcePolicy::default()) + .enable(ExpectCt::default()) + .enable(OriginAgentCluster::default()) + .enable(ReferrerPolicy::default()) + .enable(StrictTransportSecurity::default()) + .enable(XContentTypeOptions::default()) + .enable(XDnsPrefetchControl::default()) + .enable(XDownloadOptions::default()) + .enable(XFrameOptions::default()) + .enable(XPermittedCrossDomainPolicies::default()) + .enable(XXSSProtection::default()); + + layer } - pub fn content_security_policy(&mut self, v: ContentSecurityPolicy<'a>) -> &mut Self { - self.content_security_policy = Some(v); - self - } - pub fn disable_content_security_policy(&mut self) -> &mut Self { - self.content_security_policy = None; + pub fn enable(&mut self, h: impl IntoHeader) -> &mut Self { + self.headers + .insert(h.header_name(), h.header_value().unwrap()); self } - pub fn cross_origin_embedder_policy(&mut self, v: CrossOriginEmbedderPolicy) -> &mut Self { - self.cross_origin_embedder_policy = Some(v); - self - } - pub fn disable_cross_origin_embedder_policy(&mut self) -> &mut Self { - self.cross_origin_embedder_policy = None; - self - } - - pub fn cross_origin_opener_policy(&mut self, v: CrossOriginOpenerPolicy) -> &mut Self { - self.cross_origin_opener_policy = Some(v); - self - } - pub fn disable_cross_origin_opener_policy(&mut self) -> &mut Self { - self.cross_origin_opener_policy = None; - self - } - - pub fn cross_origin_resource_policy(&mut self, v: CrossOriginResourcePolicy) -> &mut Self { - self.cross_origin_resource_policy = Some(v); - self - } - pub fn disable_cross_origin_resource_policy(&mut self) -> &mut Self { - self.cross_origin_resource_policy = None; - self - } - - pub fn expect_ct(&mut self, v: ExpectCt) -> &mut Self { - self.expect_ct = Some(v); - self - } - pub fn disable_expect_ct(&mut self) -> &mut Self { - self.expect_ct = None; - self - } - - pub fn origin_agent_cluster(&mut self, v: OriginAgentCluster) -> &mut Self { - self.origin_agent_cluster = Some(v); - self - } - pub fn disable_origin_agent_cluster(&mut self) -> &mut Self { - self.origin_agent_cluster = None; - self - } - - pub fn referrer_policy(&mut self, v: ReferrerPolicy) -> &mut Self { - self.referrer_policy = Some(v); - self - } - pub fn disable_referrer_policy(&mut self) -> &mut Self { - self.referrer_policy = None; - self - } - - pub fn strict_transport_security(&mut self, v: StrictTransportSecurity) -> &mut Self { - self.strict_transport_security = Some(v); - self - } - pub fn disable_strict_transport_security(&mut self) -> &mut Self { - self.strict_transport_security = None; - self - } - - pub fn x_content_type_options(&mut self, v: XContentTypeOptions) -> &mut Self { - self.x_content_type_options = Some(v); - self - } - pub fn disable_x_content_type_options(&mut self) -> &mut Self { - self.x_content_type_options = None; - self - } - - pub fn x_dns_prefetch_control(&mut self, v: XDnsPrefetchControl) -> &mut Self { - self.x_dns_prefetch_control = Some(v); - self - } - pub fn disable_x_dns_prefetch_control(&mut self) -> &mut Self { - self.x_dns_prefetch_control = None; - self - } - - pub fn x_download_options(&mut self, v: XDownloadOptions) -> &mut Self { - self.x_download_options = Some(v); - self - } - pub fn disable_x_download_options(&mut self) -> &mut Self { - self.x_download_options = None; - self - } - - pub fn x_frame_options(&mut self, v: XFrameOptions) -> &mut Self { - self.x_frame_options = Some(v); - self - } - pub fn disable_x_frame_options(&mut self) -> &mut Self { - self.x_frame_options = None; - self - } - - pub fn x_permitted_cross_domain_policies( - &mut self, - v: XPermittedCrossDomainPolicies, - ) -> &mut Self { - self.x_permitted_cross_domain_policies = Some(v); - self - } - pub fn disable_x_permitted_cross_domain_policies(&mut self) -> &mut Self { - self.x_permitted_cross_domain_policies = None; - self - } - - pub fn x_xss_protection(&mut self, v: XXSSProtection) -> &mut Self { - self.x_xss_protection = Some(v); - self - } - pub fn disable_x_xss_protection(&mut self) -> &mut Self { - self.x_xss_protection = None; + pub fn remove(&mut self, key: K) -> &mut Self + where + K: AsHeaderName, + { + self.headers.remove(key); self } } -impl<'a, S> Layer for HelmetLayer<'a> { +impl<'a, S> Layer for HelmetLayer { type Service = HelmetService; fn layer(&self, service: S) -> Self::Service { - let mut headers = HeaderMap::new(); - - if let Some(h) = &self.content_security_policy { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.cross_origin_embedder_policy { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.cross_origin_opener_policy { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.cross_origin_resource_policy { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.expect_ct { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.origin_agent_cluster { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.referrer_policy { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.strict_transport_security { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.x_content_type_options { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.x_dns_prefetch_control { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.x_download_options { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.x_frame_options { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.x_permitted_cross_domain_policies { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - if let Some(h) = &self.x_xss_protection { - headers.insert(h.header_name(), h.header_value().unwrap()); - } - HelmetService { inner: service, - headers, + headers: self.headers.clone(), } } } @@ -355,7 +172,7 @@ where let mut res: Response = ready!(this.future.poll(cx)?); let headers = res.headers_mut(); - for (name, value) in this.headers.iter() { + for (name, value) in this.headers { headers.insert(name, value.clone()); }