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