diff --git a/.github/workflows/test-publish.yml b/.github/workflows/test-publish.yml new file mode 100644 index 0000000..3a6831f --- /dev/null +++ b/.github/workflows/test-publish.yml @@ -0,0 +1,32 @@ +name: Test and Publish +on: + push: + branches: + - master +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + all-features: true + args: --verbose + publish: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: publish + token: ${{ secrets.CRATES_IO_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..49d319a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ntex-helmet" +version = "0.1.0" +edition = "2021" +authors = ["Daniel Kovacs "] +description = "HTTP security headers middleware for ntex-web" +readme = "README.md" +license = "MIT" +homepage = "https://github.com/danielkov/ntex-helmet" +repository = "https://github.com/danielkov/ntex-helmet" +keywords = ["ntex", "ntex-web", "helmet", "security", "middleware"] +categories = ["web-programming", "http", "middleware"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ntex = "0.7" + +[dev-dependencies] +ntex = { version = "0.7", features=["tokio"] } diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc47b25 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# `ntex-helmet` - Security Middleware for `ntex` web framework + +[![crate](https://img.shields.io/crates/v/ntex-helmet.svg)](https://crates.io/crates/ntex-helmet) +[![docs](https://docs.rs/ntex-helmet/badge.svg)](https://docs.rs/ntex-helmet) + +`ntex-helmet` is a security middleware for the `ntex` web framework. It's based on the [helmet](https://helmetjs.github.io/) middleware for Node.js. + +It works by setting HTTP headers for you. These headers can help protect your app from some well-known web vulnerabilities: + +- [Cross-Origin-Embedder-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) +- [Cross-Origin-Opener-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) +- [Cross-Origin-Resource-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy) +- [Origin-Agent-Cluster](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster) +- [Referrer-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) +- [Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) +- [X-Content-Type-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) +- [X-DNS-Prefetch-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) +- [X-Download-Options]() +- [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) +- [X-Permitted-Cross-Domain-Policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Permitted-Cross-Domain-Policies) +- [X-XSS-Protection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection) +- [X-Powered-By](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Powered-By) +- [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +ntex-helmet = "0.1" +``` + +## Example + +```rust +use ntex::web::{self, App, HttpResponse}; +use ntex_helmet::Helmet; + +#[ntex::main] +fn main() { + let app = App::new() + .wrap(Helmet::default()) + .service(web::resource("/").to(|| HttpResponse::Ok())); + + // ... +} +``` + +## License + +This project is licensed under the [MIT license](LICENSE). diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..63b3fae --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2878 @@ +/// Helmet is a collection of HTTP headers that help secure your app by setting various HTTP headers. +/// +/// ntex-helmet is a middleware that automatically sets these headers. +/// +/// It is based on the [Helmet](https://helmetjs.github.io/) library for Node.js and is highly configurable. +use core::fmt::Display; +use std::rc::Rc; + +use ntex::{ + forward_poll_ready, forward_poll_shutdown, + http::header::{HeaderName, HeaderValue}, + util::BoxFuture, + web::{WebRequest, WebResponse}, + Middleware, Service, ServiceCtx, +}; + +/// Header trait +/// +/// Allows custom headers to be added to the response +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::Header; +/// +/// struct MyHeader; +/// +/// impl Header for MyHeader { +/// fn name(&self) -> &'static str { +/// "My-Header" +/// } +/// +/// fn value(&self) -> String { +/// "my-value".to_string() +/// } +/// } +/// ``` +pub trait Header { + fn name(&self) -> &'static str; + fn value(&self) -> String; +} + +/// Manages `Cross-Origin-Embedder-Policy` header +/// +/// The Cross-Origin-Embedder-Policy HTTP response header prevents a document from loading any cross-origin resources that do not explicitly grant the document permission (via CORS headers) to load them. +/// +/// # Values +/// +/// - unsafe-none: The document is not subject to any Cross-Origin-Embedder-Policy restrictions. +/// - require-corp: The document is subject to Cross-Origin-Embedder-Policy restrictions. +/// - credentialless: The document is subject to Cross-Origin-Embedder-Policy restrictions, and is not allowed to request credentials (e.g. cookies, certificates, HTTP authentication) from the user. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::CrossOriginEmbedderPolicy; +/// +/// let cross_origin_embedder_policy = CrossOriginEmbedderPolicy::unsafe_none(); +/// +/// let cross_origin_embedder_policy = CrossOriginEmbedderPolicy::require_corp(); +/// +/// let cross_origin_embedder_policy = CrossOriginEmbedderPolicy::credentialless(); +/// ``` +#[derive(Clone)] +pub enum CrossOriginEmbedderPolicy { + UnsafeNone, + RequireCorp, + Credentialless, +} + +impl CrossOriginEmbedderPolicy { + pub fn unsafe_none() -> Self { + Self::UnsafeNone + } + + pub fn require_corp() -> Self { + Self::RequireCorp + } + + pub fn credentialless() -> Self { + Self::Credentialless + } +} + +impl Display for CrossOriginEmbedderPolicy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CrossOriginEmbedderPolicy::UnsafeNone => write!(f, "unsafe-none"), + CrossOriginEmbedderPolicy::RequireCorp => write!(f, "require-corp"), + CrossOriginEmbedderPolicy::Credentialless => write!(f, "credentialless"), + } + } +} + +impl Header for CrossOriginEmbedderPolicy { + fn name(&self) -> &'static str { + "Cross-Origin-Embedder-Policy" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `Cross-Origin-Opener-Policy` header +/// +/// The Cross-Origin-Opener-Policy HTTP response header restricts how selected resources are allowed to interact with the document's browsing context in response to user navigation. Each resource can declare an opener policy which applies to the resource's corresponding browsing context. +/// +/// # Values +/// +/// - same-origin: The resource's browsing context is the same-origin as the document's browsing context. +/// - same-origin-allow-popups: The resource's browsing context is the same-origin as the document's browsing context, and the resource is allowed to open new browsing contexts. +/// - unsafe-none: The resource's browsing context is cross-origin with the document's browsing context. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::CrossOriginOpenerPolicy; +/// +/// let cross_origin_opener_policy = CrossOriginOpenerPolicy::same_origin(); +/// ``` +#[derive(Clone)] +pub enum CrossOriginOpenerPolicy { + SameOrigin, + SameOriginAllowPopups, + UnsafeNone, +} + +impl CrossOriginOpenerPolicy { + pub fn same_origin() -> Self { + Self::SameOrigin + } + + pub fn same_origin_allow_popups() -> Self { + Self::SameOriginAllowPopups + } + + pub fn unsafe_none() -> Self { + Self::UnsafeNone + } +} + +impl Display for CrossOriginOpenerPolicy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CrossOriginOpenerPolicy::SameOrigin => write!(f, "same-origin"), + CrossOriginOpenerPolicy::SameOriginAllowPopups => write!(f, "same-origin-allow-popups"), + CrossOriginOpenerPolicy::UnsafeNone => write!(f, "unsafe-none"), + } + } +} + +impl Header for CrossOriginOpenerPolicy { + fn name(&self) -> &'static str { + "Cross-Origin-Opener-Policy" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `Cross-Origin-Resource-Policy` header +/// +/// The Cross-Origin-Resource-Policy HTTP response header conveys a desire that the browser blocks no-cors cross-origin/cross-site requests to the given resource. +/// +/// # Values +/// +/// - same-origin: The resource is same-origin to the document. +/// - same-site: The resource is same-site to the document. +/// - cross-origin: The resource is cross-origin to the document. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::CrossOriginResourcePolicy; +/// +/// let cross_origin_resource_policy = CrossOriginResourcePolicy::same_origin(); +/// ``` +#[derive(Clone)] +pub enum CrossOriginResourcePolicy { + SameOrigin, + SameSite, + CrossOrigin, +} + +impl CrossOriginResourcePolicy { + pub fn same_origin() -> Self { + Self::SameOrigin + } + + pub fn same_site() -> Self { + Self::SameSite + } + + pub fn cross_origin() -> Self { + Self::CrossOrigin + } +} + +impl Display for CrossOriginResourcePolicy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CrossOriginResourcePolicy::SameOrigin => write!(f, "same-origin"), + CrossOriginResourcePolicy::SameSite => write!(f, "same-site"), + CrossOriginResourcePolicy::CrossOrigin => write!(f, "cross-origin"), + } + } +} + +impl Header for CrossOriginResourcePolicy { + fn name(&self) -> &'static str { + "Cross-Origin-Resource-Policy" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `Origin-Agent-Cluster` header +/// +/// The Origin-Agent-Cluster HTTP request header indicates that the client prefers an "origin agent cluster" (OAC) for the origin of the resource being requested. An OAC is a cluster of servers that are controlled by the same entity as the origin server, and that are geographically close to the client. The OAC is used to provide the client with a better experience, for example by serving content from a server that is close to the client, or by serving content that is optimized for the client's device. +/// +/// # Values +/// +/// - 0: The client does not prefer an OAC. +/// - 1: The client prefers an OAC. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::OriginAgentCluster; +/// +/// let origin_agent_cluster = OriginAgentCluster::new(true); +/// ``` +#[derive(Clone)] +pub struct OriginAgentCluster(bool); + +impl OriginAgentCluster { + pub fn new(prefer_mobile_experience: bool) -> Self { + Self(prefer_mobile_experience) + } +} + +impl Display for OriginAgentCluster { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.0 { + write!(f, "?1") + } else { + write!(f, "?0") + } + } +} + +impl Header for OriginAgentCluster { + fn name(&self) -> &'static str { + "Origin-Agent-Cluster" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `Referrer-Policy` header +/// +/// The Referrer-Policy HTTP response header controls how much referrer information (sent via the Referer header) should be included with requests. +/// +/// # Values +/// +/// - no-referrer: The Referer header will be omitted entirely. No referrer information is sent along with requests. +/// - no-referrer-when-downgrade: The Referer header will be omitted entirely. However, if the protected resource URL scheme is HTTPS, then the full path will still be sent as a referrer. +/// - origin: Only send the origin of the document as the referrer in all cases. The document https://example.com/page.html will send the referrer https://example.com/. +/// - origin-when-cross-origin: Send a full URL when performing a same-origin request, but only send the origin of the document for other cases. +/// - same-origin: A referrer will be sent for same-site origins, but cross-origin requests will contain no referrer information. +/// - strict-origin: Only send the origin of the document as the referrer when the protocol security level stays the same (HTTPS→HTTPS), but don't send it to a less secure destination (HTTPS→HTTP). +/// - strict-origin-when-cross-origin: Send a full URL when performing a same-origin request, only send the origin when the protocol security level stays the same (HTTPS→HTTPS), and send no header to a less secure destination (HTTPS→HTTP). +/// - unsafe-url: Send a full URL (stripped from parameters) when performing a same-origin or cross-origin request. This policy will leak origins and paths from TLS-protected resources to insecure origins. Carefully consider the impact of this setting. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::ReferrerPolicy; +/// +/// let referrer_policy = ReferrerPolicy::no_referrer(); +/// ``` +#[derive(Clone)] +pub enum ReferrerPolicy { + NoReferrer, + NoReferrerWhenDowngrade, + Origin, + OriginWhenCrossOrigin, + SameOrigin, + StrictOrigin, + StrictOriginWhenCrossOrigin, + UnsafeUrl, +} + +impl ReferrerPolicy { + pub fn no_referrer() -> Self { + Self::NoReferrer + } + + pub fn no_referrer_when_downgrade() -> Self { + Self::NoReferrerWhenDowngrade + } + + pub fn origin() -> Self { + Self::Origin + } + + pub fn origin_when_cross_origin() -> Self { + Self::OriginWhenCrossOrigin + } + + pub fn same_origin() -> Self { + Self::SameOrigin + } + + pub fn strict_origin() -> Self { + Self::StrictOrigin + } + + pub fn strict_origin_when_cross_origin() -> Self { + Self::StrictOriginWhenCrossOrigin + } + + pub fn unsafe_url() -> Self { + Self::UnsafeUrl + } +} + +impl Display for ReferrerPolicy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ReferrerPolicy::NoReferrer => write!(f, "no-referrer"), + ReferrerPolicy::NoReferrerWhenDowngrade => write!(f, "no-referrer-when-downgrade"), + ReferrerPolicy::Origin => write!(f, "origin"), + ReferrerPolicy::OriginWhenCrossOrigin => write!(f, "origin-when-cross-origin"), + ReferrerPolicy::SameOrigin => write!(f, "same-origin"), + ReferrerPolicy::StrictOrigin => write!(f, "strict-origin"), + ReferrerPolicy::StrictOriginWhenCrossOrigin => { + write!(f, "strict-origin-when-cross-origin") + } + ReferrerPolicy::UnsafeUrl => write!(f, "unsafe-url"), + } + } +} + +impl Header for ReferrerPolicy { + fn name(&self) -> &'static str { + "Referrer-Policy" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `Strict-Transport-Security` header +/// +/// The Strict-Transport-Security HTTP response header (often abbreviated as HSTS) lets a web site tell browsers that it should only be accessed using HTTPS, instead of using HTTP. +/// +/// # Values +/// +/// - max-age: The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS. +/// - includeSubDomains: If this optional parameter is specified, this rule applies to all of the site's subdomains as well. +/// - preload: If this optional parameter is specified, this rule applies to all of the site's subdomains as well. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::StrictTransportSecurity; +/// +/// let strict_transport_security = StrictTransportSecurity::default(); +/// +/// let custom_strict_transport_security = StrictTransportSecurity::default() +/// .max_age(31536000) +/// .include_sub_domains() +/// .preload(); +/// ``` +#[derive(Clone)] +pub struct StrictTransportSecurity { + max_age: u32, + include_sub_domains: bool, + preload: bool, +} + +impl StrictTransportSecurity { + pub fn new() -> Self { + Self::default() + } + + pub fn max_age(mut self, max_age: u32) -> Self { + self.max_age = max_age; + self + } + + pub fn include_sub_domains(mut self) -> Self { + self.include_sub_domains = true; + self + } + + pub fn preload(mut self) -> Self { + self.preload = true; + self + } +} + +impl Default for StrictTransportSecurity { + fn default() -> Self { + Self { + max_age: 31536000, + include_sub_domains: false, + preload: false, + } + } +} + +impl Display for StrictTransportSecurity { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "max-age={}", self.max_age)?; + if self.include_sub_domains { + write!(f, "; includeSubDomains")?; + } + if self.preload { + write!(f, "; preload")?; + } + Ok(()) + } +} + +impl Header for StrictTransportSecurity { + fn name(&self) -> &'static str { + "Strict-Transport-Security" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-Content-Type-Options` header +/// +/// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed. This allows to opt-out of MIME type sniffing, or, in other words, it is a way to say that the webmasters knew what they were doing. +/// +/// # Values +/// +/// - nosniff: Prevents the browser from MIME-sniffing a response away from the declared content-type. This also applies to Google Chrome, when downloading extensions. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XContentTypeOptions; +/// +/// let x_content_type_options = XContentTypeOptions::nosniff(); +/// ``` +#[derive(Clone)] +pub enum XContentTypeOptions { + NoSniff, +} + +impl XContentTypeOptions { + pub fn nosniff() -> Self { + Self::NoSniff + } +} + +impl Display for XContentTypeOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + XContentTypeOptions::NoSniff => write!(f, "nosniff"), + } + } +} + +impl Header for XContentTypeOptions { + fn name(&self) -> &'static str { + "X-Content-Type-Options" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-DNS-Prefetch-Control` header +/// +/// The X-DNS-Prefetch-Control HTTP response header controls DNS prefetching, a feature by which browsers proactively perform domain name resolution on both links that the user may choose to follow as well as URLs for items referenced by the document, including images, CSS, JavaScript, and so forth. +/// +/// # Values +/// +/// - off: Disable DNS prefetching. +/// - on: Enable DNS prefetching, allowing the browser to proactively perform domain name resolution on both links that the user may choose to follow as well as URLs for items referenced by the document, including images, CSS, JavaScript, and so forth. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XDNSPrefetchControl; +/// +/// let x_dns_prefetch_control = XDNSPrefetchControl::off(); +/// ``` +#[derive(Clone)] +pub enum XDNSPrefetchControl { + Off, + On, +} + +impl XDNSPrefetchControl { + pub fn off() -> Self { + Self::Off + } + + pub fn on() -> Self { + Self::On + } +} + +impl Display for XDNSPrefetchControl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + XDNSPrefetchControl::Off => write!(f, "off"), + XDNSPrefetchControl::On => write!(f, "on"), + } + } +} + +impl Header for XDNSPrefetchControl { + fn name(&self) -> &'static str { + "X-DNS-Prefetch-Control" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-Download-Options` header +/// +/// The X-Download-Options HTTP response header indicates that the browser (Internet Explorer) should not display the option to "Open" a file that has been downloaded from an application, to prevent phishing attacks that could trick users into opening potentially malicious content that could infect their computer. +/// +/// # Values +/// +/// - noopen: Prevents Internet Explorer from executing downloads in your site’s context. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XDownloadOptions; +/// +/// let x_download_options = XDownloadOptions::noopen(); +/// ``` +#[derive(Clone)] +pub enum XDownloadOptions { + NoOpen, +} + +impl XDownloadOptions { + pub fn noopen() -> Self { + Self::NoOpen + } +} + +impl Display for XDownloadOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + XDownloadOptions::NoOpen => write!(f, "noopen"), + } + } +} + +impl Header for XDownloadOptions { + fn name(&self) -> &'static str { + "X-Download-Options" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-Frame-Options` header +/// +/// The X-Frame-Options HTTP response header can be used to to avoid click-jacking attacks by preventing the content to be included in other websites. +/// +/// # Values +/// +/// - deny: The page cannot be displayed in a frame, regardless of the site attempting to do so. +/// - sameorigin: The page can only be displayed in a frame on the same origin as the page itself. +/// - allow-from: The page can only be displayed in a frame on the specified origin. Requires a URI as an argument. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XFrameOptions; +/// +/// let x_frame_options = XFrameOptions::deny(); +/// +/// let x_frame_options = XFrameOptions::same_origin(); +/// +/// let x_frame_options = XFrameOptions::allow_from("https://example.com"); +/// ``` +#[derive(Clone)] +pub enum XFrameOptions { + Deny, + SameOrigin, + // deprecated - use Content-Security-Policy instead see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options#allow-from_origin + AllowFrom(String), +} + +impl XFrameOptions { + pub fn deny() -> Self { + Self::Deny + } + + pub fn same_origin() -> Self { + Self::SameOrigin + } + + pub fn allow_from(uri: &str) -> Self { + Self::AllowFrom(uri.to_string()) + } +} + +impl Display for XFrameOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + XFrameOptions::Deny => write!(f, "DENY"), + XFrameOptions::SameOrigin => write!(f, "SAMEORIGIN"), + XFrameOptions::AllowFrom(uri) => write!(f, "ALLOW-FROM {}", uri), + } + } +} + +impl Header for XFrameOptions { + fn name(&self) -> &'static str { + "X-Frame-Options" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-Permitted-Cross-Domain-Policies` header +/// +/// The X-Permitted-Cross-Domain-Policies HTTP response header determines whether cross-domain policy files (crossdomain.xml and clientaccesspolicy.xml) will be ignored by Flash and Adobe Acrobat in subsequent requests. +/// +/// # Values +/// +/// - none: No policy file is allowed. +/// - master-only: Only a master policy file, but no other policy files, is allowed. +/// - by-content-type: A policy file is allowed if its MIME type matches the Content-Type of the requested resource. +/// - by-ftp-filename: A policy file is allowed if its URL matches the URL of the requested resource. +/// - all: Any policy file is allowed. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XPermittedCrossDomainPolicies; +/// +/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::none(); +/// +/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::master_only(); +/// +/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::by_content_type(); +/// +/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::by_ftp_filename(); +/// +/// let x_permitted_cross_domain_policies = XPermittedCrossDomainPolicies::all(); +/// ``` +#[derive(Clone)] +pub enum XPermittedCrossDomainPolicies { + None, + MasterOnly, + ByContentType, + ByFtpFilename, + All, +} + +impl XPermittedCrossDomainPolicies { + pub fn none() -> Self { + Self::None + } + + pub fn master_only() -> Self { + Self::MasterOnly + } + + pub fn by_content_type() -> Self { + Self::ByContentType + } + + pub fn by_ftp_filename() -> Self { + Self::ByFtpFilename + } + + pub fn all() -> Self { + Self::All + } +} + +impl Display for XPermittedCrossDomainPolicies { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + XPermittedCrossDomainPolicies::None => write!(f, "none"), + XPermittedCrossDomainPolicies::MasterOnly => write!(f, "master-only"), + XPermittedCrossDomainPolicies::ByContentType => write!(f, "by-content-type"), + XPermittedCrossDomainPolicies::ByFtpFilename => write!(f, "by-ftp-filename"), + XPermittedCrossDomainPolicies::All => write!(f, "all"), + } + } +} + +impl Header for XPermittedCrossDomainPolicies { + fn name(&self) -> &'static str { + "X-Permitted-Cross-Domain-Policies" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-XSS-Protection` header +/// +/// The HTTP X-XSS-Protection response header is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although these protections are largely unnecessary in modern browsers when sites implement a strong Content-Security-Policy that disables the use of inline JavaScript ('unsafe-inline'), they can still provide protections for users of older web browsers that don't yet support CSP. +/// +/// # Values +/// +/// - 0: Disables XSS filtering. +/// - 1: Enables XSS filtering (usually default in browsers). +/// - 1; mode=block: Enables XSS filtering. Rather than sanitizing the page, the browser will prevent rendering of the page if an attack is detected. +/// - 1; report=: Enables XSS filtering. If a cross-site scripting attack is detected, the browser will sanitize the page and report the violation. This uses the functionality of the CSP report-uri directive to send a report. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XXSSProtection; +/// +/// let x_xss_protection = XXSSProtection::on(); +/// +/// let x_xss_protection = XXSSProtection::off(); +/// +/// let x_xss_protection = XXSSProtection::on().mode_block(); +/// +/// let x_xss_protection = XXSSProtection::on().report("https://example.com"); +/// +/// let x_xss_protection = XXSSProtection::on().mode_block().report("https://example.com"); +/// ``` +#[derive(Clone)] +pub struct XXSSProtection { + on: bool, + mode_block: bool, + report: Option, +} + +impl XXSSProtection { + /// Disables XSS filtering. + pub fn off() -> Self { + Self { + on: false, + mode_block: false, + report: None, + } + } + + /// Enables XSS filtering (usually default in browsers). + pub fn on() -> Self { + Self { + on: true, + mode_block: false, + report: None, + } + } + + /// Enables XSS filtering. Rather than sanitizing the page, the browser will prevent rendering of the page if an attack is detected. + pub fn mode_block(mut self) -> Self { + self.mode_block = true; + self + } + + /// Enables XSS filtering. If a cross-site scripting attack is detected, the browser will sanitize the page and report the violation. This uses the functionality of the CSP report-uri directive to send a report. + pub fn report(mut self, report: &str) -> Self { + self.report = Some(report.to_string()); + self + } +} + +impl Display for XXSSProtection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.on { + write!(f, "1")?; + if self.mode_block { + write!(f, "; mode=block")?; + } + if let Some(report) = &self.report { + write!(f, "; report={}", report)?; + } + } else { + write!(f, "0")?; + } + Ok(()) + } +} + +impl Header for XXSSProtection { + fn name(&self) -> &'static str { + "X-XSS-Protection" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `X-Powered-By` header +/// +/// ntex does not set `X-Powered-By` header by default. +/// Instead of silencing the header, Helmet allows you to set it to a custom value. +/// This can be useful against primitive fingerprinting. +/// +/// # Examples +/// +/// ``` +/// use ntex_helmet::XPoweredBy; +/// +/// let x_powered_by = XPoweredBy::new("PHP 4.2.0"); +/// ``` +#[derive(Clone)] +pub struct XPoweredBy(String); + +impl XPoweredBy { + /// Set the `X-Powered-By` header to a custom value. + pub fn new(comment: &str) -> Self { + Self(comment.to_string()) + } +} + +impl Display for XPoweredBy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0)?; + Ok(()) + } +} + +impl Header for XPoweredBy { + fn name(&self) -> &'static str { + "X-Powered-By" + } + + fn value(&self) -> String { + self.to_string() + } +} + +/// Manages `Content-Security-Policy` header +/// +/// The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks (XSS). +/// +/// # Directives +/// +/// - child-src: Defines valid sources for web workers and nested browsing contexts loaded using elements such as and