Rocket

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 1bb23b81159c4a63409f1eff2f21a10ee9437a60
parent c5167f1150dd602d0f84e695c5d4fe31b5dff5fc
Author: Sergio Benitez <sb@sergio.bz>
Date:   Thu,  8 Nov 2018 09:01:58 -0800

Rename 'space_helmet' to 'helmet'. Rework API.

Diffstat:
MCargo.toml | 1-
Mcontrib/lib/Cargo.toml | 5+----
Acontrib/lib/src/helmet/helmet.rs | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontrib/lib/src/helmet/mod.rs | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontrib/lib/src/helmet/policy.rs | 401+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcontrib/lib/src/lib.rs | 4++--
Dcontrib/lib/src/space_helmet/helmet.rs | 285-------------------------------------------------------------------------------
Dcontrib/lib/src/space_helmet/mod.rs | 88-------------------------------------------------------------------------------
Dcontrib/lib/src/space_helmet/policy.rs | 302------------------------------------------------------------------------------
Acontrib/lib/tests/helmet.rs | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcontrib/lib/tests/space_helmet.rs | 99-------------------------------------------------------------------------------
Dexamples/space_helmet/Cargo.toml | 18------------------
Dexamples/space_helmet/Rocket.toml | 4----
Dexamples/space_helmet/private/cert.pem | 30------------------------------
Dexamples/space_helmet/private/key.pem | 51---------------------------------------------------
Dexamples/space_helmet/src/hello.rs | 33---------------------------------
Mscripts/test.sh | 2+-
17 files changed, 855 insertions(+), 918 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "core/http/", "contrib/lib", "contrib/codegen", - "examples/space_helmet", "examples/cookies", "examples/errors", "examples/form_validation", diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml @@ -21,7 +21,7 @@ json = ["serde", "serde_json"] msgpack = ["serde", "rmp-serde"] tera_templates = ["tera", "templates"] handlebars_templates = ["handlebars", "templates"] -space_helmet = ["time"] +helmet = ["time"] serve = [] # The barage of user-facing database features. @@ -73,8 +73,5 @@ time = { version = "0.1.40", optional = true } [target.'cfg(debug_assertions)'.dependencies] notify = { version = "^4.0" } -[dev-dependencies] -rocket_codegen = { version = "0.4.0-dev", path = "../../core/codegen" } - [package.metadata.docs.rs] all-features = true diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs @@ -0,0 +1,212 @@ +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; + +use rocket::http::uncased::UncasedStr; +use rocket::fairing::{Fairing, Info, Kind}; +use rocket::{Request, Response, Rocket}; + +use helmet::*; + +/// A [`Fairing`](../../rocket/fairing/trait.Fairing.html) that adds HTTP +/// headers to outgoing responses that control security features on the browser. +/// +/// # Usage +/// +/// To use `SpaceHelmet`, first construct an instance of it. To use the default +/// set of headers, construct with [`SpaceHelmet::default()`](#method.default). +/// For an instance with no preset headers, use [`SpaceHelmet::new()`]. To +/// enable an additional header, use [`enable()`](SpaceHelmet::enable()), and to +/// disable a header, use [`disable()`](SpaceHelmet::disable()): +/// +/// ```rust +/// use rocket_contrib::helmet::SpaceHelmet; +/// use rocket_contrib::helmet::{XssFilter, ExpectCt}; +/// +/// // A `SpaceHelmet` with the default headers: +/// let helmet = SpaceHelmet::default(); +/// +/// // A `SpaceHelmet` with the default headers minus `XssFilter`: +/// let helmet = SpaceHelmet::default().disable::<XssFilter>(); +/// +/// // A `SpaceHelmet` with the default headers plus `ExpectCt`. +/// let helmet = SpaceHelmet::default().enable(ExpectCt::default()); +/// +/// // A `SpaceHelmet` with only `XssFilter` and `ExpectCt`. +/// let helmet = SpaceHelmet::default() +/// .enable(XssFilter::default()) +/// .enable(ExpectCt::default()); +/// ``` +/// +/// Then, attach the instance of `SpaceHelmet` to your application's instance of +/// `Rocket`: +/// +/// ```rust +/// # extern crate rocket; +/// # extern crate rocket_contrib; +/// # use rocket_contrib::helmet::SpaceHelmet; +/// # let helmet = SpaceHelmet::default(); +/// rocket::ignite() +/// // ... +/// .attach(helmet) +/// # ; +/// ``` +/// +/// The fairing will inject all enabled headers into all outgoing responses +/// _unless_ the response already contains a header with the same name. If it +/// does contain the header, a warning is emitted, and the header is not +/// overwritten. +/// +/// # TLS and HSTS +/// +/// If TLS is configured and enabled when the application is launched in a +/// non-development environment (e.g., staging or production), HSTS is +/// automatically enabled with its default policy and a warning is issued. +/// +/// To get rid of this warning, explicitly [`enable()`](SpaceHelmet::enable()) +/// an [`Hsts`] policy. +pub struct SpaceHelmet { + policies: HashMap<&'static UncasedStr, Box<dyn SubPolicy>>, + force_hsts: AtomicBool, +} + +impl Default for SpaceHelmet { + /// Returns a new `SpaceHelmet` instance. See the [table] for a description + /// of the policies used by default. + /// + /// [table]: ./#supported-headers + /// + /// # Example + /// + /// ```rust + /// # extern crate rocket; + /// # extern crate rocket_contrib; + /// use rocket_contrib::helmet::SpaceHelmet; + /// + /// let helmet = SpaceHelmet::default(); + /// ``` + fn default() -> Self { + SpaceHelmet::new() + .enable(NoSniff::default()) + .enable(Frame::default()) + .enable(XssFilter::default()) + } +} + +impl SpaceHelmet { + /// Returns an instance of `SpaceHelmet` with no headers enabled. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// + /// let helmet = SpaceHelmet::new(); + /// ``` + pub fn new() -> Self { + SpaceHelmet { + policies: HashMap::new(), + force_hsts: AtomicBool::new(false), + } + } + + /// Enables the policy header `policy`. + /// + /// If the poliicy was previously enabled, the configuration is replaced + /// with that of `policy`. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// use rocket_contrib::helmet::NoSniff; + /// + /// let helmet = SpaceHelmet::new().enable(NoSniff::default()); + /// ``` + pub fn enable<P: Policy>(mut self, policy: P) -> Self { + self.policies.insert(P::NAME.into(), Box::new(policy)); + self + } + + /// Disables the policy header `policy`. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// use rocket_contrib::helmet::NoSniff; + /// + /// let helmet = SpaceHelmet::default().disable::<NoSniff>(); + /// ``` + pub fn disable<P: Policy>(mut self) -> Self { + self.policies.remove(UncasedStr::new(P::NAME)); + self + } + + /// Returns `true` if the policy `P` is enabled. + /// + /// # Example + /// + /// ```rust + /// use rocket_contrib::helmet::SpaceHelmet; + /// use rocket_contrib::helmet::{XssFilter, NoSniff, Frame}; + /// use rocket_contrib::helmet::{Hsts, ExpectCt, Referrer}; + /// + /// let helmet = SpaceHelmet::default(); + /// + /// assert!(helmet.is_enabled::<XssFilter>()); + /// assert!(helmet.is_enabled::<NoSniff>()); + /// assert!(helmet.is_enabled::<Frame>()); + /// + /// assert!(!helmet.is_enabled::<Hsts>()); + /// assert!(!helmet.is_enabled::<ExpectCt>()); + /// assert!(!helmet.is_enabled::<Referrer>()); + /// ``` + pub fn is_enabled<P: Policy>(&self) -> bool { + self.policies.contains_key(UncasedStr::new(P::NAME)) + } + + /// Sets all of the headers in `self.policies` in `response` as long as the + /// header is not already in the response. + fn apply(&self, response: &mut Response) { + for policy in self.policies.values() { + let name = policy.name(); + if response.headers().contains(name.as_str()) { + warn!("Space Helmet: response contains a '{}' header.", name); + warn_!("Refusing to overwrite existing header."); + continue + } + + // FIXME: Cache the rendered header. + response.set_header(policy.header()); + } + + if !self.force_hsts.load(Ordering::Relaxed) { + response.set_header(Policy::header(&Hsts::default())); + } + } +} + +impl Fairing for SpaceHelmet { + fn info(&self) -> Info { + Info { + name: "Space Helmet", + kind: Kind::Response | Kind::Launch, + } + } + + fn on_response(&self, _request: &Request, response: &mut Response) { + self.apply(response); + } + + fn on_launch(&self, rocket: &Rocket) { + if rocket.config().tls_enabled() + && !rocket.config().environment.is_dev() + && !self.is_enabled::<Hsts>() + { + warn_!("Space Helmet: deploying with TLS without enabling HSTS."); + warn_!("Enabling default HSTS policy."); + info_!("To disable this warning, configure an HSTS policy."); + self.force_hsts.store(true, Ordering::Relaxed); + } + } +} diff --git a/contrib/lib/src/helmet/mod.rs b/contrib/lib/src/helmet/mod.rs @@ -0,0 +1,100 @@ +//! Security and privacy headers for all outgoing responses. +//! +//! [`SpaceHelmet`] provides a typed interface for HTTP security headers. It +//! takes some inspiration from [helmetjs], a similar piece of middleware for +//! [express]. +//! +//! [fairing]: https://rocket.rs/v0.4/guide/fairings/ +//! [helmetjs]: https://helmetjs.github.io/ +//! [express]: https://expressjs.com +//! [`SpaceHelmet`]: helmet::SpaceHelmet +//! +//! # Supported Headers +//! +//! | HTTP Header | Description | Policy | Default? | +//! | --------------------------- | -------------------------------------- | ------------ | -------- | +//! | [X-XSS-Protection] | Prevents some reflected XSS attacks. | [`XssFilter`] | ✔ | +//! | [X-Content-Type-Options] | Prevents client sniffing of MIME type. | [`NoSniff`] | ✔ | +//! | [X-Frame-Options] | Prevents [clickjacking]. | [`Frame`] | ✔ | +//! | [Strict-Transport-Security] | Enforces strict use of HTTPS. | [`Hsts`] | ? | +//! | [Expect-CT] | Enables certificate transparency. | [`ExpectCt`] | ✗ | +//! | [Referrer-Policy] | Enables referrer policy. | [`Referrer`] | ✗ | +//! +//! <small>? If TLS is enabled when the application is launched, in a +//! non-development environment (e.g., staging or production), HSTS is +//! automatically enabled with its default policy and a warning is +//! issued.</small> +//! +//! [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection +//! [X-Content-Type-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options +//! [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options +//! [Strict-Transport-Security]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +//! [Expect-CT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT +//! [Referrer-Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy +//! [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking +//! +//! [`XssFilter`]: helmet::XssFilter +//! [`NoSniff`]: helmet::NoSniff +//! [`Frame`]: helmet::Frame +//! [`Hsts`]: helmet::Hsts +//! [`ExpectCt`]: helmet::ExpectCt +//! [`Referrer`]: helmet::Referrer +//! +//! # Usage +//! +//! To apply default headers, simply attach an instance of [`SpaceHelmet`] +//! before launching: +//! +//! ```rust +//! # extern crate rocket; +//! # extern crate rocket_contrib; +//! use rocket_contrib::helmet::SpaceHelmet; +//! +//! let rocket = rocket::ignite().attach(SpaceHelmet::default()); +//! ``` +//! +//! Each header can be configured individually. To enable a particular header, +//! call the chainable [`enable()`](helmet::SpaceHelmet::enable()) method +//! on an instance of `SpaceHelmet`, passing in the configured policy type. +//! Similarly, to disable a header, call the chainable +//! [`disable()`](helmet::SpaceHelmet::disable()) method on an instance of +//! `SpaceHelmet`: +//! +//! ```rust +//! # extern crate rocket; +//! # extern crate rocket_contrib; +//! use rocket::http::uri::Uri; +//! use rocket_contrib::helmet::{SpaceHelmet, Frame, XssFilter, Hsts, NoSniff}; +//! +//! let site_uri = Uri::parse("https://mysite.example.com").unwrap(); +//! let report_uri = Uri::parse("https://report.example.com").unwrap(); +//! let helmet = SpaceHelmet::default() +//! .enable(Hsts::default()) +//! .enable(Frame::AllowFrom(site_uri)) +//! .enable(XssFilter::EnableReport(report_uri)) +//! .disable::<NoSniff>(); +//! ``` +//! +//! # FAQ +//! +//! * **Which policies should I choose?** +//! +//! See the links in the table above for individual header documentation. The +//! [helmetjs] docs are also a good resource, and [OWASP] has a collection of +//! references on these headers. +//! +//! * **Do I need any headers beyond what `SpaceHelmet` enables by default?** +//! +//! Maybe! The other headers can protect against many important +//! vulnerabilities. Please consult their documentation and other resources to +//! determine if they are needed for your project. +//! +//! [OWASP]: https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers + +extern crate time; + +mod helmet; +mod policy; + +pub use self::helmet::SpaceHelmet; +pub use self::policy::*; diff --git a/contrib/lib/src/helmet/policy.rs b/contrib/lib/src/helmet/policy.rs @@ -0,0 +1,401 @@ +//! Module containing the [`Policy`] trait and types that implement it. + +use std::borrow::Cow; + +use rocket::http::{Header, uri::Uri, uncased::UncasedStr}; + +use helmet::time::Duration; + +/// Trait implemented by security and privacy policy headers. +/// +/// Types that implement this trait can be [`enable()`]d and [`disable()`]d on +/// instances of [`SpaceHelmet`]. +/// +/// [`SpaceHelmet`]: ::helmet::SpaceHelmet +/// [`enable()`]: ::helmet::SpaceHelmet::enable() +/// [`disable()`]: ::helmet::SpaceHelmet::disable() +pub trait Policy: Default + Send + Sync + 'static { + /// The actual name of the HTTP header. + /// + /// This name must uniquely identify the header as it is used to determine + /// whether two implementations of `Policy` are for the same header. Use the + /// real HTTP header's name. + /// + /// # Example + /// + /// ```rust + /// # extern crate rocket; + /// # extern crate rocket_contrib; + /// # use rocket::http::Header; + /// use rocket_contrib::helmet::Policy; + /// + /// #[derive(Default)] + /// struct MyPolicy; + /// + /// impl Policy for MyPolicy { + /// const NAME: &'static str = "X-My-Policy"; + /// # fn header(&self) -> Header<'static> { unimplemented!() } + /// } + /// ``` + const NAME: &'static str; + + /// Returns the [`Header`](../../rocket/http/struct.Header.html) to attach + /// to all outgoing responses. + /// + /// # Example + /// + /// ```rust + /// # extern crate rocket; + /// # extern crate rocket_contrib; + /// use rocket::http::Header; + /// use rocket_contrib::helmet::Policy; + /// + /// #[derive(Default)] + /// struct MyPolicy; + /// + /// impl Policy for MyPolicy { + /// # const NAME: &'static str = "X-My-Policy"; + /// fn header(&self) -> Header<'static> { + /// Header::new(Self::NAME, "value-to-enable") + /// } + /// } + /// ``` + fn header(&self) -> Header<'static>; +} + +crate trait SubPolicy: Send + Sync { + fn name(&self) -> &'static UncasedStr; + fn header(&self) -> Header<'static>; +} + +impl<P: Policy> SubPolicy for P { + fn name(&self) -> &'static UncasedStr { + P::NAME.into() + } + + fn header(&self) -> Header<'static> { + Policy::header(self) + } +} + +macro_rules! impl_policy { + ($T:ty, $name:expr) => ( + impl Policy for $T { + const NAME: &'static str = $name; + + fn header(&self) -> Header<'static> { + self.into() + } + } + ) +} + +impl_policy!(XssFilter, "X-XSS-Protection"); +impl_policy!(NoSniff, "X-Content-Type-Options"); +impl_policy!(Frame, "X-Frame-Options"); +impl_policy!(Hsts, "Strict-Transport-Security"); +impl_policy!(ExpectCt, "Expect-CT"); +impl_policy!(Referrer, "Referrer-Policy"); + +/// The [Referrer-Policy] header: controls the value set by the browser for the +/// [Referer] header. +/// +/// Tells the browser if it should send all or part of URL of the current page +/// to the next site the user navigates to via the [Referer] header. This can be +/// important for security as the URL itself might expose sensitive data, such +/// as a hidden file path or personal identifier. +/// +/// [Referrer-Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy +/// [Referer]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer +pub enum Referrer { + /// Omits the `Referer` header (_SpaceHelmet default_). + NoReferrer, + + /// Omits the `Referer` header on connection downgrade i.e. following HTTP + /// link from HTTPS site (_Browser default_). + NoReferrerWhenDowngrade, + + /// Only send the origin of part of the URL, e.g. the origin of + /// https://foo.com/bob.html is https://foo.com + Origin, + + /// Send full URL for same-origin requests, only send origin part when + /// replying to [cross-origin] requests. + /// + /// [cross-origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS + OriginWhenCrossOrigin, + + /// Send full URL for same-origin requests only. + SameOrigin, + + /// Only send origin part of URL, only send if protocol security level + /// remains the same e.g. HTTPS to HTTPS. + StrictOrigin, + + /// Send full URL for same-origin requests. For cross-origin requests, only + /// send origin part of URL if protocl security level remains the same e.g. + /// HTTPS to HTTPS. + StrictOriginWhenCrossOrigin, + + /// Send full URL for same-origin or cross-origin requests. _This will leak + /// the full URL of TLS protected resources to insecure origins. Use with + /// caution._ + UnsafeUrl, + } + +/// Defaults to [`Referrer::NoReferrer`]. Tells the browser to omit the +/// `Referer` header. +impl Default for Referrer { + fn default() -> Referrer { + Referrer::NoReferrer + } +} + +impl<'h, 'a> Into<Header<'h>> for &'a Referrer { + fn into(self) -> Header<'h> { + let policy_string = match self { + Referrer::NoReferrer => "no-referrer", + Referrer::NoReferrerWhenDowngrade => "no-referrer-when-downgrade", + Referrer::Origin => "origin", + Referrer::OriginWhenCrossOrigin => "origin-when-cross-origin", + Referrer::SameOrigin => "same-origin", + Referrer::StrictOrigin => "strict-origin", + Referrer::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin", + Referrer::UnsafeUrl => "unsafe-url", + }; + + Header::new(Referrer::NAME, policy_string) + } +} + +/// The [Expect-CT] header: enables [Certificate Transparency] to detect and +/// prevent misuse of TLS certificates. +/// +/// [Certificate Transparency] solves a variety of problems with public TLS/SSL +/// certificate management and is valuable measure for all public applications. +/// If you're just [getting started] with certificate transparency, ensure that +/// your [site is in compliance][getting started] before you enable enforcement +/// with [`ExpectCt::Enforce`] or [`ExpectCt::ReportAndEnforce`]. Failure to do +/// so will result in the browser refusing to communicate with your application. +/// _You have been warned_. +/// +/// [Expect-CT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT +/// [Certificate Transparency]: http://www.certificate-transparency.org/what-is-ct +/// [getting started]: http://www.certificate-transparency.org/getting-started +pub enum ExpectCt { + /// Enforce certificate compliance for the next [`Duration`]. Ensure that + /// your certificates are in compliance before turning on enforcement. + /// (_SpaceHelmet_ default). + Enforce(Duration), + + /// Report to `Uri`, but do not enforce, compliance violations for the next + /// [`Duration`]. Doesn't provide any protection but is a good way make sure + /// things are working correctly before turning on enforcement in + /// production. + Report(Duration, Uri<'static>), + + /// Enforce compliance and report violations to `Uri` for the next + /// [`Duration`]. + ReportAndEnforce(Duration, Uri<'static>), +} + +/// Defaults to [`ExpectCt::Enforce(Duration::days(30))`], enforce CT +/// compliance, see [draft] standard for more. +/// +/// [draft]: https://tools.ietf.org/html/draft-ietf-httpbis-expect-ct-03#page-15 +impl Default for ExpectCt { + fn default() -> ExpectCt { + ExpectCt::Enforce(Duration::days(30)) + } +} + +impl<'a> Into<Header<'static>> for &'a ExpectCt { + fn into(self) -> Header<'static> { + let policy_string = match self { + ExpectCt::Enforce(age) => format!("max-age={}, enforce", age.num_seconds()), + ExpectCt::Report(age, uri) => { + format!(r#"max-age={}, report-uri="{}""#, age.num_seconds(), uri) + } + ExpectCt::ReportAndEnforce(age, uri) => { + format!("max-age={}, enforce, report-uri=\"{}\"", age.num_seconds(), uri) + } + }; + + Header::new(ExpectCt::NAME, policy_string) + } +} + +/// The [X-Content-Type-Options] header: turns off [mime sniffing] which can +/// prevent certain [attacks]. +/// +/// [mime sniffing]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing +/// [X-Content-Type-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options +/// [attacks]: https://helmetjs.github.io/docs/dont-sniff-mimetype/ +pub enum NoSniff { + /// Turns off mime sniffing. + Enable, +} + +/// Defaults to [`NoSniff::Enable`], turns off mime sniffing. +impl Default for NoSniff { + fn default() -> NoSniff { + NoSniff::Enable + } +} + +impl<'h, 'a> Into<Header<'h>> for &'a NoSniff { + fn into(self) -> Header<'h> { + Header::new(NoSniff::NAME, "nosniff") + } +} + +/// The HTTP [Strict-Transport-Security] (HSTS) header: enforces strict HTTPS +/// usage. +/// +/// HSTS tells the browser that the site should only be accessed using HTTPS +/// instead of HTTP. HSTS prevents a variety of downgrading attacks and should +/// always be used when TLS is enabled. `SpaceHelmet` will turn HSTS on and +/// issue a warning if you enable TLS without enabling HSTS when the application +/// is run in the staging or production environments. +/// +/// While HSTS is important for HTTPS security, incorrectly configured HSTS can +/// lead to problems as you are disallowing access to non-HTTPS enabled parts of +/// your site. [Yelp engineering] has good discussion of potential challenges +/// that can arise and how to roll this out in a large scale setting. So, if +/// you use TLS, use HSTS, but roll it out with care. +/// +/// [TLS]: https://rocket.rs/guide/configuration/#configuring-tls +/// [Strict-Transport-Security]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +/// [default policy]: /rocket_contrib/helmet/enum.Hsts.html#impl-Default +/// [Yelp engineering]: https://engineeringblog.yelp.com/2017/09/the-road-to-hsts.html +/// [Staging]: /rocket/config/enum.Environment.html#variant.Staging +/// [Production]: /rocket/config/enum.Environment.html#variant.Production +pub enum Hsts { + /// Browser should only permit this site to be accesses by HTTPS for the + /// next [`Duration`]. + Enable(Duration), + + /// Like [`Hsts::Enable`], but also apply to all of the site's subdomains. + IncludeSubDomains(Duration), + + /// Google maintains an [HSTS preload service] that can be used to prevent + /// the browser from ever connecting to your site over an insecure + /// connection. Read more [here]. Don't enable this before you have + /// registered your site. + /// + /// [HSTS preload service]: https://hstspreload.org/ + /// [here]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#Preloading_Strict_Transport_Security + Preload(Duration), +} + +/// Defaults to `Hsts::Enable(Duration::weeks(52))`. +impl Default for Hsts { + fn default() -> Hsts { + Hsts::Enable(Duration::weeks(52)) + } +} + +impl<'a> Into<Header<'static>> for &'a Hsts { + fn into(self) -> Header<'static> { + let policy_string = match self { + Hsts::Enable(age) => format!("max-age={}", age.num_seconds()), + Hsts::IncludeSubDomains(age) => { + format!("max-age={}; includeSubDomains", age.num_seconds()) + } + Hsts::Preload(age) => format!("max-age={}; preload", age.num_seconds()), + }; + + Header::new(Hsts::NAME, policy_string) + } +} + +/// The [X-Frame-Options] header: helps prevent [clickjacking] attacks. +/// +/// Controls whether the browser should allow the page to render in a `<frame>`, +/// [`<iframe>`][iframe] or `<object>`. This can be used to prevent +/// [clickjacking] attacks. +/// +/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options +/// [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking +/// [owasp-clickjacking]: https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet +/// [iframe]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe +pub enum Frame { + /// Page cannot be displayed in a frame. + Deny, + + /// Page can only be displayed in a frame if the page trying to render it is + /// in the same origin. Interpretation of same-origin is [browser + /// dependent][X-Frame-Options]. + /// + /// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options + SameOrigin, + + /// Page can only be displayed in a frame if the page trying to render it is + /// in the origin for `Uri`. Interpretation of origin is [browser + /// dependent][X-Frame-Options]. + /// + /// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options + AllowFrom(Uri<'static>), +} + +/// Defaults to [`Frame::SameOrigin`]. +impl Default for Frame { + fn default() -> Frame { + Frame::SameOrigin + } +} + +impl<'a> Into<Header<'static>> for &'a Frame { + fn into(self) -> Header<'static> { + let policy_string: Cow<'static, str> = match self { + Frame::Deny => "DENY".into(), + Frame::SameOrigin => "SAMEORIGIN".into(), + Frame::AllowFrom(uri) => format!("ALLOW-FROM {}", uri).into(), + }; + + Header::new(Frame::NAME, policy_string) + } +} + +/// The [X-XSS-Protection] header: filters some forms of reflected [XSS] +/// attacks. +/// +/// [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection +/// [XSS]: https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting +pub enum XssFilter { + /// Disables XSS filtering. + Disable, + + /// Enables XSS filtering. If XSS is detected, the browser will sanitize + /// before rendering the page (_SpaceHelmet default_). + Enable, + + /// Enables XSS filtering. If XSS is detected, the browser will not + /// render the page. + EnableBlock, + + /// Enables XSS filtering. If XSS is detected, the browser will sanitize and + /// render the page and report the violation to the given `Uri`. (_Chromium + /// only_) + EnableReport(Uri<'static>), +} + +/// Defaults to [`XssFilter::Enable`]. +impl Default for XssFilter { + fn default() -> XssFilter { + XssFilter::Enable + } +} + +impl<'a> Into<Header<'static>> for &'a XssFilter { + fn into(self) -> Header<'static> { + let policy_string: Cow<'static, str> = match self { + XssFilter::Disable => "0".into(), + XssFilter::Enable => "1".into(), + XssFilter::EnableBlock => "1; mode=block".into(), + XssFilter::EnableReport(u) => format!("{}{}", "1; report=", u).into(), + }; + + Header::new(XssFilter::NAME, policy_string) + } +} diff --git a/contrib/lib/src/lib.rs b/contrib/lib/src/lib.rs @@ -24,7 +24,7 @@ //! * [tera_templates](templates) - Tera Templating //! * [uuid](uuid) - UUID (de)serialization //! * [${database}_pool](databases) - Database Configuration and Pooling -//! * [space_helmet](space_helmet) +//! * [helmet](helmet) - Fairing for Security and Privacy Headers //! //! The recommend way to include features from this crate via Cargo in your //! project is by adding a `[dependencies.rocket_contrib]` section to your @@ -50,7 +50,7 @@ #[cfg(feature="templates")] pub mod templates; #[cfg(feature="uuid")] pub mod uuid; #[cfg(feature="databases")] pub mod databases; -#[cfg(feature = "space_helmet")] pub mod space_helmet; +#[cfg(feature = "helmet")] pub mod helmet; #[cfg(feature="databases")] extern crate rocket_contrib_codegen; #[cfg(feature="databases")] #[doc(hidden)] pub use rocket_contrib_codegen::*; diff --git a/contrib/lib/src/space_helmet/helmet.rs b/contrib/lib/src/space_helmet/helmet.rs @@ -1,285 +0,0 @@ -use std::sync::atomic::{AtomicBool, Ordering}; - -use rocket::fairing::{Fairing, Info, Kind}; -use rocket::http::Header; -use rocket::{Request, Response, Rocket}; - -use super::policy::*; - -/// A [`Fairing`](/rocket/fairing/trait.Fairing.html) -/// that adds HTTP headers to outgoing responses that control security features -/// on the browser. -/// -/// # Usage -/// -/// `SpaceHelmet` can be used in several ways. -/// -/// To use it with its [defaults](#method.default), create a new instance and -/// attach it to [Rocket](/rocket/struct.Rocket.html) -/// as shown. -/// -/// ```rust -/// # extern crate rocket; -/// # extern crate rocket_contrib; -/// use rocket_contrib::space_helmet::SpaceHelmet; -/// -/// let rocket = rocket::ignite().attach(SpaceHelmet::default()); -/// ``` -/// -/// To enable an additional header, call the method for that header, with the -/// policy for that header, before attach e.g. -/// -/// ```rust -/// # extern crate rocket; -/// # extern crate rocket_contrib; -/// use rocket_contrib::space_helmet::{SpaceHelmet, ReferrerPolicy}; -/// -/// let helmet = SpaceHelmet::default().referrer_policy(ReferrerPolicy::NoReferrer); -/// let rocket = rocket::ignite().attach(helmet); -/// ``` -/// -/// To disable a header, call the method for that header with `None` as -/// the policy. -/// -/// ```rust -/// # extern crate rocket; -/// # extern crate rocket_contrib; -/// use rocket_contrib::space_helmet::SpaceHelmet; -/// -/// let helmet = SpaceHelmet::default().no_sniff(None); -/// let rocket = rocket::ignite().attach(helmet); -/// ``` -/// -/// `SpaceHelmet` supports the builder pattern to configure multiple policies -/// -/// ```rust -/// # extern crate rocket; -/// # extern crate rocket_contrib; -/// use rocket_contrib::space_helmet::{HstsPolicy, ExpectCtPolicy, ReferrerPolicy, SpaceHelmet}; -/// -/// let helmet = SpaceHelmet::default() -/// .hsts(HstsPolicy::default()) -/// .expect_ct(ExpectCtPolicy::default()) -/// .referrer_policy(ReferrerPolicy::default()); -/// -/// let rocket = rocket::ignite().attach(helmet); -/// ``` -/// -/// # TLS and HSTS -/// -/// If TLS is enabled when a [Rocket](rocket/struct.Rocket.html) -/// is [launched](/rocket/fairing/trait.Fairing.html#method.on_launch) -/// in a non-development environment e.g. [Staging](rocket/struct.Config.html#method.staging) -/// or [Production](/rocket/struct.Config.html#method.production) -/// `SpaceHelmet` enables hsts with its default policy and issue a -/// warning. -/// -/// To get rid of this warning, set an [hsts](#method.hsts) policy if you are using tls. -pub struct SpaceHelmet<'a> { - expect_ct_policy: Option<ExpectCtPolicy<'a>>, - no_sniff_policy: Option<NoSniffPolicy>, - xss_protect_policy: Option<XssPolicy<'a>>, - frameguard_policy: Option<FramePolicy<'a>>, - hsts_policy: Option<HstsPolicy>, - force_hsts_policy: Option<HstsPolicy>, - force_hsts: AtomicBool, - referrer_policy: Option<ReferrerPolicy>, -} - -// helper for Helmet.apply -macro_rules! try_apply_header { - ($self:ident, $response:ident, $policy_name:ident) => { - if let Some(ref policy) = $self.$policy_name { - if $response.set_header(policy) { - warn_!( - "(Space Helmet): set_header failed, found existing header \"{}\"", - Header::from(policy).name - ); - } - } - }; -} - -impl<'a> Default for SpaceHelmet<'a> { - /// Returns a new `SpaceHelmet` instance. See [table](/rocket_contrib/space_helmet/index.html#what-it-supports) for - /// a description of what policies are used by default. - /// - /// # Example - /// - /// ```rust - /// # extern crate rocket; - /// # extern crate rocket_contrib; - /// use rocket_contrib::space_helmet::SpaceHelmet; - /// - /// let helmet = SpaceHelmet::default(); - /// ``` - fn default() -> Self { - Self { - expect_ct_policy: None, - no_sniff_policy: Some(NoSniffPolicy::default()), - frameguard_policy: Some(FramePolicy::default()), - xss_protect_policy: Some(XssPolicy::default()), - hsts_policy: None, - force_hsts_policy: Some(HstsPolicy::default()), - force_hsts: AtomicBool::new(false), - referrer_policy: None, - } - } -} - -impl<'a> SpaceHelmet<'a> { - /// Same as [`SpaceHelmet::default()`]. - pub fn new() -> Self { - SpaceHelmet::default() - } - - /// Sets the [X-XSS-Protection]( - /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection) - /// header to the given `policy` or disables it if `policy == None`. - /// # Example - /// - /// ```rust - /// # extern crate rocket; - /// # extern crate rocket_contrib; - /// use rocket::http::uri::Uri; - /// use rocket_contrib::space_helmet::{SpaceHelmet, XssPolicy}; - /// - /// let report_uri = Uri::parse("https://www.google.com").unwrap(); - /// let helmet = SpaceHelmet::new().xss_protect(XssPolicy::EnableReport(report_uri)); - /// ``` - pub fn xss_protect<T: Into<Option<XssPolicy<'a>>>>(mut self, policy: T) -> SpaceHelmet<'a> { - self.xss_protect_policy = policy.into(); - self - } - - /// Sets the [X-Content-Type-Options]( - /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) - /// header to `policy` or disables it if `policy == None`. - /// # Example - /// - /// ```rust - /// - /// use rocket_contrib::space_helmet::{SpaceHelmet, NoSniffPolicy}; - /// - /// let helmet = SpaceHelmet::new().no_sniff(NoSniffPolicy::Enable); - /// ``` - pub fn no_sniff<T: Into<Option<NoSniffPolicy>>>(mut self, policy: T) -> SpaceHelmet<'a> { - self.no_sniff_policy = policy.into(); - self - } - - /// Sets the [X-Frame-Options]( - /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) - /// header to `policy`, or disables it if `policy == None`. - /// # Example - /// - /// ```rust - /// # extern crate rocket; - /// # extern crate rocket_contrib; - /// - /// use rocket::http::uri::Uri; - /// use rocket_contrib::space_helmet::{SpaceHelmet, FramePolicy}; - /// - /// let allow_uri = Uri::parse("https://www.google.com").unwrap(); - /// let helmet = SpaceHelmet::new().frameguard(FramePolicy::AllowFrom(allow_uri)); - /// ``` - pub fn frameguard<T: Into<Option<FramePolicy<'a>>>>(mut self, policy: T) -> SpaceHelmet<'a> { - self.frameguard_policy = policy.into(); - self - } - - /// Sets the [Strict-Transport-Security]( - /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) - /// header to `policy`, or disables it if `policy == None`. - /// # Example - /// - /// ```rust - /// use rocket_contrib::space_helmet::{SpaceHelmet, HstsPolicy}; - /// - /// let helmet = SpaceHelmet::new().hsts(HstsPolicy::default()); - /// ``` - pub fn hsts<T: Into<Option<HstsPolicy>>>(mut self, policy: T) -> SpaceHelmet<'a> { - self.hsts_policy = policy.into(); - self - } - - /// Sets the [Expect-CT]( - /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT) - /// header to `policy`, or disables it if `policy == None`. - /// ```rust - /// # extern crate rocket; - /// # extern crate rocket_contrib; - /// # extern crate time; - /// use rocket::http::uri::Uri; - /// use rocket_contrib::space_helmet::{SpaceHelmet, ExpectCtPolicy}; - /// use time::Duration; - /// - /// let report_uri = Uri::parse("https://www.google.com").unwrap(); - /// let helmet = SpaceHelmet::new() - /// .expect_ct(ExpectCtPolicy::ReportAndEnforce( - /// Duration::days(30), report_uri)); - /// ``` - pub fn expect_ct<T: Into<Option<ExpectCtPolicy<'a>>>>(mut self, policy: T) -> SpaceHelmet<'a> { - self.expect_ct_policy = policy.into(); - self - } - - /// Sets the [Referrer-Policy]( - /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) - /// header to `policy`, or disables it if `policy == None`. - /// ```rust - /// # extern crate rocket; - /// # extern crate rocket_contrib; - /// - /// use rocket_contrib::space_helmet::{ReferrerPolicy, SpaceHelmet}; - /// - /// let helmet = SpaceHelmet::new().referrer_policy(ReferrerPolicy::NoReferrer); - /// ``` - pub fn referrer_policy<T: Into<Option<ReferrerPolicy>>>( - mut self, - policy: T, - ) -> SpaceHelmet<'a> { - self.referrer_policy = policy.into(); - self - } - - fn apply(&self, response: &mut Response) { - try_apply_header!(self, response, no_sniff_policy); - try_apply_header!(self, response, xss_protect_policy); - try_apply_header!(self, response, frameguard_policy); - try_apply_header!(self, response, expect_ct_policy); - try_apply_header!(self, response, referrer_policy); - if self.hsts_policy.is_some() { - try_apply_header!(self, response, hsts_policy); - } else { - if self.force_hsts.load(Ordering::Relaxed) { - try_apply_header!(self, response, force_hsts_policy); - } - } - } -} - -impl Fairing for SpaceHelmet<'static> { - fn info(&self) -> Info { - Info { - name: "Rocket SpaceHelmet (HTTP Security Headers)", - kind: Kind::Response | Kind::Launch, - } - } - - fn on_response(&self, _request: &Request, response: &mut Response) { - self.apply(response); - } - - fn on_launch(&self, rocket: &Rocket) { - if rocket.config().tls_enabled() - && !rocket.config().environment.is_dev() - && !self.hsts_policy.is_some() - { - warn_!("(Space Helmet): deploying with TLS without enabling hsts."); - warn_!("Enabling default HSTS policy."); - info_!("To disable this warning, configure an HSTS policy."); - self.force_hsts.store(true, Ordering::Relaxed); - } - } -} diff --git a/contrib/lib/src/space_helmet/mod.rs b/contrib/lib/src/space_helmet/mod.rs @@ -1,88 +0,0 @@ -//! [`SpaceHelmet`] is a [`Fairing`](/rocket/fairing/trait.Fairing.html) that -//! turns on browsers security features by adding HTTP headers to all outgoing -//! responses. -//! -//! It provides a typed interface for http security headers and takes some -//! inspiration from [helmet](https://helmetjs.github.io/), a similar piece -//! of middleware for [express](https://expressjs.com). -//! -//! ### What it supports -//! -//! | HTTP Header | Description | Method | Enabled by Default? | -//! | --------------------------- | -------------------------------------- | ---------------------------------- | ------------------- | -//! | [X-XSS-Protection] | Prevents some reflected XSS attacks. | [`SpaceHelmet::xss_protect()`] | ✔ | -//! | [X-Content-Type-Options] | Prevents client sniffing of MIME type. | [`SpaceHelmet::no_sniff()`] | ✔ | -//! | [X-Frame-Options] | Prevents [clickjacking]. | [`SpaceHelmet::frameguard()`] | ✔ | -//! | [Strict-Transport-Security] | Enforces strict use of HTTPS. | [`SpaceHelmet::hsts()`] | ? | -//! | [Expect-CT] | Enables certificate transparency. | [`SpaceHelmet::expect_ct()`] | ✗ | -//! | [Referrer-Policy] | Enables referrer policy. | [`SpaceHelmet::referrer_policy()`] | ✗ | -//! -//! [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection -//! [X-Content-Type-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options -//! [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options -//! [Strict-Transport-Security]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security -//! [Expect-CT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT -//! [Referrer-Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy -//! [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking -//! _? If tls is enabled when a [Rocket](/rocket/struct.Rocket.html) is -//! [launched()'ed](/rocket/fairing/trait.Fairing.html) -//! in a non-development environment e.g. [Staging](/rocket/struct.Config.html#method.staging) -//! or [Production](/rocket/struct.Config.html#method.production), `SpaceHelmet` enables hsts with its -//! default policy and outputs a warning._ -//! -//! ### Examples -//! -//! To apply the headers that are enabled by default, just create a new [`SpaceHelmet`] instance -//! and attach before launch. -//! -//! ```rust -//! # extern crate rocket; -//! # extern crate rocket_contrib; -//! use rocket_contrib::space_helmet::{SpaceHelmet}; -//! -//! let rocket = rocket::ignite().attach(SpaceHelmet::new()); -//! ``` -//! -//! Each header can be configured individually if desired. To enable a particular -//! header, call the method for the header on the [`SpaceHelmet`] instance. Multiple -//! method calls can be chained in a builder pattern as illustrated below. -//! -//! ```rust -//! # extern crate rocket; -//! # extern crate rocket_contrib; -//! use rocket::http::uri::Uri; -//! -//! // Every header has a corresponding policy type. -//! use rocket_contrib::space_helmet::{SpaceHelmet, FramePolicy, XssPolicy, HstsPolicy}; -//! -//! let site_uri = Uri::parse("https://mysite.example.com").unwrap(); -//! let report_uri = Uri::parse("https://report.example.com").unwrap(); -//! let helmet = SpaceHelmet::new() -//! .hsts(HstsPolicy::default()) // each policy has a default. -//! .no_sniff(None) // setting policy to None disables the header. -//! .frameguard(FramePolicy::AllowFrom(site_uri)) -//! .xss_protect(XssPolicy::EnableReport(report_uri)); -//! ``` -//! -//! #### Still have questions? -//! -//! * _What policy should I choose?_ Check out the links in the table -//! above for individual header documentation. The [helmetjs](https://helmetjs.github.io/) doc's -//! are also a good resource, and owasp has a collection of [references] on these headers. -//! -//! [references]: https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers -//! -//! * _Where I can I read more about using fairings?_ Check out the -//! [fairing](https://rocket.rs/guide/fairings/) section of the rocket guide. -//! -//! * _Do I only need those headers `SpaceHelmet` enables by default?_ Maybe, the other headers -//! can protect against many important vulnerabilities. Please consult their documentation and -//! other resources to find out if they are needed for your project. - -extern crate time; - -mod helmet; -mod policy; - -pub use self::helmet::*; -pub use self::policy::*; diff --git a/contrib/lib/src/space_helmet/policy.rs b/contrib/lib/src/space_helmet/policy.rs @@ -1,302 +0,0 @@ -use std::borrow::Cow; -use rocket::http::uri::Uri; -use rocket::http::Header; - -use super::time::Duration; - -/// The [Referrer-Policy] header tells the browser if should send all or part of URL of the current -/// page to the next site the user navigates to via the [Referer] header. This can be important -/// for security as the URL itself might expose sensitive data, such as a hidden file path or -/// personal identifier. -/// -/// [Referrer-Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy -/// [Referer]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer -pub enum ReferrerPolicy { - /// Omits the `Referer` header (_SpaceHelmet default_). - NoReferrer, - - /// Omits the `Referer` header on connection downgrade i.e. following HTTP link from HTTPS site - /// (_Browser default_). - NoReferrerWhenDowngrade, - - /// Only send the origin of part of the URL, e.g. the origin of https://foo.com/bob.html is - /// https://foo.com - Origin, - - /// Send full URL for same-origin requests, only send origin part when replying - /// to [cross-origin] requests. - /// - /// [cross-origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - OriginWhenCrossOrigin, - - /// Send full URL for same-origin requests only. - SameOrigin, - - /// Only send origin part of URL, only send if protocol security level remains the same e.g. - /// HTTPS to HTTPS. - StrictOrigin, - - /// Send full URL for same-origin requests. For cross-origin requests, only send origin - /// part of URL if protocl security level remains the same e.g. HTTPS to HTTPS. - StrictOriginWhenCrossOrigin, - - /// Send full URL for same-origin or cross-origin requests. _This will leak the full - /// URL of TLS protected resources to insecure origins. Use with caution._ - UnsafeUrl, - } - -/// Defaults to [`ReferrerPolicy::NoReferrer`]. Tells the browser Omit the `Referer` header. -impl Default for ReferrerPolicy { - fn default() -> ReferrerPolicy { - ReferrerPolicy::NoReferrer - } -} - -impl<'a, 'b> From<&'a ReferrerPolicy> for Header<'b> { - fn from(policy: &ReferrerPolicy) -> Header<'b> { - let policy_string = match policy { - ReferrerPolicy::NoReferrer => "no-referrer", - ReferrerPolicy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade", - ReferrerPolicy::Origin => "origin", - ReferrerPolicy::OriginWhenCrossOrigin => "origin-when-cross-origin", - ReferrerPolicy::SameOrigin => "same-origin", - ReferrerPolicy::StrictOrigin => "strict-origin", - ReferrerPolicy::StrictOriginWhenCrossOrigin => { - "strict-origin-when-cross-origin" - } - ReferrerPolicy::UnsafeUrl => "unsafe-url", - }; - - Header::new("Referrer-Policy", policy_string) - } -} - - -/// The [Expect-CT] header tells browser to enable [Certificate Transparency] checking, which -/// can detect and prevent the misuse of the site's certificate. Read all [`ExpectCtPolicy`] -/// documentation before use. -/// -/// [Certificate Transparency] -/// solves a variety of problems with public TLS/SSL certificate management and is valuable -/// measure if your standing up a public website. If your [getting started] with certificate -/// transparency, be sure that your [site is in compliance][getting started] before you turn on -/// enforcement with [`ExpectCtPolicy::Enforce`] or [`ExpectCtPolicy::ReportAndEnforce`] -/// otherwise the browser will stop talking to your site until you bring it into compliance -/// or [`Duration`] time elapses. _You have been warned_. -/// -/// -/// -/// [Expect-CT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT -/// [Certificate Transparency]: http://www.certificate-transparency.org/what-is-ct -/// [getting started]: http://www.certificate-transparency.org/getting-started -pub enum ExpectCtPolicy<'a> { - /// Tells browser to enforce certificate compliance for [`Duration`] seconds. - /// Check if your site is in compliance before turning on enforcement. - /// (_SpaceHelmet_ default). - Enforce(Duration), - - /// Tells browser to report compliance violations certificate transparency for [`Duration`] - /// seconds. Doesn't provide any protection but is a good way make sure - /// things are working correctly before turning on enforcement in production. - Report(Duration, Uri<'a>), - - /// Enforces compliance and supports notification to if there has been a violation for - /// [`Duration`]. - ReportAndEnforce(Duration, Uri<'a>), -} - -/// Defaults to [`ExpectCtPolicy::Enforce(Duration::days(30))`], enforce CT -/// compliance, see [draft] standard for more. -/// -/// [draft]: https://tools.ietf.org/html/draft-ietf-httpbis-expect-ct-03#page-15 -impl<'a> Default for ExpectCtPolicy<'a> { - fn default() -> ExpectCtPolicy<'a> { - ExpectCtPolicy::Enforce(Duration::days(30)) - } -} - -impl<'a, 'b> From<&'a ExpectCtPolicy<'a>> for Header<'b> { - fn from(policy: &ExpectCtPolicy<'a>) -> Header<'b> { - let policy_string = match policy { - ExpectCtPolicy::Enforce(max_age) => format!("max-age={}, enforce", max_age.num_seconds()), - ExpectCtPolicy::Report(max_age, url) => { - format!("max-age={}, report-uri=\"{}\"", max_age.num_seconds(), url) - } - ExpectCtPolicy::ReportAndEnforce(max_age, url) => { - format!("max-age={}, enforce, report-uri=\"{}\"", max_age.num_seconds(), url) - } - }; - - Header::new("Expect-CT", policy_string) - } -} - -/// The [X-Content-Type-Options] header can tell the browser to turn off [mime sniffing]( -/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#MIME_sniffing) which -/// can prevent certain [attacks]. -/// -/// [X-Content-Type-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options -/// [attacks]: https://helmetjs.github.io/docs/dont-sniff-mimetype/ -pub enum NoSniffPolicy { - - ///Turns off mime sniffing. - Enable, -} - -/// Defaults to [`NoSniffPolicy::Enable`], turns off mime sniffing. -impl Default for NoSniffPolicy { - fn default() -> NoSniffPolicy { - NoSniffPolicy::Enable - } -} - -impl<'a, 'b> From<&'a NoSniffPolicy> for Header<'b> { - fn from(_policy: &NoSniffPolicy) -> Header<'b> { - Header::new("X-Content-Type-Options", "nosniff") - } -} - -/// The HTTP [Strict-Transport-Security] (HSTS) header tells the browser that the site should only -/// be accessed using HTTPS instead of HTTP. HSTS prevents a variety of downgrading attacks and -/// should always be used when TLS is enabled. `SpaceHelmet` will turn HSTS on and -/// issue a warning if you enable TLS without enabling HSTS in [Staging ] or [Production]. -/// Read full [`HstsPolicy`] documentation before you configure this. -/// -/// HSTS is important for HTTPS security, however, incorrectly configured HSTS can lead to problems as -/// you are disallowing access to non-HTTPS enabled parts of your site. [Yelp engineering] has good -/// discussion of potential challenges that can arise, and how to roll this out in a large scale setting. -/// So, if you use TLS, use HSTS, just roll it out with care. -/// -/// Finally, requiring TLS use with valid certificates may be something of a nuisance in -/// development settings, so you may with to restrict HSTS use to [Staging] and [Production]. -/// -/// [TLS]: https://rocket.rs/guide/configuration/#configuring-tls -/// [Strict-Transport-Security]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security -/// [default policy]: /rocket_contrib/space_helmet/enum.HstsPolicy.html#impl-Default -/// [Yelp engineering]: https://engineeringblog.yelp.com/2017/09/the-road-to-hsts.html -/// [Staging]: /rocket/config/enum.Environment.html#variant.Staging -/// [Production]: /rocket/config/enum.Environment.html#variant.Production -pub enum HstsPolicy { - /// Browser should only permit this site to be accesses by HTTPS for the next [`Duration`] - /// seconds. - Enable(Duration), - - /// Same as above, but also apply to all of the sites subdomains. - IncludeSubDomains(Duration), - - /// Google maintains an [HSTS preload service] that can be used to prevent - /// the browser from ever connecting to your site over an insecure connection. - /// Read more [here]. Don't enable this before you have registered your site - /// - /// [HSTS preload service]: https://hstspreload.org/ - /// [here]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#Preloading_Strict_Transport_Security - Preload(Duration), -} - -/// Defaults to `HstsPolicy::Enable(Duration::weeks(52))`. -impl Default for HstsPolicy { - fn default() -> HstsPolicy { - HstsPolicy::Enable(Duration::weeks(52)) - } -} - -impl<'a, 'b> From<&'a HstsPolicy> for Header<'b> { - fn from(policy: &HstsPolicy) -> Header<'b> { - let policy_string = match policy { - HstsPolicy::Enable(max_age) => format!("max-age={}", max_age.num_seconds()), - HstsPolicy::IncludeSubDomains(max_age) => { - format!("max-age={}; includeSubDomains", max_age.num_seconds()) - } - HstsPolicy::Preload(max_age) => format!("max-age={}; preload", max_age.num_seconds()), - }; - - Header::new("Strict-Transport-Security", policy_string) - } -} - -/// The [X-Frame-Options] header controls whether the browser should allow the page -/// to render in a `<frame>`, [`<iframe>`][iframe] or `<object>`. This can be used -/// to prevent [clickjacking] attacks. -/// -/// -/// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options -/// [clickjacking]: https://en.wikipedia.org/wiki/Clickjacking -/// [owasp-clickjacking]: https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet -/// [iframe]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe -pub enum FramePolicy<'a> { - /// Page cannot be displayed in a frame. - Deny, - - /// Page can only be displayed in a frame if the page trying to render it came from - /// the same-origin. Interpretation of same-origin is [browser dependant][X-Frame-Options]. - /// - /// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options - SameOrigin, - - /// Page can only be displayed in a frame if the page trying to render it came from - /// the origin given `Uri`. Interpretation of origin is [browser dependant][X-Frame-Options]. - /// - /// [X-Frame-Options]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options - AllowFrom(Uri<'a>), -} - -///Defaults to [`FramePolicy::SameOrigin`]. -impl<'a> Default for FramePolicy<'a> { - fn default() -> FramePolicy<'a> { - FramePolicy::SameOrigin - } -} - -impl<'a, 'b> From<&'a FramePolicy<'a>> for Header<'b> { - fn from(policy: &FramePolicy<'a>) -> Header<'b> { - let policy_string: Cow<'static, str> = match policy { - FramePolicy::Deny => "DENY".into(), - FramePolicy::SameOrigin => "SAMEORIGIN".into(), - FramePolicy::AllowFrom(uri) => format!("ALLOW-FROM {}", uri).into(), - }; - - Header::new("X-Frame-Options", policy_string) - } -} - -/// The [X-XSS-Protection] header tells the browsers to filter some forms of reflected -/// XSS([cross-site scripting]) attacks. -/// -/// [X-XSS-Protection]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection -/// [cross-site scripting]: https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting -pub enum XssPolicy<'a> { - /// Disables XSS filtering. - Disable, - - /// Enables XSS filtering, if XSS detected browser will sanitize and render page (_often browser - /// default_). - Enable, - - /// Enables XSS filtering, if XSS detected browser will block rendering of page (_SpaceHelmet default_). - EnableBlock, - - /// Enables XSS filtering, if XSS detected browser will sanitize and render page and report the - /// violation to the given `Uri`. (Chromium only) - EnableReport(Uri<'a>), -} - -/// Defaults to [`XssPolicy::EnableBlock`], turns on XSS filtering and blocks page rendering if -/// detected. -impl<'a> Default for XssPolicy<'a> { - fn default() -> XssPolicy<'a> { - XssPolicy::EnableBlock - } -} - -impl<'a, 'b> From<&'a XssPolicy<'a>> for Header<'b> { - fn from(policy: &XssPolicy) -> Header<'b> { - let policy_string: Cow<'static, str> = match policy { - XssPolicy::Disable => "0".into(), - XssPolicy::Enable => "1".into(), - XssPolicy::EnableBlock => "1; mode=block".into(), - XssPolicy::EnableReport(u) => format!("{}{}", "1; report=", u).into(), - }; - - Header::new("X-XSS-Protection", policy_string) - } -} diff --git a/contrib/lib/tests/helmet.rs b/contrib/lib/tests/helmet.rs @@ -0,0 +1,138 @@ +#![feature(proc_macro_hygiene, decl_macro)] + +#[macro_use] +#[cfg(feature = "helmet")] +extern crate rocket; + +#[cfg(feature = "helmet")] +mod helmet_tests { + extern crate time; + extern crate rocket_contrib; + + use rocket; + use rocket::http::{Status, uri::Uri}; + use rocket::local::{Client, LocalResponse}; + + use self::rocket_contrib::helmet::*; + use self::rocket_contrib::helmet::*; + use self::time::Duration; + + #[get("/")] fn hello() { } + + macro_rules! assert_header { + ($response:ident, $name:expr, $value:expr) => { + match $response.headers().get_one($name) { + Some(value) => assert_eq!(value, $value), + None => panic!("missing header '{}' with value '{}'", $name, $value) + } + }; + } + + macro_rules! assert_no_header { + ($response:ident, $name:expr) => { + if let Some(value) = $response.headers().get_one($name) { + panic!("unexpected header: '{}={}", $name, value); + } + }; + } + + macro_rules! dispatch { + ($helmet:expr, $closure:expr) => {{ + let rocket = rocket::ignite().mount("/", routes![hello]).attach($helmet); + let client = Client::new(rocket).unwrap(); + let response = client.get("/").dispatch(); + assert_eq!(response.status(), Status::Ok); + $closure(response) + }} + } + + #[test] + fn default_headers_test() { + dispatch!(SpaceHelmet::default(), |response: LocalResponse| { + assert_header!(response, "X-XSS-Protection", "1"); + assert_header!(response, "X-Frame-Options", "SAMEORIGIN"); + assert_header!(response, "X-Content-Type-Options", "nosniff"); + }) + } + + #[test] + fn disable_headers_test() { + let helmet = SpaceHelmet::default().disable::<XssFilter>(); + dispatch!(helmet, |response: LocalResponse| { + assert_header!(response, "X-Frame-Options", "SAMEORIGIN"); + assert_header!(response, "X-Content-Type-Options", "nosniff"); + assert_no_header!(response, "X-XSS-Protection"); + }); + + let helmet = SpaceHelmet::default().disable::<Frame>(); + dispatch!(helmet, |response: LocalResponse| { + assert_header!(response, "X-XSS-Protection", "1"); + assert_header!(response, "X-Content-Type-Options", "nosniff"); + assert_no_header!(response, "X-Frame-Options"); + }); + + let helmet = SpaceHelmet::default() + .disable::<Frame>() + .disable::<XssFilter>() + .disable::<NoSniff>(); + + dispatch!(helmet, |response: LocalResponse| { + assert_no_header!(response, "X-Frame-Options"); + assert_no_header!(response, "X-XSS-Protection"); + assert_no_header!(response, "X-Content-Type-Options"); + }); + + dispatch!(SpaceHelmet::new(), |response: LocalResponse| { + assert_no_header!(response, "X-Frame-Options"); + assert_no_header!(response, "X-XSS-Protection"); + assert_no_header!(response, "X-Content-Type-Options"); + }); + } + + #[test] + fn additional_headers_test() { + let helmet = SpaceHelmet::default() + .enable(Hsts::default()) + .enable(ExpectCt::default()) + .enable(Referrer::default()); + + dispatch!(helmet, |response: LocalResponse| { + assert_header!( + response, + "Strict-Transport-Security", + format!("max-age={}", Duration::weeks(52).num_seconds()) + ); + + assert_header!( + response, + "Expect-CT", + format!("max-age={}, enforce", Duration::days(30).num_seconds()) + ); + + assert_header!(response, "Referrer-Policy", "no-referrer"); + }) + } + + #[test] + fn uri_test() { + let allow_uri = Uri::parse("https://www.google.com").unwrap(); + let report_uri = Uri::parse("https://www.google.com").unwrap(); + let enforce_uri = Uri::parse("https://www.google.com").unwrap(); + + let helmet = SpaceHelmet::default() + .enable(Frame::AllowFrom(allow_uri)) + .enable(XssFilter::EnableReport(report_uri)) + .enable(ExpectCt::ReportAndEnforce(Duration::seconds(30), enforce_uri)); + + dispatch!(helmet, |response: LocalResponse| { + assert_header!(response, "X-Frame-Options", + "ALLOW-FROM https://www.google.com"); + + assert_header!(response, "X-XSS-Protection", + "1; report=https://www.google.com"); + + assert_header!(response, "Expect-CT", + "max-age=30, enforce, report-uri=\"https://www.google.com\""); + }); + } +} diff --git a/contrib/lib/tests/space_helmet.rs b/contrib/lib/tests/space_helmet.rs @@ -1,99 +0,0 @@ -#![cfg_attr(test, feature(plugin, decl_macro))] -#![cfg_attr(test, plugin(rocket_codegen))] -#![feature(proc_macro_non_items)] - -#[macro_use] extern crate rocket; -extern crate rocket_contrib; - -#[cfg(feature = "space_helmet")] -extern crate time; - -#[cfg(feature = "space_helmet")] -mod space_helmet_tests { - use rocket; - use rocket::http::uri::Uri; - use rocket::http::Status; - use rocket::local::Client; - use rocket_contrib::space_helmet::*; - use time::Duration; - - #[get("/")] - fn hello() -> &'static str { - "Hello, world!" - } - - macro_rules! check_header { - ($response:ident, $header_name:expr, $header_param:expr) => { - match $response.headers().get_one($header_name) { - Some(string) => assert_eq!(string, $header_param), - None => panic!("missing header parameters") - } - }; - } - - #[test] - fn default_headers_test() { - let helmet = SpaceHelmet::new(); - let rocket = rocket::ignite().mount("/", routes![hello]).attach(helmet); - let client = Client::new(rocket).unwrap(); - let mut response = client.get("/").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some("Hello, world!".into())); - check_header!(response, "X-XSS-Protection", "1; mode=block"); - check_header!(response, "X-Frame-Options", "SAMEORIGIN"); - check_header!(response, "X-Content-Type-Options", "nosniff"); - } - - #[test] - fn additional_headers_test() { - let helmet = SpaceHelmet::new() - .hsts(HstsPolicy::default()) - .expect_ct(ExpectCtPolicy::default()) - .referrer_policy(ReferrerPolicy::default()); - let rocket = rocket::ignite().mount("/", routes![hello]).attach(helmet); - let client = Client::new(rocket).unwrap(); - let mut response = client.get("/").dispatch(); - assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some("Hello, world!".into())); - check_header!( - response, - "Strict-Transport-Security", - format!("max-age={}", Duration::weeks(52).num_seconds()) - ); - check_header!( - response, - "Expect-CT", - format!("max-age={}, enforce", Duration::days(30).num_seconds()) - ); - check_header!(response, "Referrer-Policy", "no-referrer"); - } - - #[test] - fn uri_test() { - let allow_uri = Uri::parse("https://www.google.com").unwrap(); - let report_uri = Uri::parse("https://www.google.com").unwrap(); - let enforce_uri = Uri::parse("https://www.google.com").unwrap(); - let helmet = SpaceHelmet::new() - .frameguard(FramePolicy::AllowFrom(allow_uri)) - .xss_protect(XssPolicy::EnableReport(report_uri)) - .expect_ct(ExpectCtPolicy::ReportAndEnforce(Duration::seconds(30), enforce_uri)); - let rocket = rocket::ignite().mount("/", routes![hello]).attach(helmet); - let client = Client::new(rocket).unwrap(); - let response = client.get("/").dispatch(); - check_header!( - response, - "X-Frame-Options", - "ALLOW-FROM https://www.google.com" - ); - check_header!( - response, - "X-XSS-Protection", - "1; report=https://www.google.com" - ); - check_header!( - response, - "Expect-CT", - "max-age=30, enforce, report-uri=\"https://www.google.com\"" - ); - } -} diff --git a/examples/space_helmet/Cargo.toml b/examples/space_helmet/Cargo.toml @@ -1,18 +0,0 @@ -[package] -name = "space_helmet" -version = "0.0.0" -workspace = "../../" -publish = false - -[dependencies] -rocket = { path = "../../core/lib", features = ["tls"]} -rocket_codegen = { path = "../../core/codegen" } - -[dependencies.rocket_contrib] -path = "../../contrib/lib" -default-features = false -features = ["space_helmet"] - -[[bin]] -name = "hello" -path = "src/hello.rs" diff --git a/examples/space_helmet/Rocket.toml b/examples/space_helmet/Rocket.toml @@ -1,4 +0,0 @@ -# Example cert and key pair from the tls example, NEVER use these in production, for demonstration only!!! -[global.tls] -certs = "private/cert.pem" -key = "private/key.pem" diff --git a/examples/space_helmet/private/cert.pem b/examples/space_helmet/private/cert.pem @@ -1,30 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFITCCAwmgAwIBAgIJAII1fQkonYEEMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEChMJUm9ja2V0IENBMRcwFQYDVQQD -Ew5Sb2NrZXQgUm9vdCBDQTAeFw0xNzA5MDExMDAyMjhaFw0yNzA4MzAxMDAyMjha -MD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEPMA0GA1UEChMGUm9ja2V0MRIw -EAYDVQQDEwlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQDqe/Ps0tJf11HBuxJ4HvgC4VJeeiSl3D4P8ZT6uamCj8XD0MPtfRjGgfZPRjfY -ksiYRs4Wg3Wy3aiQR6IVrNAxtfU1ZA3vRGCBwV0oWkfyPJKQOtF0Ih0/MhmYdiWG -gDqs5qF/6B9K8qbinexal8v1oXpwQC5dod/NOuSLZQtQfkiYIeNqo0BbxtcaNE2u -kgOYg1Cvc9ui3KPNA2JTN+Uzq6A8n4Pej6erG2NeCAoov9nrkPyustDWLQ76wdTp -5YU6zwwsl+fJtb5scNUmagujoXTTqn06WoCMDUsSjC/jlGMIrzmx90Wq8Dg6HBGn -Cscz3M/AUXYzJtShkxMNZCsdxH+8x5oyO/RrtyeRyN8iDiOolz+SfQROVXMU0zkx -nRl7hIxgB/QeDi6MMXGLTd08vpIAohk3hnycsGgTwTCT5LxWJnorpm4wdr1bDmCY -InUO5hX0rFWtS0ij78GTUbpajkNTEXIXXwa1VnSE2kIeUX6aiKhJsm3KWp496JuM -ahIR7XCP9PyGclWI+Pa0eq5L8nnuSfqUAwCeOvvwdBOxUvKmecly1IHLoUXGnhy0 -46MjYo80yYFqrGgop6lUEZ0ThYpDpMxq+JIeUoyGaCJFDvundzt0u0sh9i+hUCVe -v3zsgxwvBeJy0L1G1uGkpCqERkYJQt9O+qLM8i7hf7ONkQIDAQABoxgwFjAUBgNV -HREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIBAAcXycXdWVvwLl+7 -ryG8V7FpB9bp0lHNA4q3DpEINCNyuA2cbnVU/u2L3ddvroTcrPjE+hhNbcVaz/NH -w4sQqlDzkV+G9kZ4qBTlk5zon6aGGow181J+B5SVznt6lO4/KYYpEN0vX+uMvsgK -OG7ydsRMDxPpsnVS9SFx0Ke8AlmUet5S/NGYCfedd4rwCu+oJHUWhXNwFZqLF3Yn -s8lg3xdM0kJt8g4m1/KUpunanX3w+DdZaIwbltEZs4NriXn0VVbEPRpHyiGMosgf -mEUV2z49f6S2joEnSn2Y/ILOdKFQ2mKFXtXJP43Qzj8Mr5mSb2bXyABlrn0pl/+o -HBkyVyDx5BKqWKe5uK3YCDsbIJj026AkCdTKF+BSBWfB+EqdSIOvVrpHtQK7BwFx -pS5rdQBLA86f1NC0e235L6pwFKm+imazr6Jn7fbbwq1y9PSL36rUn4e/+R2Yoia9 -S7zDOqGbnyv9h7eE3Muiy26kJsJfCrjse/dmce+6YnB1FC5RKPn7kM86t7MyDrgx -W60xRMdgmcGfPjei2V4MdVM6ysOlNoeh39DizjkV9+r8iGl4vngplJrPgAIvywQz -v1pLk6dSlSOwgqY94hqxqNvG80xSoYsmMjDrPmtBVERjhbffsdIDHjcPVsJKH6l6 -8wg+/u6aK2bMHt41f3XE/UTY+A57 ------END CERTIFICATE----- diff --git a/examples/space_helmet/private/key.pem b/examples/space_helmet/private/key.pem @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/examples/space_helmet/src/hello.rs b/examples/space_helmet/src/hello.rs @@ -1,33 +0,0 @@ -#![feature(decl_macro)] -#![feature(plugin)] -#![plugin(rocket_codegen)] -#![feature(proc_macro_non_items)] - -#[macro_use] extern crate rocket; -extern crate rocket_contrib; -use rocket::http::uri::Uri; - -use rocket_contrib::space_helmet::*; - -#[get("/")] -fn index() -> &'static str { - "Hello, world!" -} - -fn rocket() -> rocket::Rocket { - let allow_uri = Uri::parse("https://mysite.example.com").unwrap(); - let report_uri = Uri::parse("https://report.example.com").unwrap(); - let helmet = SpaceHelmet::new() - //illustrates how to disable a header by using None as the policy. - .no_sniff(None) - .frameguard(FramePolicy::AllowFrom(allow_uri)) - .xss_protect(XssPolicy::EnableReport(report_uri)) - //we need an hsts policy as we are using tls - .hsts(HstsPolicy::default()) - .expect_ct(ExpectCtPolicy::default()); - rocket::ignite().mount("/", routes![index]).attach(helmet) -} - -fn main() { - rocket().launch(); -} diff --git a/scripts/test.sh b/scripts/test.sh @@ -86,7 +86,7 @@ if [ "$1" = "--contrib" ]; then tera_templates handlebars_templates serve - space_helmet + helmet diesel_postgres_pool diesel_sqlite_pool diesel_mysql_pool