overhaul library

master
Atrox 2022-04-12 21:33:27 +02:00
parent 412cf59ba9
commit 39b0f75c38
18 changed files with 155 additions and 338 deletions

View File

@ -1,7 +1,7 @@
[package] [package]
name = "tower-helmet" name = "tower-helmet"
description = "Helps with securing your tower servers with various HTTP headers " description = "Helps with securing your tower servers with various HTTP headers "
version = "0.1.0" version = "0.2.0"
authors = ["Atrox <hello@atrox.dev>"] authors = ["Atrox <hello@atrox.dev>"]
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"

View File

@ -20,7 +20,7 @@ use tower_helmet::header::{ContentSecurityPolicy, ExpectCt, XFrameOptions};
use tower_helmet::HelmetLayer; use tower_helmet::HelmetLayer;
// default layer with all security headers active // default layer with all security headers active
let layer = HelmetLayer::default(); let layer = HelmetLayer::with_defaults();
// default layer with customizations applied // default layer with customizations applied
let mut directives = HashMap::new(); let mut directives = HashMap::new();
@ -32,13 +32,10 @@ let csp = ContentSecurityPolicy {
..Default::default() ..Default::default()
}; };
let layer = HelmetLayer::default() let layer = HelmetLayer::with_defaults().enable(csp);
.disable_strict_transport_security()
.disable_cross_origin_embedder_policy()
.content_security_policy(csp);
// completely blank layer, selectively enable and add headers // completely blank layer, selectively enable and add headers
let layer = HelmetLayer::new() let layer = HelmetLayer::blank()
.x_frame_options(XFrameOptions::SameOrigin) .enable(XFrameOptions::SameOrigin)
.expect_ct(ExpectCt::default()); .enable(ExpectCt::default());
``` ```

View File

@ -1,8 +1,10 @@
use crate::IntoHeader; use std::collections::HashMap;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::collections::HashMap;
use crate::IntoHeader;
lazy_static! { lazy_static! {
static ref DEFAULT_DIRECTIVES: HashMap<&'static str, Vec<&'static str>> = { 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. /// `ContentSecurityPolicy` sets the `Content-Security-Policy` header which helps mitigate
/// See [MDN's introductory article on Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). /// 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. /// 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): /// If no directive is supplied and `use_defaults` is `true`, the following policy is set
/// ```text /// (whitespace added for readability): ```text
/// default-src 'self'; /// default-src 'self';
/// base-uri 'self'; /// base-uri 'self';
/// block-all-mixed-content; /// block-all-mixed-content;
@ -41,9 +43,6 @@ lazy_static! {
/// style-src 'self' https: 'unsafe-inline'; /// style-src 'self' https: 'unsafe-inline';
/// upgrade-insecure-requests /// upgrade-insecure-requests
/// ``` /// ```
///
/// Examples:
/// TODO
pub struct ContentSecurityPolicy<'a> { pub struct ContentSecurityPolicy<'a> {
pub use_defaults: bool, pub use_defaults: bool,
/// Each key is the directive name in kebab case (such as `default-src`). /// 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()) 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:");
}
}

View File

@ -1,7 +1,8 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
use crate::IntoHeader;
/// `CrossOriginEmbedderPolicy` sets the `Cross-Origin-Embedder-Policy` header to `require-corp`. /// `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. /// 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; pub struct CrossOriginEmbedderPolicy;

View File

@ -1,7 +1,9 @@
use crate::IntoHeader; use std::fmt::{Display, Formatter};
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
use std::fmt::{Display, Formatter};
use crate::IntoHeader;
/// `CrossOriginOpenerPolicy` sets the `Cross-Origin-Opener-Policy` header. /// `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). /// For more, see [MDN's article on this header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy).

View File

@ -1,7 +1,9 @@
use crate::IntoHeader; use std::fmt::{Display, Formatter};
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
use std::fmt::{Display, Formatter};
use crate::IntoHeader;
/// `CrossOriginResourcePolicy` sets the `Cross-Origin-Resource-Policy` header. /// `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). /// 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).

View File

@ -1,14 +1,17 @@
use crate::IntoHeader; use std::time::Duration;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
use std::time::Duration;
use crate::IntoHeader;
/// `ExpectCt` sets the `Expect-CT` header which helps mitigate misissued SSL certificates. /// `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. /// 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 { pub struct ExpectCt {
/// `max_age` is the number of seconds to expect Certificate Transparency. /// `max_age` is the number of seconds to expect Certificate Transparency.
pub max_age: Duration, 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, pub enforce: bool,
/// If set, complying user agents will report Certificate Transparency failures to this URL. /// If set, complying user agents will report Certificate Transparency failures to this URL.
pub report_uri: Option<String>, pub report_uri: Option<String>,

View File

@ -13,19 +13,17 @@ mod x_frame_options;
mod x_permitted_cross_domain_policies; mod x_permitted_cross_domain_policies;
mod x_xss_protection; mod x_xss_protection;
pub use self::{ pub use self::content_security_policy::ContentSecurityPolicy;
content_security_policy::ContentSecurityPolicy, pub use self::cross_origin_embedder_policy::CrossOriginEmbedderPolicy;
cross_origin_embedder_policy::CrossOriginEmbedderPolicy, pub use self::cross_origin_opener_policy::CrossOriginOpenerPolicy;
cross_origin_opener_policy::CrossOriginOpenerPolicy, pub use self::cross_origin_resource_policy::CrossOriginResourcePolicy;
cross_origin_resource_policy::CrossOriginResourcePolicy, pub use self::expect_ct::ExpectCt;
expect_ct::ExpectCt, pub use self::origin_agent_cluster::OriginAgentCluster;
origin_agent_cluster::OriginAgentCluster, pub use self::referrer_policy::{ReferrerPolicy, ReferrerPolicyValue};
referrer_policy::{ReferrerPolicy, ReferrerPolicyValue}, pub use self::strict_transport_security::StrictTransportSecurity;
strict_transport_security::StrictTransportSecurity, pub use self::x_content_type_options::XContentTypeOptions;
x_content_type_options::XContentTypeOptions, pub use self::x_dns_prefetch_control::XDnsPrefetchControl;
x_dns_prefetch_control::XDnsPrefetchControl, pub use self::x_download_options::XDownloadOptions;
x_download_options::XDownloadOptions, pub use self::x_frame_options::XFrameOptions;
x_frame_options::XFrameOptions, pub use self::x_permitted_cross_domain_policies::XPermittedCrossDomainPolicies;
x_permitted_cross_domain_policies::XPermittedCrossDomainPolicies, pub use self::x_xss_protection::XXSSProtection;
x_xss_protection::XXSSProtection,
};

View File

@ -1,9 +1,10 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
/// `OriginAgentCluster` sets the `Origin-Agent-Cluster` header, which provides a mechanism to allow web applications to isolate their origins. use crate::IntoHeader;
/// Read more about it [in the spec](https://whatpr.org/html/6214/origin.html#origin-keyed-agent-clusters).
/// `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; pub struct OriginAgentCluster;
impl Default for OriginAgentCluster { impl Default for OriginAgentCluster {

View File

@ -1,7 +1,9 @@
use crate::IntoHeader; use std::fmt::{Display, Formatter};
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; 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). /// `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. /// 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.

View File

@ -1,17 +1,21 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue;
use std::time::Duration; use std::time::Duration;
/// `StrictTransportSecurity` sets the `Strict-Transport-Security` header which tells browsers to prefer HTTPS over insecure HTTP. use http::header::{HeaderName, InvalidHeaderValue};
/// See [the documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) for more. 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 { 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, 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, pub include_subdomains: bool,
/// If true, it adds the `preload` directive, expressing intent to add your HSTS policy to browsers. /// If true, it adds the `preload` directive, expressing intent to add your HSTS policy to
/// 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. /// 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, pub preload: bool,
} }

View File

@ -1,7 +1,8 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
use crate::IntoHeader;
/// `XContentTypeOptions` sets the `X-Content-Type-Options` header to `nosniff`. /// `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. /// 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. /// See [documentation for this header on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) for more.

View File

@ -1,9 +1,10 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; 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. use crate::IntoHeader;
/// See [documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) for more.
/// `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)] #[derive(Default)]
pub struct XDnsPrefetchControl( pub struct XDnsPrefetchControl(
/// Is indictating whether to enable DNS prefetching. /// Is indictating whether to enable DNS prefetching.

View File

@ -1,10 +1,11 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
/// `XDownloadOptions` sets the `X-Download-Options` header, which is specific to Internet Explorer 8. use crate::IntoHeader;
/// 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). /// `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; pub struct XDownloadOptions;
impl Default for XDownloadOptions { impl Default for XDownloadOptions {

View File

@ -1,7 +1,9 @@
use crate::IntoHeader; use std::fmt::{Display, Formatter};
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; 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). /// `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. /// 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.

View File

@ -1,10 +1,12 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue;
use std::fmt::{Display, Formatter}; 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. use http::header::{HeaderName, InvalidHeaderValue};
/// See [the description on OWASP](https://owasp.org/www-project-secure-headers/) for more. 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 { pub enum XPermittedCrossDomainPolicies {
None, None,
MasterOnly, MasterOnly,

View File

@ -1,9 +1,10 @@
use crate::IntoHeader;
use http::header::{HeaderName, InvalidHeaderValue}; use http::header::{HeaderName, InvalidHeaderValue};
use http::HeaderValue; use http::HeaderValue;
/// `XXSSProtection` disables browsers' buggy cross-site scripting filter by setting the `X-XSS-Protection` header to `0`. use crate::IntoHeader;
/// 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).
/// `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; pub struct XXSSProtection;
impl Default for XXSSProtection { impl Default for XXSSProtection {

View File

@ -1,308 +1,125 @@
//! # Overview //! # 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. //! You can find a list of all available headers under the [header] module. By default (with
//! Please take a good look at [ContentSecurityPolicy]. Most of the time you will need to adapt this one to your needs. //! [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 //! # Examples
//! //!
//! ``` //! ```
//! use std::collections::HashMap;
//!
//! use tower_helmet::header::{ContentSecurityPolicy, ExpectCt, XFrameOptions}; //! use tower_helmet::header::{ContentSecurityPolicy, ExpectCt, XFrameOptions};
//! use tower_helmet::HelmetLayer; //! use tower_helmet::HelmetLayer;
//! //!
//! // default layer with all security headers active //! // 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(); //! let mut directives = HashMap::new();
//! directives.insert("default-src", vec!["'self'", "https://example.com"]); //! directives.insert("default-src", vec!["'self'", "https://example.com"]);
//! directives.insert("img-src", vec!["'self'", "data:", "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 { //! let csp = ContentSecurityPolicy {
//! directives, //! directives,
//! ..Default::default() //! ..Default::default()
//! }; //! };
//! //!
//! let layer = HelmetLayer::default() //! let layer = HelmetLayer::with_defaults().enable(csp);
//! .disable_strict_transport_security()
//! .disable_cross_origin_embedder_policy()
//! .content_security_policy(csp);
//! //!
//! // completely blank layer, selectively enable and add headers //! // completely blank layer, selectively enable and add headers
//! let layer = HelmetLayer::new() //! let layer = HelmetLayer::blank()
//! .x_frame_options(XFrameOptions::SameOrigin) //! .enable(XFrameOptions::SameOrigin)
//! .expect_ct(ExpectCt::default()); //! .enable(ExpectCt::default());
//! ``` //! ```
pub mod header; 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::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; 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_layer::Layer;
use tower_service::Service; use tower_service::Service;
use header::{ use crate::header::{
ContentSecurityPolicy, CrossOriginEmbedderPolicy, CrossOriginOpenerPolicy, ContentSecurityPolicy, CrossOriginEmbedderPolicy, CrossOriginOpenerPolicy,
CrossOriginResourcePolicy, ExpectCt, OriginAgentCluster, ReferrerPolicy, CrossOriginResourcePolicy, ExpectCt, OriginAgentCluster, ReferrerPolicy,
StrictTransportSecurity, XContentTypeOptions, XDnsPrefetchControl, XDownloadOptions, StrictTransportSecurity, XContentTypeOptions, XDnsPrefetchControl, XDownloadOptions,
XFrameOptions, XPermittedCrossDomainPolicies, XXSSProtection, XFrameOptions, XPermittedCrossDomainPolicies, XXSSProtection,
}; };
trait IntoHeader { pub trait IntoHeader {
fn header_name(&self) -> HeaderName; fn header_name(&self) -> HeaderName;
fn header_value(&self) -> Result<HeaderValue, InvalidHeaderValue>; fn header_value(&self) -> Result<HeaderValue, InvalidHeaderValue>;
} }
/// HelmetLayer /// HelmetLayer
pub struct HelmetLayer<'a> { pub struct HelmetLayer {
content_security_policy: Option<ContentSecurityPolicy<'a>>, headers: HeaderMap,
cross_origin_embedder_policy: Option<CrossOriginEmbedderPolicy>,
cross_origin_opener_policy: Option<CrossOriginOpenerPolicy>,
cross_origin_resource_policy: Option<CrossOriginResourcePolicy>,
expect_ct: Option<ExpectCt>,
origin_agent_cluster: Option<OriginAgentCluster>,
referrer_policy: Option<ReferrerPolicy>,
strict_transport_security: Option<StrictTransportSecurity>,
x_content_type_options: Option<XContentTypeOptions>,
x_dns_prefetch_control: Option<XDnsPrefetchControl>,
x_download_options: Option<XDownloadOptions>,
x_frame_options: Option<XFrameOptions>,
x_permitted_cross_domain_policies: Option<XPermittedCrossDomainPolicies>,
x_xss_protection: Option<XXSSProtection>,
} }
impl<'a> Default for HelmetLayer<'a> { impl HelmetLayer {
fn default() -> Self { /// Helmet without any headers added in by default. See [`enable`] for enabling headers.
HelmetLayer { pub fn blank() -> Self {
content_security_policy: Some(ContentSecurityPolicy::default()), Self {
cross_origin_embedder_policy: Some(CrossOriginEmbedderPolicy::default()), headers: HeaderMap::new(),
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,
} }
} }
pub fn recommended_defaults() -> Self { /// Helmet with most of the headers already added with the base configuration.
HelmetLayer::default() 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 { pub fn enable(&mut self, h: impl IntoHeader) -> &mut Self {
self.content_security_policy = Some(v); self.headers
self .insert(h.header_name(), h.header_value().unwrap());
}
pub fn disable_content_security_policy(&mut self) -> &mut Self {
self.content_security_policy = None;
self self
} }
pub fn cross_origin_embedder_policy(&mut self, v: CrossOriginEmbedderPolicy) -> &mut Self { pub fn remove<K>(&mut self, key: K) -> &mut Self
self.cross_origin_embedder_policy = Some(v); where
self K: AsHeaderName,
} {
pub fn disable_cross_origin_embedder_policy(&mut self) -> &mut Self { self.headers.remove(key);
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;
self self
} }
} }
impl<'a, S> Layer<S> for HelmetLayer<'a> { impl<'a, S> Layer<S> for HelmetLayer {
type Service = HelmetService<S>; type Service = HelmetService<S>;
fn layer(&self, service: S) -> Self::Service { 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 { HelmetService {
inner: service, inner: service,
headers, headers: self.headers.clone(),
} }
} }
} }
@ -355,7 +172,7 @@ where
let mut res: Response<ResBody> = ready!(this.future.poll(cx)?); let mut res: Response<ResBody> = ready!(this.future.poll(cx)?);
let headers = res.headers_mut(); let headers = res.headers_mut();
for (name, value) in this.headers.iter() { for (name, value) in this.headers {
headers.insert(name, value.clone()); headers.insert(name, value.clone());
} }