product-os-server 0.0.55

Product OS : Server provides a full functioning advanced server capable of acting as a web server, command and control distributed network, authentication server, crawling server and more. Fully featured with high level of flexibility.
Documentation
//! Content Security Policy configuration
//!
//! This module provides functionality to configure Content Security Policy headers
//! based on configuration settings.

use std::prelude::v1::*;

use csp::{CSP, Directive, Sources, Source, ReportUris, SriFor, SandboxAllowedList, SandboxAllow, Plugins};

use product_os_security::CSPConfig;

/// Content Security Policy builder
pub struct ContentSecurityPolicy {
    policy: CSPConfig
}

impl ContentSecurityPolicy {
    /// Creates a new CSP builder from a `CSPConfig`
    pub fn from_csp_config(csp_config: &CSPConfig) -> Self {
        Self {
            policy: csp_config.clone()
        }
    }

    /// Helper to build a Sources list from a Vec of strings
    fn build_sources(sources: &[String]) -> Sources<'_> {
        let mut sources_list = Sources::new();
        for value in sources {
            if value == "self" {
                sources_list.push_borrowed(Source::Self_);
            } else {
                sources_list.push_borrowed(Source::Host(value.as_str()));
            }
        }
        sources_list
    }

    /// Generates the CSP header string
    pub fn get_csp(&self) -> String {
        let csp_config = &self.policy;
        let mut policy = CSP::new();

        // Add all source-based directives
        if let Some(sources) = &csp_config.base_uri {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::BaseUri(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.child_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ChildSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.connect_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ConnectSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.default_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::DefaultSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.font_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::FontSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.form_action {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::FormAction(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.frame_ancestors {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::FrameAncestors(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.frame_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::FrameSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.img_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ImgSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.manifest_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ManifestSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.media_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::MediaSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.navigate_to {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::NavigateTo(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.object_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ObjectSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.prefetch_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::PrefetchSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.script_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ScriptSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.script_src_attr {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ScriptSrcAttr(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.script_src_elem {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::ScriptSrcElem(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.style_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::StyleSrc(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.style_src_attr {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::StyleSrcAttr(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.style_src_elem {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::StyleSrcElem(Self::build_sources(sources)));
            }
        }
        if let Some(sources) = &csp_config.worker_src {
            if !sources.is_empty() {
                policy.push_borrowed(Directive::WorkerSrc(Self::build_sources(sources)));
            }
        }

        // Block all mixed content
        if let Some(values) = &csp_config.block_all_mixed_content {
            if values.iter().any(|v| v == "true") {
                policy.push_borrowed(Directive::BlockAllMixedContent);
            }
        }

        // Plugin types
        // Each plugin type entry must be a 2-element array: ["type", "subtype"]
        // (e.g. ["application", "pdf"]). Entries with != 2 elements are silently skipped.
        if let Some(plugin_types) = &csp_config.plugin_types {
            if !plugin_types.is_empty() {
                let mut plugins = Plugins::new();
                for value in plugin_types {
                    if value.len() == 2 {
                        plugins = plugins.push((value[0].as_str(), value[1].as_str()));
                    }
                }
                policy.push_borrowed(Directive::PluginTypes(plugins));
            }
        }

        // Report-To
        if let Some(report_to) = &csp_config.report_to {
            for value in report_to {
                policy.push_borrowed(Directive::ReportTo(value.as_str()));
            }
        }

        // Report-URI
        if let Some(report_uri) = &csp_config.report_uri {
            if !report_uri.is_empty() {
                let mut report_uris = ReportUris::new();
                for value in report_uri {
                    report_uris.push_borrowed(value.as_str());
                }
                policy.push_borrowed(Directive::ReportUri(report_uris));
            }
        }

        // Require SRI for
        // Enabled when any entry in the list is the literal string "true".
        // Other truthy representations (e.g. "1", "yes") are not recognised.
        if let Some(values) = &csp_config.require_sri_for {
            if values.iter().any(|v| v == "true") {
                policy.push_borrowed(Directive::RequireSriFor(SriFor::ScriptStyle));
            }
        }

        // Sandbox
        if let Some(sandbox_allow) = &csp_config.sandbox_allow {
            if !sandbox_allow.is_empty() {
                let mut sandbox_list = SandboxAllowedList::new();
                for value in sandbox_allow {
                    let allow = match value.as_str() {
                        "downloads_without_user_activation" => SandboxAllow::DownloadsWithoutUserActivation,
                        "forms" => SandboxAllow::Forms,
                        "modals" => SandboxAllow::Modals,
                        "orientation_lock" => SandboxAllow::OrientationLock,
                        "popups" => SandboxAllow::Popups,
                        "popups_to_escape_sandbox" => SandboxAllow::PopupsToEscapeSandbox,
                        "pointer_lock" => SandboxAllow::PointerLock,
                        "presentation" => SandboxAllow::Presentation,
                        "same_origin" => SandboxAllow::SameOrigin,
                        "scripts" => SandboxAllow::Scripts,
                        "storage_access_by_user_activation" => SandboxAllow::StorageAccessByUserActivation,
                        "top_navigation" => SandboxAllow::TopNavigation,
                        "top_navigation_by_user_activation" => SandboxAllow::TopNavigationByUserActivation,
                        _ => SandboxAllow::SameOrigin,
                    };
                    sandbox_list.push_borrowed(allow);
                }
                policy.push_borrowed(Directive::Sandbox(sandbox_list));
            }
        }

        // Trusted types
        if let Some(trusted_types) = &csp_config.trusted_types {
            if !trusted_types.is_empty() {
                let types: Vec<&str> = trusted_types.iter().map(|s| s.as_str()).collect();
                policy.push_borrowed(Directive::TrustedTypes(types));
            }
        }

        // Upgrade insecure requests
        if let Some(values) = &csp_config.upgrade_insecure_requests {
            if values.iter().all(|v| v != "false") && !values.is_empty() {
                policy.push_borrowed(Directive::UpgradeInsecureRequests);
            }
        }

        policy.to_string()
    }
}