Initial commit
This commit is contained in:
commit
60f07375ef
|
@ -0,0 +1 @@
|
||||||
|
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "tide_csp"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Michael Pfaff <michael@pfaff.dev>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tide = "0.15.0"
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Tide Tracing
|
||||||
|
|
||||||
|
Simple middleware for Tide to add CSP headers without needing to know the syntax.
|
|
@ -0,0 +1,173 @@
|
||||||
|
use core::fmt::Display;
|
||||||
|
use core::future::Future;
|
||||||
|
use core::pin::Pin;
|
||||||
|
use tide::Next;
|
||||||
|
use tide::Request;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Directive {
|
||||||
|
ChildSrc(Vec<String>),
|
||||||
|
ConnectSrc(Vec<String>),
|
||||||
|
DefaultSrc(Vec<String>),
|
||||||
|
FontSrc(Vec<String>),
|
||||||
|
FrameSrc(Vec<String>),
|
||||||
|
ImgSrc(Vec<String>),
|
||||||
|
ManifestSrc(Vec<String>),
|
||||||
|
MediaSrc(Vec<String>),
|
||||||
|
ObjectSrc(Vec<String>),
|
||||||
|
PrefetchSrc(Vec<String>),
|
||||||
|
ScriptSrc(Vec<String>),
|
||||||
|
ScriptSrcElem(Vec<String>),
|
||||||
|
ScriptSrcAttr(Vec<String>),
|
||||||
|
StyleSrc(Vec<String>),
|
||||||
|
StyleSrcElem(Vec<String>),
|
||||||
|
StyleSrcAttr(Vec<String>),
|
||||||
|
WorkerSrc(Vec<String>),
|
||||||
|
BaseUri(Vec<String>),
|
||||||
|
PluginTypes(Vec<String>),
|
||||||
|
Sandbox(Sandbox),
|
||||||
|
FormAction(Vec<String>),
|
||||||
|
FrameAncestors(Vec<String>),
|
||||||
|
NavigateTo(Vec<String>),
|
||||||
|
ReportTo(String),
|
||||||
|
RequireTrustedTypesFor(TrustedTypesTarget),
|
||||||
|
TrustedTypes(TrustedTypes),
|
||||||
|
UpgradeInsecureRequests,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Directive {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
Self::ChildSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ConnectSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::DefaultSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::FontSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::FrameSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ImgSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ManifestSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::MediaSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ObjectSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::PrefetchSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ScriptSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ScriptSrcElem(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::ScriptSrcAttr(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::StyleSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::StyleSrcElem(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::StyleSrcAttr(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::WorkerSrc(sources) => write!(f, "{}", sources.join(" ")),
|
||||||
|
Self::BaseUri(base_uris) => write!(f, "{}", base_uris.join(" ")),
|
||||||
|
Self::PluginTypes(plugin_types) => write!(f, "{}", plugin_types.join(" ")),
|
||||||
|
Self::Sandbox(sandbox) => write!(f, "{}", sandbox),
|
||||||
|
Self::FormAction(form_actions) => write!(f, "{}", form_actions.join(" ")),
|
||||||
|
Self::FrameAncestors(frame_ancestors) => write!(f, "{}", frame_ancestors.join(" ")),
|
||||||
|
Self::NavigateTo(targets) => write!(f, "{}", targets.join(" ")),
|
||||||
|
Self::ReportTo(report_url) => write!(f, "report-to {}", report_url),
|
||||||
|
Self::RequireTrustedTypesFor(target) => {
|
||||||
|
write!(f, "require-trusted-types-for {}", target)
|
||||||
|
}
|
||||||
|
Self::TrustedTypes(trusted_types) => write!(f, "trusted-types {}", trusted_types),
|
||||||
|
Self::UpgradeInsecureRequests => write!(f, "upgrade-insecure-requests"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Sandbox {
|
||||||
|
AllowDownloadsWithoutUserActivation,
|
||||||
|
AllowForms,
|
||||||
|
AllowModals,
|
||||||
|
AllowOrienationLock,
|
||||||
|
AllowPointerLock,
|
||||||
|
AllowPopups,
|
||||||
|
AllowPopupsToEscapeSandbox,
|
||||||
|
AllowPresentation,
|
||||||
|
AllowSameOrigin,
|
||||||
|
AllowScripts,
|
||||||
|
AllowStorageAccessByUserActivation,
|
||||||
|
AllowTopNavigation,
|
||||||
|
AllowTopNavigationByUserActivation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Sandbox {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
Self::AllowDownloadsWithoutUserActivation =>
|
||||||
|
"allow-downloads-without-user-activation",
|
||||||
|
Self::AllowForms => "allow-forms",
|
||||||
|
Self::AllowModals => "allow-modals",
|
||||||
|
Self::AllowOrienationLock => "allow-orienation-lock",
|
||||||
|
Self::AllowPointerLock => "allow-pointer-lock",
|
||||||
|
Self::AllowPopups => "allow-popups",
|
||||||
|
Self::AllowPopupsToEscapeSandbox => "allow-popups-to-escape-sandbox",
|
||||||
|
Self::AllowPresentation => "allow-presentation",
|
||||||
|
Self::AllowSameOrigin => "allow-same-origin",
|
||||||
|
Self::AllowScripts => "allow-scripts",
|
||||||
|
Self::AllowStorageAccessByUserActivation =>
|
||||||
|
"allow-storage-access-by-user-activation",
|
||||||
|
Self::AllowTopNavigation => "allow-top-navigation",
|
||||||
|
Self::AllowTopNavigationByUserActivation =>
|
||||||
|
"allow-top-navigation-by-user-activation",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum TrustedTypesTarget {
|
||||||
|
Script,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TrustedTypesTarget {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
write!(f, "'script'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum TrustedTypes {
|
||||||
|
None,
|
||||||
|
Values {
|
||||||
|
policy_names: Vec<String>,
|
||||||
|
allow_duplicates: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TrustedTypes {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
Self::None => write!(f, "'none'"),
|
||||||
|
Self::Values {
|
||||||
|
policy_names,
|
||||||
|
allow_duplicates: true,
|
||||||
|
} => write!(f, "{} 'allow-duplicates'", policy_names.join(" ")),
|
||||||
|
Self::Values {
|
||||||
|
policy_names,
|
||||||
|
allow_duplicates: false,
|
||||||
|
} => write!(f, "{}", policy_names.join(" ")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn csp_middleware<'a, State: 'static + Clone + Send + Sync>(
|
||||||
|
directives: &'a [Directive],
|
||||||
|
) -> impl Fn(Request<State>, Next<'a, State>) -> Pin<Box<dyn Future<Output = tide::Result> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |req: Request<State>, next: Next<'a, State>| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut res = next.run(req).await;
|
||||||
|
res.insert_header(
|
||||||
|
"Content-Security-Policy",
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
directives
|
||||||
|
.iter()
|
||||||
|
.fold(String::new(), |a, b| a + &b.to_string() + ";")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Ok(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue